I am writing a Windows Api wrapper and I have run into a problem. I am abstracting the Window Procedure (WndProc) and wrote a static MsgHandler which would call the WndProc of my WinHandler class. Here is the code:
LRESULT CALLBACK WinHandle::MsgHandler(HWND hwnd,UINT msg,WPARAM wParam,LPARAM lParam)
{
WinHandle* wnd = 0;
if (msg == WM_NCCREATE)
{
::SetWindowLong(hwnd,GWL_USERDATA,
long((LPCREATESTRUCT(lParam))->lpCreateParams));
}
wnd = (WinHandle*) (::GetWindowLong(hwnd,GWL_USERDATA));
if (wnd)
return wnd->WndProc(msg,wParam,lParam);
else
return ::DefWindowProc(hwnd,msg,wParam,lParam);
}
Since this functions is static , there is no this pointer and it works perfectly with WNDCLASSEX, but for some reason it never calls wnd->WndProc() and always returns the default window procedure. Need Help. What is wrong? Does anyone know a better approach?
Call SetWindowLong when you receive WM_CREATE, not WM_NCCREATE. I don't think lpCreateParams is valid in WM_NCCREATE. That is:
if (msg == WM_CREATE)
{
::SetWindowLong(hwnd,GWL_USERDATA,
long((LPCREATESTRUCT(lParam))->lpCreateParams));
}
Related
I'll go ahead and give a summary to this, how can I use a dialog procedure that is a member of a class? I am creating a window wrapper class, but CreateDialogParam needs a global dialog procedure, so I tried this workaround:
I have done a bit of searching on this topic. I am making a Dialog class which I am subclassing to make a CMainWnd and then instantiating that. In the Dialog class I have a member function defined as INT_PTR CALLBACK Dialog::cb_proc(HWND,UINT,WPARAM,LPARAM). Now, I know that windows must have a global function as a callback procedure.
So I made a std::map<HWND,Dialog*> DlgProcs map to associate the dialogs window handle with its Dialog class pointer.
And a INT_PTR CALLBACK DlgMainProc(HWND,UINT,WPARAM,LPARAM) so I could pass that to CreateDialogParam(). In the body of DlgMainProc(...) I search the map for using the hWnd parameter to find the Dialog* and return its cb_proc(..) member.
My problem is that none of the messages get processed, this is because the member procedure in my Dialog class never gets called. Even though when I put a MessageBox() in DlgMainProc inside a if (DlgProcs.find(hWnd) != DlgProcs.end()) { statement, the messagebox is displayed, over and over again until I have to abort the program from Visual Studio 2008. Which tells me that it is finding the hWnd in my map. The weird thing is it also does this if I put it in the else statement after that, which contradictingly tells me it is NOT finding the hWnd in the map.
If I put a messagebox in the cb_proc member function it does not get displayed at all. But during this I never get any compiler, linker, or runtime errors. When I remove the messagebox from it (as to not have to abort the program, it was just for debugging purposes) the program runs but no messages get processed, the X button does not close the program, button clicks do nothing.
Here is the PasteBin code: http://pastebin.com/GsGUBpZU
Btw, I have no problem subclassing this, my window is created fine, just no messages are processed, cb_proc just never gets called.
EDIT: Here is the relevant parts of the code
map<HWND,Dialog*> g_DlgProcs;
INT_PTR CALLBACK g_MainDlgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
if (g_DlgProcs.find(hWnd) != g_DlgProcs.end()) {
Alert("blah"); // Gets executed repeatedly
return g_DlgProcs[hWnd]->cb_proc(hWnd, msg, wParam, lParam);
} else {
Alert("blah"); // Removing the above alert, this gets
// executed repeatedly, erm, as well.. O.o strange
return FALSE;
}
}
Dialog::Dialog(int id, HWND parent /* = HWND_DESKTOP */) {
_id = id;
_parent = parent;
// Tried this before CreateDialogParam
g_DlgProcs.insert(make_pair(_handle, this));
_handle = CreateDialogParam(
(HINSTANCE)GetModuleHandle(NULL),
MAKEINTRESOURCE(id), _parent,
(DLGPROC)g_MainDlgProc, NULL
);
// Then tried it after CreateDialogParam
g_DlgProcs.insert(make_pair(_handle, this));
}
INT_PTR CALLBACK Dialog::cb_proc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) {
Alert("blah"); // Never gets executed
bool handled = true;
switch (msg)
{
case WM_INITDIALOG:
OnInitialize();
break;
case WM_COMMAND:
if (HIWORD(wParam) == 0 || HIWORD(wParam) == 1) {
OnMenuCommand((HIWORD(wParam) == 1), (int)LOWORD(wParam));
} else {
OnCtrlCommand((int)HIWORD(wParam), (int)LOWORD(wParam), (HWND)lParam);
}
break;
case WM_NOTIFY:
{
LPNMHDR head = (LPNMHDR)lParam;
OnNotification(head->code, head->idFrom, head->hwndFrom);
}
break;
case WM_CLOSE:
OnClose(); // DestroyWindow(_handle)
break;
case WM_DESTROY:
OnDestroy(); // PostQuitMessage(0)
default:
handled = ProcessMsg(msg, wParam, lParam);
}
// Convert bool to Windows BOOL enum
return ((handled == true) ? TRUE : FALSE);
}
Does anybody know why it never gets called? Or maybe just guide me to another way to use a member function as a DLGPROC?
The standard solution is to pass your this pointer as the last parameter to Create,DialogParam, stash it in DWLP_USER in your WM_INITDIALOG handler, and retrieve it from DWLP_USER thereafter. Basically you use DWLP_USER as your map.
I tried your code and it worked: cb_proc gets called. You will miss any messages (e.g. WM_INITDIALOG) that get sent before CreateDialogParam returns.
You can fix the latter problem by adding the window handle and the object to the map in g_MainDlgProc. If you get a message for an unknown window, you know it belongs to the window you're creating; put the object in a global and you can add the handle/object to the map.
I'm just adding this here in case someone finds it useful; using the magic of C++11 lambdas and templates you can have a simple wrapper template that means you don't have to continually rewrite the boiler-plate code for saving and loading userdata in window and dialog box procedures.
Here's an example for the DialogBoxParam function, but the same technique can be applied to CreateDialogParam and CreateWindowEx as well.
template <typename T, INT_PTR (T::*P)(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)>
INT_PTR DialogBoxThis(T* pThis, HINSTANCE hInstance, LPCWSTR lpTemplateName, HWND hWndParent)
{
return ::DialogBoxParam(hInstance, lpTemplateName, hWndParent, [](HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) -> INT_PTR {
if (uMsg == WM_INITDIALOG) SetWindowLongPtr(hWnd, DWLP_USER, lParam);
T* pThis = reinterpret_cast<T*>(GetWindowLongPtr(hWnd, DWLP_USER));
return pThis ? (pThis->*P)(hWnd, uMsg, wParam, lParam) : FALSE;
}, reinterpret_cast<LPARAM>(pThis));
}
You would use it like this:
class MyClass
{
INT_PTR MyDlgProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
};
// from inside MyClass, we can show a dialog that uses member function MyDlgProc as the dialog procedure
// note it is NOT a static function
DialogBoxThis<MyClass, &MyClass::MyDlgProc>(this, hInstance,
MAKEINTRESOURCE(IDD_MYDIALOG), hWndParent);
I want to detect everytime I paste something. It's just something to make some data entry work simpler.
I set a global hook and then "wait" for the wm_paste. This is part of the code I have:
LRESULT CALLBACK GetMsgProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if(nCode < 0)//Do not process the message
return CallNextHookEx(msg_hook,nCode,wParam,lParam);
LPMSG m=(LPMSG)lParam;
if(m->message == WM_PASTE)
{
OutputString("Paste detected!\n");
}
if(m->message == WM_PASTE)
{
OutputString("Paste detected!\n");
}
return CallNextHookEx(msg_hook,nCode,wParam,lParam);
}
//DLL_ATTACH:
...
if(strstr(ProcName, LOADERNAME))
{
InitCommonControls();
if(!(msg_hook=SetWindowsHookEx(WH_GETMESSAGE, GetMsgProc, hinstDLL, 0)))
{
ErrorExit(TEXT("SetWindowsHookEx"));
//MessageBox(0, "WH_GETMESSAGE", 0, 0);
//return -1;
}
}
WM_PASTE debug string never gets printed. I'm aware that not all applications use WM_PASTE. But at least notepad should work.
Any suggestions?
Thanks!
In GetMsgProc, the wParam parameter is not the message being intercepted, but a flag that indicates whether or not the message in lParam and been removed from the message queue.
You should be using m->wParam instead.
Wm_paste message is fired only in combo box and edit control. There is no easy way to capture paste, but you can get copy message by creating a tiny window and adding this window to the chain of clipboard viewers.
VS2008, c++, mfc
I have to handle messages from the child windows in the parent window. In fact i want to handle only ON_BN_CLICKED messages and then make some ather actions.
As i understood i have to redefine WindowProc():
LRESULT CDLauncherDlg::WindowProc(UINT mes, WPARAM wp, LPARAM lp)
{
HWND hWnd = this->m_hWnd;
switch (mes){
case WM_COMMAND:
if((LOWORD(wp)==IDC_BUTTON4)&& (HIWORD(wp) == BN_CLICKED))
{
MessageBox("Button pressed.", "", 0);
}
break;
}
return DefWindowProc(mes, wp, lp);
}
Unfortunatelly, after pressing Cancel button DefWindowProc() does nothing and i can't close the application.
What's the problem?
The final answer was to replace
return DefWindowProc(mes, wp, lp);
with
return CDialog::WindowProc(mes, wp, lp);
Your code snippet doesn't indicate you're handling a WM_CLOSE message, or that you're explicitly calling DestroyWindow() when IDC_BUTTON4 is clicked. If this is a child window, and you want to terminate the application, you could call DestroyWindow() and then somewhere later PostQuitMessage().
If your snippet here is the windowproc for your parent window, and the handling of IDC_BUTTON4 is the parent window receiving the original message that you handled in the child and passed to the parent, simply call PostQuitMessage() where you've put the call to MessageBox().
I want to create my own class to handle creating windows and the window procedure but I have noticed that the window procedure has to be static! I'm now wondering whether its possible to make the window procedure object oriented? I have read some tutorials on object oriented windows, but they always make the procedure static -.- whats the use in that? :/
Any links or info on how to get around this problem would be appreciated,
thanks
You can get around that by making the static WndProc delegate everything to the members:
// Forward declarations
class MyWindowClass;
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
std::map<HWND, MyWindowClass *> windowMap;
// Your class
class MyWindowClass {
private:
HWND m_handle;
// The member WndProc
LRESULT MyWndProc(UINT message, WPARAM wParam, LPARAM lParam) { /* ... */ }
public:
MyWindowClass()
{
/* TODO: Create the window here and assign its handle to m_handle */
/* Pass &WndProc as the pointer to the Window procedure */
// Register the window
windowMap[m_handle] = this;
}
};
// The delegating WndProc
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
std::map<HWND, MyWindowClass *>::iterator it = windowMap.find(hWnd);
if (it != windowMap.end())
return it->second->MyWndProc(message, wParam, lParam);
return 0;
}
The general technique of allowing a window instance to be represented by as class instance is to make use of the SetWindowLongPtr and GetWindowLongPtr to associate your class instance pointer with the window handle. Below is some sample code to get you started. It may not compile without a few tweaks. It's only meant to be a reference.
Personally, I've stopped rolling my own window classes back a few years ago when I discovered ATL's CWindow and CWindowImpl template class. They take care of doing all this mundane coding for you so can focus on just writing methods that handle window messages. See the example code I wrote up here.
Hope this helps.
class CYourWindowClass
{
private:
HWND m_hwnd;
public:
LRESULT WndProc(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_CREATE: return OnCreate(wParam, lParam);
case wM_PAINT: return OnPaint(wParam, lParam);
case WM_DESTROY:
{
SetWindowLongPtr(m_hwnd, GWLP_USERDATA, NULL);
m_hwnd = NULL;
return 0;
}
}
return DefWindowProc(m_hwnd, uMsg, wParam, lParam);
}
CYourWindowClass()
{
m_hwnd = NULL;
}
~CYourWindowClass()
{
ASSERT(m_hwnd == NULL && "You forgot to destroy your window!");
if (m_hwnd)
{
SetWindowLong(m_hwnd, GWLP_USERDATA, 0);
}
}
bool Create(...) // add whatever parameters you want
{
HWND hwnd = CreateWindow("Your Window Class Name", "Your Window title", dwStyle, x, y, width, height, NULL, hMenu, g_hInstance, (LPARAM)this);
if (hwnd == NULL)
return false;
ASSERT(m_hwnd == hwnd);
return true;
}
static LRESULT __stdcall StaticWndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
CYourWindowClass* pWindow = (CYourWindowClass*)GetWindowLongPtr(hwnd, GWLP_USERDATA);
if (uMsg == WM_CREATE)
{
pWindow = ((CREATESTRUCT*)lParam)->lpCreateParams;
SetWindowLongPtr(hwnd, GWLP_USERDATA, (void*)pWindow);
m_hWnd = hwnd;
}
if (pWindow != NULL)
{
return pWindow->WndProc(uMsg, wParam, lParam);
}
return DefWindowProc(hwnd, uMsg, wParam, lParam);
};
};
If you are looking for object oriented Win32 API then you should look to MFC and/or WTL.
Just to add to Brian's answer but for a win32 framework that's more beginner friendly take a look at Win32++. The library itself isn't as comprehensive in features compared to MFC or QT but that is a tradeoff the designer made at the beginning to keep the library easy to understand and simple to use.
If you're still interested in this topic, I highly encourage you to take a look at it since it uses yet another technique for saving the 'this' pointer by utilizing thread local storage.
You can use the window handle passed to the WindowProc to grab an object you've created for that particular window and delegate the event handling to that object.
e.g.
IMyWindowInterface* pWnd = getMyWindowObject(hWnd);
pWnd->ProcessMessage(uMsg, wParam, lParam);
I am trying to subclass the currently focused window on a Windows system using a global CBT hook. This is related to what happens in this question, but the bug is different.
What happens when this subclassing is in effect, is that Opera's (version 10.50) main window is prevented from displaying. Opera has a "splash screen" where you are required to click "Start" for the main window to show that appears after Opera has not shut down properly. Whenever this window pops up, Opera's main window won't show. If Opera was shut down properly, and this splash screen does not show, the main window displays as it should.
HHOOK hHook;
HWND hWndSubclass = 0;
void SubclassWindow(HWND hWnd)
{
Unsubclass();
FARPROC lpfnOldWndProc = (FARPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LPARAM)SubClassFunc);
SetProp(hWnd, L"PROP_OLDWNDPROC", lpfnOldWndProc);
hWndSubclass = hWnd;
}
void Unsubclass()
{
if (hWndSubclass != 0 && IsWindow(hWndSubclass))
{
FARPROC lpfnOldWndProc = (FARPROC)GetProp(hWndSubclass, L"PROP_OLDWNDPROC");
RemoveProp(hWndSubclass, L"PROP_OLDWNDPROC");
SetWindowLongPtr(hWndSubclass, GWLP_WNDPROC, (LPARAM)lpfnOldWndProc);
hWndSubclass = 0;
}
}
static LRESULT CALLBACK SubClassFunc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
if (message == WM_MOVING)
{
// do something irrelevant
}
else if (message == WM_DESTROY)
{
Unsubclass();
}
FARPROC lpfnOldWndProc = (FARPROC)GetProp(hWndSubclass, L"PROP_OLDWNDPROC");
return CallWindowProc((WNDPROC)lpfnOldWndProc, hWndSubclass, message, wParam, lParam);
}
static LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam)
{
if (nCode == HCBT_SETFOCUS && hWndServer != NULL)
{
SubclassWindow((HWND)wParam);
}
if (nCode < 0)
{
return CallNextHookEx(hHook, nCode, wParam, lParam);
}
return 0;
}
BOOL APIENTRY DllMain( HINSTANCE hInstance,
DWORD Reason,
LPVOID Reserved
)
{
switch(Reason)
{
case DLL_PROCESS_ATTACH:
hInst = hInstance;
return TRUE;
case DLL_PROCESS_DETACH:
Unsubclass();
return TRUE;
}
return TRUE;
}
My suspicion is that Opera's main window is somehow already subclassed. I imagine the following is happening:
The window is created with it's own basic WndProc, and is given focus
My application subclasses the window, storing the original WndProc
Opera subclasses its own window
When the window loses focus, I restore the original WndProc, thus ignoring the second WndProc
Can this really be the case? Are there any other explanations?
This can happen, as Raymond Chen writes:
Consider what would happen if somebody else had subclassed the window during the "... do stuff ..." section. When we unsubclassed the window, we would have removed two subclasses, the one we installed, and the one that was installed after us. If the other subclass allocated memory (which is very common), then that memory got leaked, in addition to the subclass failing to do whatever it was trying to do.
He continues with a solution:
This is quite a cumbersome process, so the shell team wrote some helper functions to do all this for you. The SetWindowSubclass function does all the grunt work of installing a subclass procedure, remembering the previous one, and passing reference data to the subclass procedure you provide. You use the DefSubclassProc function to forward the message to the previous subclass procedure, and when you're done, you use the RemoveWindowSubclass function to remove yourself from the chain. RemoveWindowSubclass does all the work to do the right thing if you are not the window procerure at the top of the chain.