Why constructor doesn't execute all the code using WinAPI? - c++

This is my class constructor:
ActionButton::ActionButton(CallbackFunction function, void* param, HWND parent, int x, int y, int heigth, int width) :
m_function(function), m_parameters(param), m_window(NULL)
{
HWND m_window = CreateWindowEx(0, L"Action button", NULL, WS_CHILD | WS_VISIBLE,
x, y, width, heigth, parent, NULL, NULL, NULL);
DWORD dw = GetLastError();
SetWindowLongPtr(m_window, GWLP_USERDATA, (LONG_PTR)this);
ShowWindow(m_window, SW_NORMAL);
}
I used debuger and found that it executes CreateWindowEx() but after pressing F11 program jumped off the constructor (and I'm using only one thread). Is something wrong with my code?
After CreateWindowEx there is executing window procedure with parameters e.g WM_CREATE, so step after CreateWindowEx is not in constructor, after executing a few window procedure callbacks it goes back into constructor.

While executing CreateWindow[Ex] the system calls the window procedure associated with the window class for several messages (WM_GETMINMAXINFO, WM_NCCREATE, WM_NCCALCSIZE, WM_CREATE) before it returns. While the window procedure handles these messages the GWLP_USERDATA is not yet set. The system however guarantees, that GWLP_USERDATA is zero-initialized so you can safely query and handle the uninitialized GWLP_USERDATA.
If you want to set GWLP_USERDATA before CreateWindow[Ex] returns you will have to set up a CBT hook hook using SetWindowsHookEx and handle the HCBT_CREATEWND event. This lets you store any data attached to a HWND before the window procedure gets called with a WM_NCCREATE message.
Unrelated to your question, GWLP_USERDATA is fairly unreliable. A lot of applications will store their own data there, overwriting each other's data. Since this seems to be a private window class that you control you should allocate space in the Extra Window Memory instead and store your data there.

Related

Class method for WndProc

This article explains brilliantly the options to call a class member WndProc. I've seen this response in stackoverflow but the main problem associating class member WndProc after CreateWindow is that some messages will be lost (including the important WM_CREATE) as explained in the mentioned article.
My question: I would like to hear the opinion from an expert on which of the methods exposed below or new one is the best one (performance, maintanability, ...) to create a class member WndProc.
Briefing the two final solutions exposed in the article (suposing that it exists a Window class with WndProc method):
Per-window data with this global pointer storage, protecting it with CRITICAL_SECTION to make it thread safe (extracted from here):
// The helper window procedure
// It is called by Windows, and thus it's a non-member function
// This message handler will only be called after successful SetWindowLong call
// We can assume that pointer returned by GetWindowLong is valid
// It will route messages to our member message handler
LRESULT CALLBACK WndProc2(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
// Get a window pointer associated with this window
Window *w = (Window *) GetWindowLong(hwnd, GWL_USERDATA);
// It should be valid, assert so
_ASSERT(w);
// Redirect messages to the window procedure of the associated window
return w->WndProc(hwnd, msg, wp, lp);
}
// The temporary global this pointer
// It will be used only between CreateWindow is called and the first message is processed by WndProc
// WARNING: it is not thread-safe.
Window *g_pWindow;
// Critical section protecting the global Window pointer
CRITICAL_SECTION g_WindowCS;
// The helper window procedure
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
// Stash global Window pointer into per-window data area
SetWindowLong(hwnd, GWL_USERDATA, (long) g_pWindow);
// Unlock global critical section
g_pWindow->HaveCSLock = false;
LeaveCriticalSection(&g_WindowCS);
// Reset the window message handler
SetWindowLong(hwnd, GWL_WNDPROC, (long) WndProc2);
// Dispatch first message to the member message handler
return WndProc2(hwnd, msg, wp, lp);
}
And now we can create the window:
InitializeCriticalSection(&g_WindowCS);
// Enter the critical section before you write to protected data
EnterCriticalSection(&g_WindowCS);
// Set global Window pointer to our Window instance
// Moved the assignment here, where we have exclusive access to the pointer
g_pWindow = &w;
// Set a flag indicating that the window has the critical section lock
// Note: this must be executed after the above assignment
g_pWindow->HaveCSLock = true;
// Create window
// Note: lpParam is not used
HWND hwnd = CreateWindow(TEXT("BaseWnd"), TEXT("Hello, World!"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hinst, 0);
// Leave critical section if window creation failed and our window procedure hasn't released it
if (g_pWindow->HaveCSLock)
LeaveCriticalSection(&g_WindowCS);
// Destroy critical section
// In production code, you'd do this when application terminates, not immediately after CreateWindow call
DeleteCriticalSection(&g_WindowCS);
Using CBT hook procedure (extracted from here):
// The helper window procedure
// It is called by Windows, and thus it's a non-member function
// This message handler will only be called after successful SetWindowLong call from the hook
// We can assume that pointer returned by GetWindowLong is valid
// It will route messages to our member message handler
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp)
{
// Get a window pointer associated with this window
Window *w = (Window *) GetWindowLong(hwnd, GWL_USERDATA);
// It should be valid, assert so
_ASSERT(w);
// Redirect messages to the window procedure of the associated window
return w->WndProc(hwnd, msg, wp, lp);
}
// The CBT hook procedure
// It is called during CreateWindow call before WndProc receives any messages
// Its job is to set per-window Window pointer to the one passed through lpParam to CreateWindow
LRESULT CALLBACK CBTProc(int code, WPARAM wp, LPARAM lp)
{
if (code != HCBT_CREATEWND) {
// Ignore everything but create window requests
// Note: generally, HCBT_CREATEWND is the only notification we will get,
// assuming the thread is hooked only for the duration of CreateWindow call.
// However, we may receive other notifications, in which case they will not be passed to other CBT hooks.
return 0;
}
// Grab a pointer passed to CreateWindow as lpParam
std::pair<Window *, HHOOK> *p = (std::pair<Window *, HHOOK> *) LPCBT_CREATEWND(lp)->lpcs->lpCreateParams;
// Only handle this window if it wasn't handled before, to prevent rehooking windows when CreateWindow is called recursively
// ie, when you create windows from a WM_CREATE handler
if (p->first) {
// Stash the associated Window pointer, which is the first member of the pair, into per-window data area
SetWindowLong((HWND) wp, GWL_USERDATA, (long) p->first);
// Mark this window as handled
p->first = 0;
}
// Call the next hook in chain, using the second member of the pair
return CallNextHookEx(p->second, code, wp, lp);
}
And now we can create the window:
// Install the CBT hook
// Note: hook the thread immediately before, and unhook it immediately after CreateWindow call.
// The hook procedure can only process window creation nofitications, and it shouldn't be called for other types of notifications
// Additionally, calling hook for other events is wasteful since it won't do anything useful anyway
HHOOK hook = SetWindowsHookEx(WH_CBT, CBTProc, 0, GetCurrentThreadId());
_ASSERT(hook);
// Create window
// Pass a pair consisting of window object pointer and hook as lpParam
std::pair<Window *, HHOOK> p(&w, hook);
HWND hwnd = CreateWindow(TEXT("BaseWnd"), TEXT("Hello, World!"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, 0, 0, hinst, &p);
// Unhook first
UnhookWindowsHookEx(hook);
I personally would not use either of these methods. The global variable approach works, but feels dirty. Especially with the lock. And the CBT hook is, well over the top. Although it points in the right direction.
The standard way to pass state information to your window procedure during creation is through lpParam parameter of CreateWindow or CreateWindowEx. So the technique is as follows:
Pass your instance pointer in the lpParam parameter of CreateWindow or CreateWindowEx.
Read this value in your WM_NCCREATE handler. That message supplies the information as part of the CREATESTRUCT struct.
Still in WM_NCCREATE call SetWindowLongPtr to set the user data of the window to the instance pointer.
All future calls to the window procedure can now obtain the instance pointer by calling GetWindowLongPtr.
Raymond Chen illustrates the details here: How can I make a WNDPROC or DLGPROC a member of my C++ class?

Add visible window titles to combobox MFC

I want to add visible window titles to a combobox. Here is my source:
BOOL CALLBACK EnumWindowsProc(HWND hWnd, long lParam)
{
TCHAR buff[255];
CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_PROCESS);
if (IsWindowVisible(hWnd))
{
GetWindowText(hWnd, buff, 254);
pComboBox->AddString(buff);
}
return TRUE;
}
void CFindProcess::OnDropdownComboProcess()
{
EnumWindows(EnumWindowsProc, 0);
}
but I get error:
error C2660: 'GetDlgItem' : function does not take 1 arguments 60
How I can correctly add titles to combo?
MFC objects are thread-sensitive, GetDlgItem works well in the thread that created the object, probably the main UI thread. Function EnumWindows probably creates a worker thread to access the callback function, and that is why GetDlgItem failed to get a valid handle of the combobox.
To access the combobox properly in another thread, you have to use the static function: CWnd::FromHandle with the raw handle of the combobox object as follows:
BOOL CALLBACK EnumWindowsProc(HWND hWnd, long lParam)
{
if (IsWindowVisible(hWnd))
{ TCHAR szBuffer[255];
INT nLength = GetWindowText(hWnd, szBuffer, 254);
if (nLength>0)
{ // only add windows that has a caption
CComboBox *pComboBox = (CComboBox*)CWnd::FromHandle((HWND)lParam);
pComboBox->AddString(szBuffer);
}
}
return TRUE;
}
// call EnumWindows --------------------
CComboBox *pComboBox = (CComboBox *)GetDlgItem(IDC_COMBO1);
// passing the raw handle of the combobox as parameter
EnumWindows(EnumWindowsProc, (LPARAM)pComboBox->m_hWnd);
Firstly, your GetDlgItem has two parameters, and the first is a handle to the dialog box that contains the control.
So it expects a HWND parameter of the dialog that contains this control, I would presume that will be the HWND you pass as a parameter to your function.
CComboBox* pComboBox = (CComboBox*)GetDlgItem(hWnd,IDC_COMBO_PROCESS);
^^^^ added parameter
If you look at EnumWindows in MSDN, you'll see you have to pass a callback and it has a HWND parameter, if you look at what this parameter is for it says:
A handle to a top-level window.
This is exactly what you have to pass to GetDlgItem.
Also, you should check the return value of GetWindowText as this returns the number of characters written to the buff you passed it.
int ret = GetWindowText(hWnd, buff, 254);
if (ret > 0) pComboBox->AddString(buff); // only add non-empty strings.
In addition to what user #mfc has provided, I would not do UI update from a different thread. I believe EnumWindows does not create thread for enumeration. It would call the callbacks within the call-stack of current thread.
This, in turn, means that UI may freeze for a while. Thus, it is recommended to create a thread for enumeration. More over, I would not directly update UI from different thread. May be a vector of string, or a PostMessage (on each iteration) I would have used.
It is true that EnumWindows may perform quite fast. But when you move to enumerate other (kernel) objects like file, printers, users etc - the UI is definitely going to freeze. So, better practice writing multithreaded code. Initially writing MT-code would be a pain, but later you'd love it, appreciate it, and cannot live without it.

