Close callback or WM_CLOSE from a HWND reference - c++

I'm calling the HtmlHelpA winapi method to display the .chm of my app. This method returns a HWND immediatelly, and the help window is shown as a separate window.
Along with the window, I set a timer for this hwnd. My problem is that I need to know when this window gets closed to kill the timer.
My first approach was trying to register the WndProc callback, but I couldn't do that because I'm not creating the window, I only have a reference to the hwnd.
Then I tried with a hook (SetWindowsHookEx) but the HOOKPROC won't bring the HWND as a parameter to the callback. Besides, I need to know the thread for this hwnd.
Is there any way to register a callback when a HWND gets closed or having a WndProc to wait for the WM_CLOSE message?

If required you can register a new window procedure for an existing window. Check out the documentation on SetWindowLongPtr().
Invoking it like this:
SetWindowLongPtr(hwnd, GWLP_WNDPROC, &MyCustomHelpWindowProc);
Just remember that window subclassing is very delicate. You might want to store the old window procedure somewhere and invoke that rather than DefWindowProc() for messages you are not interested in.

You want to subclass the help window. Subclassing gives you a chance to spy on all the messages going to the window proc. You do whatever additional work you need when you see a message of interest, and then pass the message on to the original window procedure for normal processing.
LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
WNDPROC fnOldProc = reinterpret_cast<WNDPROC>(::SetWindowLongPtr(hwndHelp, GWLP_WNDPROC, &MyWndProc));
LRESULT CALLBACK MyWndProc(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
if (msg == WM_CLOSE) {
// Kill your timer here.
}
return CallWindowProc(fnOldProc, hwnd, msg, wp, lp);
}

Related

C++: How to set a new wndProc for a console application?

If I have a console application with a handle to it set up like so;
HWND hWnd = GetConsoleWindow();
Then how do I set up a new wndProc for the window?
I tried using
SetWindowLong(hWnd, GWL_WNDPROC, (LONG)conProc);
With conProc being defined as
LRESULT CALLBACK conProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch(msg)
{
case WM_NCHITTEST:
return HTCAPTION;
}
return DefWindowProc(hWnd, msg, wParam, lParam );
}
But it doesn't work and says "Error code: 5 - Access is denied" on GetLastError()
I understand that it's pretty difficult to modify the console application like this, since it's a csrss.exe application and all, but I'd still like to try..
Thanks.
While the impression is that console window belongs to your process (like other window), it is in fact hosted by CSRSS system process and its WndProc is there. This makes you unable to subclass the window and provide your own WndProc living in your process.
Some related reading:
The process that is in charge of displaying the GUI windows in which consoles are presented is... CSRSS
SetWindowsHookEx with WH_KEYBOARD doesn't work for me, what do I wrong?
Subclassing XP Console Window
First of all SetWindowLong is superseded by SetWindowLongPtr, you should use that function.
Are you trying to change the WNDPROC of your own console window or another process?
From the MSDN docs :
GWL_WNDPROC
-4
Sets a new address for the window procedure.
You cannot change this attribute if the window does not belong to the same process as the calling thread.

not properly closing from taskbar closing event

if i close my application via Alt-F4, or the corner "X" button, or by posting destroywindow(hwnd) myself, the application closes correctly, and everything works just as expected.
but recently i realized, on windows7, when i close the app by right klicking on the icon in the taskbar, and clicking "Close Window", the window closes, but my app is still running. when debugging, i don't get into either of the WM_QUIT/DESTROY/CLOSE events.
is there some special behavior when closing a window via taskbar? how can i detect that?
You should get WM_CLOSE. Maybe you're getting it on a different window that the one you're expecting? Although that shouldn't be able to happen unless you have multiple top-level windows.
via Alt-F4, or the corner "X" button, or by posting destroywindow(hwnd) myself
You can't post DestroyWindow(). Ensure that the window procedure of your main window resembles this:
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
// Other cases
//...
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
The PostQuitMessage() function call ensures that your message loop exits, GetMessage() returns FALSE when it receives the WM_QUIT message. This makes your main window behave like the main window, closing it ends the process. You may have other top-level windows that don't, they shouldn't have this WM_DESTROY message handler. DestroyWindow() is already called automatically by DefWindowProc() when it processes the WM_CLOSE message.

a c++ timer is blocking my entire class?

i guess there is some reasonable and an easy solution. So for instance i have:
INT_PTR CALLBACK About(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
time(2)
// anything else in the about class....
//return code
}
The problem is that the whole application almost freezes and i cannot do anything but "wait for the timer".
How can i make the timer runs irrespectively to any other operation? 10x!
You want to do a SetTimer call and either give it a pointer to a callback function, or respond to the WM_TIMER message that comes back.

Window Wrapper Class C++ (G++)