the function CWnd::CreateControl succeeds, but m_hWnd is NULL

I run the code with VC++ 6.0, everything is OK. But when run the same code under Visual C++ 2010, the handle of wnd(namely m_hWnd) is always NULL. Besides, the return value bRet is TRUE (i.e. success).
Here is my code:
BOOL CDemoDlg::OnInitDialog()
{
CDialog::OnInitDialog();
// Set the icon for this dialog. The framework does this automatically
// when the application's main window is not a dialog
SetIcon(m_hIcon, TRUE); // Set big icon
SetIcon(m_hIcon, FALSE); // Set small icon
// TODO: Add extra initialization here
CRect rect;
GetClientRect(rect);
CWnd wnd;
BOOL bRet = wnd.CreateControl(_T("WMPlayer.OCX"), NULL, WS_VISIBLE, rect, this, 19089);
return TRUE; // return TRUE unless you set the focus to a control
}
The wnd object is going out of scope - try making it a member and checking what happens then. Also, if you are trying to assign a different handle to your dialog m_hWnd object, then you are making a mistake, since the m_hWnd should be valid for your dialog by the time that OnInitDialog is called (in response to the Create call), so you should not be reassigning the dialogs member window handle, but rather creating a separate member for it. Hope this helps.

RegisterClassEx Fails as Invalid Parameter - C++

A call to RegisterClassEx in my application is failing with error code 87, "The parameter is incorrect."
memset( &m_wcx, 0, sizeof(WNDCLASSEX) );
m_wcx.cbSize = sizeof(WNDCLASSEX); // size of structure
m_wcx.style = WS_ICONIC; // initially minimized
m_wcx.lpfnWndProc = &WndProc; // points to window procedure
m_wcx.cbClsExtra = 0; // no extra class memory
m_wcx.cbWndExtra = 0; // no extra window memory
m_wcx.hInstance = m_hInstance; // handle to instance
m_wcx.hIcon = ::LoadIcon( NULL, IDI_APPLICATION ); // default app icon
m_wcx.hCursor = ::LoadCursor( NULL, IDC_ARROW ); // standard arrow cursor
m_wcx.hbrBackground = NULL; // no background to paint
m_wcx.lpszMenuName = NULL; // no menu resource
m_wcx.lpszClassName = _pwcWindowClass; // name of window class
m_wcx.hIconSm = NULL; // search system resources for sm icon
m_atom = ::RegisterClassEx( &m_wcx );
if ( m_atom == 0 )
{
TRACE(_T("CNotifyWindow::CNotifyWindow : Failed to register window class.\r\n\tError: %d\r\n\tFile: %s\r\n\tLine: %d\r\n"), ::GetLastError(), __WFILE__, __LINE__);
THROW(::GetLastError());
}
Does anyone know what I'm doing wrong? Thanks.
The style member of the WNDCLASSEX structure accepts class styles, not window styles. In other words, you can't make all windows of that class initially minimized that way.
You should pass WS_ICONIC in the dwStyle argument to CreateWindow() or CreateWindowEx() instead.
The first thing is the WS_ICONIC. The window class style is something entirely diferent from window style. The class styles are the CS_* ones.
Usually "The parameter is incorrect" is the WINAPI's way of saying, "dude, you're sending me crap."
So one of the WNDCLASSEX member variables is probably crap. Start by taking a closer look at the variables that are most likely to have something inappropriate in them: m_wcx.hInstance, m_wcx.lpfnWndProc, and m_wcx.lpszClassName.
EDIT:
As pointed out by #Johann Gerell, m_wcx.style = WS_ICONIC is an example of this. The documentation says that this is a class style, but you've sent a window style. No good.
What's the difference? Well, you know the difference between a C++ class and an object, right? A class is like a blueprint. An object is an instantiation of that blueprint. Same is true of Window Classes & Windows. A Window Class is a blueprint for creating a window, and a window is an instantiation of that Window Class. Window Classes have styles that specify things like what kind of DC to use, when to vertical refresh -- low level stuff like that which applies to every instance of that window class. Windows also have styles, but these are different. Window styles specify per-window things like if the window should be visible, minimized, etc. So RegisterClassEx asked you for an orange, and you tried to give it an apple.