I am attempting to learn about creating windows in c++, I have looked at an article about creating a wrapper class but I don't really understand it. So far I know that you can't have a class method WndProc (I dont know why) but honestly, that is all. Can somebody give an explanation, also explaining the reinterpret_cast? Here is the article.
LRESULT CALLBACK Window::MsgRouter(HWND hwnd, UINT message,
WPARAM wparam, LPARAM lparam)
{
Window *wnd = 0;
if(message == WM_NCCREATE)
{
// retrieve Window instance from window creation data and associate
wnd = reinterpret_cast<Window *>((LPCREATESTRUCT)lparam)->lpCreateParams;
::SetWindowLong(hwnd, GWL_USERDATA, reinterpret_cast<long>(wnd));
// save window handle
wnd->SetHWND(hwnd);
}
else
// retrieve associated Window instance
wnd = reinterpret_cast<Window *>(::GetWindowLong(hwnd, GWL_USERDATA));
// call the windows message handler
wnd->WndProc(message, wparam, lparam);
}
Thanks in advance, ell.
The MsgRouter() procedure acts as a proxy between the Windows message handling system to the Window instance associated with a HWND. It routes Windows messages to C++ objects.
A pointer to the Window instance is passed to the MsgRouter() procedure via the last parameter of the CreateWindow() function. When you first create a HWND via CreateWindow() (or CreateWindowEx()), some messages are sent - one of them being WM_NCCREATE. When the procedure receives a WM_NCCREATE message, the LPARAM parameter contains a pointer to a CREATESTRUCT which contains the arguments that was passed into the CreateWindow() function. The procedure retrieves the Window instance pointer from the CREATESTRUCT and saves it in the HWND by setting it as an attribute of the HWND (GWL_USERDATA via SetWindowLong()).
Now that the pointer has been saved, the window procedure can from now on retrieve a pointer to the original Window instance from a HWND via GetWindowLong() when it receives a message. Finally, the window procedure calls WndProc() on the retrieved Window pointer, passing in the exact message and parameters, so the Window instance can handle the message.

PostMessage for cross application messages

I'm trying to send a keystroke to another application. I can successfully find the window handle since using SendMessage worked exactly as intended.
However, when I switched the SendMessage over to PostMessage, the application no longer received the messages.
I did, however, find a workaround by using HWND_BROADCAST as the window handle, and it works fine, but isn't the ideal way to go about it.
What I'm asking is, I have a valid hWnd, how can I send it messages using PostMessage and not SendMessage?
Edit
This is what I'm trying to do.
HWND Target = FindWindow(0, "Window Title Goes Here");
LPARAM lParam = (1 | (57<<16)); // OEM Code and Repeat for WM_KEYDOWN
WPARAM wParam = VK_SPACE;
PostMessage(HWND_BROADCAST, WM_KEYDOWN, wParam, lParam); // Works
PostMessage(Target, WM_KEYDOWN, wParam, lParam); // Doesn't Work
SendMessage(Target, WM_KEYDOWN, wParam, lParam); // Works, but I need Post
The PostMessage function does not work when the message numbers between 0 and WM_USER-1. Use RegisterWindowMessage function to register your own messages.
Sent messages and posted messages take completely different routeres. Target is recieving your posted message, it's just either filtering or dispatching it to another window. It gets to do what ever it wants with it. When you send the messages, it goes directly to the window procedure without filtering, so is most likely that cause of that issue.
I don't know why HWND_BROADCAST is working; my best guess is that a window other than Target is processing the message. Or maybe its even being sent to a different window than Target. (You do realize that HWND_BROADCAST sends the messages to every top level window)
There is a Win32 API function designed to send input, SendInput(), that places the messages on the input queue just like a user keypress. However this doesn't let you specify a window, it sends its input to the active window. To use it you would have to activate and switch focus to Target, which means the user would see that window move to the top (just like you Alt-Tabbed to it). Along that same route VBScript has a SendKeys() function that does the same thing, but is easier to use.
As a final alternative you could use SendMessageCallback() which will give you the behavior of an asynchronous SendMessage which is what I assume you want. (And is different than PostMessage. Posted messages go into the posted message queue, sent messages are delivered directly)
*For the lparam go here http://msdn.microsoft.com/en-us/library/ms646280%28v=vs.85%29.aspx, change the 32 bits (31...3 2 1 0) of lParam. Once you have the binary sentence you want for your paramaters (cRepeat, Scancode etc), convert it to hexadecimal.
try this :
void SendString(HWND h, char *text)
{
int len = strlen(text);
for(int i = 0; i < len; i++)
PostMessage(h, WM_CHAR, text[i], 0);
}
HWND Target = FindWindow(0, "Window Title Goes Here");
LPARAM lParam = //The hexadecimal value matching with the parameters you want* example 0x29A1.
WPARAM wParam = VK_SPACE;
PostMessage(HWND_BROADCAST, WM_KEYDOWN, wParam, lParam);
PostMessage(Target, WM_KEYDOWN, wParam, lParam);
SendString(Target, (char*)"themessageyouwant\n");