How to create a hidden window in C++

How to create a hidden window ?
The purpose of this window is to receive some messages.
When you create the window, omit the WS_VISIBLE flag and don't call ShowWindow.
In a win32/mfc environment what you need to do is create a class and inherit from CWnd like this:
class HiddenMsgWindow : public CWnd
{
...
}
in the constructor of that class you would instantiate a window like this:
HiddenMsgWindow::HiddenMsgWindow()
{
CString wcn = ::AfxRegisterWndClass(NULL);
BOOL created = this->CreateEx(0, wcn, _T("YourExcellentWindowClass"), 0, 0, 0, 0, 0, HWND_MESSAGE, 0);
}
This gets you a hidden window with a message pump almost ready to rock and roll.
the rest of the story is to provide the linkage between the window messages and the handlers for those messages.
This is done by adding a few macros and a message handler to your implementation file (.cpp) like this:
BEGIN_MESSAGE_MAP(HiddenMsgWindow, CWnd)
ON_MESSAGE(WM_USER + 1, DoNOOP)
END_MESSAGE_MAP()
LRESULT HiddenMsgWindow::DoNOOP(WPARAM wParam, LPARAM lParam)
{
AfxMessageBox(_T("Get Reaaady for a Ruuummmmmmmbllllle!"));
return LRESULT(true);
}
Then you need to fill in the rest of the glue in the header file like this:
class HiddenMsgWindow : public CWnd
{
public:
HiddenMsgWindow();
protected:
afx_msg LRESULT DoNOOP(WPARAM wParam, LPARAM lParam);
DECLARE_MESSAGE_MAP()
}
And just like magic, you have a hidden window all ready to pump your messages.
In order to use this message window you would instantiate the class retrieve it's handle and send or post messages as desired. Just like this:
HiddenMsgWindow *hmw = new HiddenMsgWindow();
SendMessage(hmw->m_hWnd, WM_USER + 1, 0, 0);
You call CreateWindow() or CreateWindowEx() as usual but don't specify the WS_VISIBLE window style. Of course ShowWindow() should also not be called.
If you don't need to show the window,
as some suggest create the window, omit the WS_VISIBLE flag and don't call ShowWindow.
If not when you call showWindow(), add SW_HIDE parameter.
ShowWindow(hWnd, SW_HIDE);
Ahh, Just came across an issue. If u fail to specify HWND_MESSAGE unexpected behaviours could occur. I have used NULL which is NOT correct. In my case it caused that MS Excel took 10s or more to load an xls file, while it normally takes less then a second when my app was not running!
MSDN says this as someone mentioned before.