Using the Win32 APIs, is it possible to create a Window or Dialog in one thread then collect events for it from another thread?
Are HWNDs tied to threads?
Trying the contrived example below I never see GetMessage() fire.
HWND g_hWnd;
DWORD WINAPI myThreadProc(LPVOID lpParam)
{
while(GetMessage(&msg, hWnd, 0, 0) > 0)
{
...
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MYDIALOG), 0, myDlgProc);
CreateThread(NULL, 0 myThreadProc, NULL, 0, NULL);
...
}
But here, I do.
HWND g_hWnd;
HINSTANCE g_hInstance;
DWORD WINAPI myThreadProc(LPVOID lpParam)
{
hWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MYDIALOG), 0, myDlgProc);
while(GetMessage(&msg, hWnd, 0, 0) > 0)
{
...
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
g_hInstance = hInstance;
CreateThread(NULL, 0 myThreadProc, NULL, 0, NULL);
...
}
Can somebody explain what I'm seeing?
No.
GetMessage returns messages on the current thread's input queue. The HWND parameter is a filter, so that GetMessage only returns messages in the current thread's input queue intended for that window.
Windows have thread affinity - messages intended for a window get handled on the thread that created and therefore owns the window.
From the MSDN:
The GetMessage function retrieves a
message from the calling thread's
message queue
So no, what you describe is not directly possible.
In your first example the Dialog and GetMessage are in separate threads. And the documentation says:
The GetMessage function retrieves a message from the calling thread's message queue.
The second example works since the calling thread (for GetMessage) also owns the Dialog.
Use AttachThreadInput.
In your example programm finish after create window.
But anyway in win32 all threads have own message queue.
And all message queues get messages for windows created in this thread.
see:
http://msdn.microsoft.com/en-us/library/ms644928(VS.85).aspx (Using Messages and Message Queues)
http://msdn.microsoft.com/en-us/library/ms644936(VS.85).aspx (GetMessage Function)
You can of course change the window procedure that handles messages for any window. Check the SetWindowLong function - http://msdn.microsoft.com/en-us/library/ms633591(VS.85).aspx - there are some rules as to what address space the new proc is. I suggest using a dll. Another way is to sub class the window message queue.
Of course you can !
Just use remote code injection ! (very classic !)
Related
When I create a window in C using the CreateWindow() function, it works fine it just disappears instantly, so I used the getch() function to try to resolve the issue but it does not work. The window does not display the button, and crashes.
But when I used MessageBox() instead of getch(), it stays and functions normally. I am trying to figure out why this happens.
I tried many things, like using MessageBox() and getch() together, using getch() before and after ShowWindow(), but every time it gives me some interesting result but not the normal functionality of the window.
Code that works:
#include <windows.h>
int _stdcall WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
HWND h;
HINSTANCE i;
h = CreateWindow("Button", "XYZ", WS_OVERLAPPEDWINDOW, 15, 20, 250, 200, 0, 0, i, 0);
ShowWindow(h, nCmdShow);
MessageBox(0, "Stop", "Wait", MB_OK);
return 0;
}
Code that does not work:
#include <windows.h>
int _stdcall WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
HWND h;
HINSTANCE i;
h = CreateWindow("Button", "XYZ", WS_OVERLAPPEDWINDOW, 15, 20, 250, 200, 0, 0, i, 0);
ShowWindow(h, nCmdShow);
getch();
return 0;
}
I want to know the reason for this error. I think it is because getch() is a DOS function, but still the compiler should at least show a warning.
There is no crash in this code.
Your getch() example simply lacks a message loop needed to service the window, and also getch() is meaningless in a non-console app. So your WinMain() exits immediately after showing the button window.
Whereas your MessageBox() example has a message loop (inside of MessageBox() itself) which keeps WinMain() running, and the button window processong UI messages, until the MessageBox dialog is closed.
Also, it doesn't make sense to try to display a button as its own overlapped window. You should be registering and creating a separate overlapped window that then creates the button as a child. User actions on the button are sent to the button's parent window, so you need to create a parent window for it.
Disclaimer: I'm somewhat of a noob when it comes to multi-threading. I've read stuff online, have done some simple multi-threading examples.
I've got a Win32 app that wants to draw the stuff in one thread and handle the Win32 messages in another thread. However, after the windows is created and the threads start, it hangs. I have a hunch that it may have to do with WaitForMultipleObjects(), but I don't know how to make it right. Does anyone have any idea why this is happening? Should I suspend and resume threads?
Here's my code:
WinAPI:
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nCmdShow)
{
/* initialization blah blah */
zgE->startThreads(); // <--- starts the 2 threads
WaitForMultipleObjects(zgE->getThreadsNo(), zgE->getThreads(), TRUE, INFINITE);
return TRUE;
}
This is how I start the threads:
void zgEngine::startThreads()
{
/* allocation and stuff, blah blah blah */
m_arrThreads[m_nThreads++] = CreateThread(NULL, 0, &zgEngine::handleMsg, (void*)this, NULL, NULL);
m_arrThreads[m_nThreads++] = CreateThread(NULL, 0, &zgEngine::drawObjects, (void*)this, NULL, NULL);
assert(m_nThreads <= THREADS_NO);
}
And the 2 functions that draw & handle messages are quite simplist. A while loop in each of them.
// draw function
DWORD WINAPI zgEngine::drawObjects(LPVOID lpParam)
{
while (true)
{
/* draw stuff - valid code that if called outside this function
works as intended */
}
return TRUE;
}
// message handler function
DWORD WINAPI zgEngine::handleMsg(LPVOID lpParam)
{
MSG msg;
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// Process the message
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return TRUE;
}
When I don't use threads and remove the "while (true)" in drawObjects(), but leave the code (to be executed only once), don't call handleMsg() and make WinMain like in the example below, it works like a charm.
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int nCmdShow)
{
/* initialization blah blah */
MSG msg;
while (true)
{
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
// Process the message
if (msg.message == WM_QUIT)
break;
TranslateMessage(&msg);
DispatchMessage(&msg);
} else {
zgEngine::DrawObjects(zgE);
}
}
return TRUE;
}
Later edit: from what I've seen, PeekMessage() always returns 0 :(
Quote from PeekMessage on Microsofts website:
A handle to the window whose messages are to be retrieved. The window
must belong to the current thread.
If hWnd is NULL, PeekMessage retrieves messages for any window that
belongs to the current thread, and any messages on the current
thread's message queue whose hwnd value is NULL (see the MSG
structure). Therefore if hWnd is NULL, both window messages and thread
messages are processed.
If hWnd is -1, PeekMessage retrieves only messages on the current
thread's message queue whose hwnd value is NULL, that is, thread
messages as posted by PostMessage (when the hWnd parameter is NULL) or
PostThreadMessage.
I suspect the thread doesn't have a "current window", and the "current threads message queue" isn't the one that Windows actually posts the messages onto as the default thread. This is only an assumption, since it is entirely possible that there are other problems (as well?) with your approach. But I believe this is a main portion of where the problem lies.
The following code works fine. It gives out the message when the user presses a key. But there are certain things I am not aware of. What is the role of Message Loop here ? I read that calling SetWindowsHookEx(...) registers a function with the windows and windows calls the appropriate function automatically when a event of registered type happens. No doubt that i don't see the output if don't give the message loop it's space.
#include<iostream>
#include <windows.h>
using namespace std;
HINSTANCE hinst = NULL;
static HHOOK handleKeyboardHook = NULL;
static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam);
void setWinHook() {
handleKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc,NULL, 0);
if(handleKeyboardHook == NULL) {
cout << "is NULL";
} else {
cout << "is not NULL";
}
cout<<("Inside function setWinHook !");
}
static LRESULT CALLBACK LowLevelKeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) {
cout << ("You pressed a key !\n");
return CallNextHookEx(handleKeyboardHook, nCode, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
{
handleKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, LowLevelKeyboardProc, hInstance, 0);
MSG msg;
while(GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
EDIT
Does exiting the program (closing the console window) unregister the hook ?
EDIT 2
what role does Dispatch Message play here ? According to doc it dispatches a message to window procedure,but here even if i exclude that,it doesn't effect the output.
All events in Windows, even the low-level keyboard event used in your example, is sent using the normal message events. So for the program to be able to sense keyboard events, it has to use an event loop processing messages.
Without a loop, the program would exit immediately, and the hook would be removed at once too. You cannot register a hook and exit — the system would become a mess if buggy programs were leaving too many forgotten hooks after them. Once your process dies, the hook is scheduled for removal.
I don't remember about low-level keyboard hook, but callbacks of many other hooks are only called inside GetMessage/PeekMessage, and not on some other thread, so just an infinite loop won't suffice — it has to be a message loop.
"what role does Dispatch Message play here ? According to doc it dispatches a message to window procedure,but here even if i exclude that,it doesn't effect the output."
DispatchMessage is pretty useless cos console window doesnt receive much messages.
Only message received is when window loses focus.
I want to catch:
Window resize/move/minimize/maximize/close messages.
Mouseclicks and keyboard presses.
When any program was executed by the user either pressing enter or dblclick. (if possible?)
This should work the same way as the keylock programs works: if you do some event, i can decide via my program will i let Windows handle it, or do i handle it, or both.
How can i do this?
As Hans Passant pointed out, you need the SetWindowsHookEx function.
In the link all possible hooks are explained in detail and the hook functions you need to implement are as well. Here is a small example, how to install a global hook that will process messages, after they are processed by the window.
int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow){
HHOOK msgHook = SetWindowsHookEx(WH_CALLWNDPROCRET, msgHook, hInstance, 0);
if(msgHook == NULL){
//Error handling here
cout << "Failed to set hook";
}
else{
//Hook has been set and will automatically be removed, when your application exits.
}
//A clean shutdown should always unhook everything it has installed
UnhookWindowsHookEx(msgHook);
return 0;
}
You can look up the hook functions definition in the MSDN, but it could look like this:
LRESULT CallWndRetProc(int nCode, WPARAM wParam, LPARAM lParam){
CWPRETSTRUCT* theMessage = (CWPRETSTRUCT*)lParam;
//now you can read all message parameters and the return value
//...
//Always return by calling the next hook in the chain
return CallNextHookEx(0, nCode, wParam, lParam);
}
The other hooks that you want to install follow the same principle.
See also
Hooks Overview
Using Hooks
Using the Win32 APIs, is it possible to create a Window or Dialog in one thread then collect events for it from another thread?
Are HWNDs tied to threads?
Trying the contrived example below I never see GetMessage() fire.
HWND g_hWnd;
DWORD WINAPI myThreadProc(LPVOID lpParam)
{
while(GetMessage(&msg, hWnd, 0, 0) > 0)
{
...
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
hWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MYDIALOG), 0, myDlgProc);
CreateThread(NULL, 0 myThreadProc, NULL, 0, NULL);
...
}
But here, I do.
HWND g_hWnd;
HINSTANCE g_hInstance;
DWORD WINAPI myThreadProc(LPVOID lpParam)
{
hWnd = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_MYDIALOG), 0, myDlgProc);
while(GetMessage(&msg, hWnd, 0, 0) > 0)
{
...
}
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
g_hInstance = hInstance;
CreateThread(NULL, 0 myThreadProc, NULL, 0, NULL);
...
}
Can somebody explain what I'm seeing?
No.
GetMessage returns messages on the current thread's input queue. The HWND parameter is a filter, so that GetMessage only returns messages in the current thread's input queue intended for that window.
Windows have thread affinity - messages intended for a window get handled on the thread that created and therefore owns the window.
From the MSDN:
The GetMessage function retrieves a
message from the calling thread's
message queue
So no, what you describe is not directly possible.
In your first example the Dialog and GetMessage are in separate threads. And the documentation says:
The GetMessage function retrieves a message from the calling thread's message queue.
The second example works since the calling thread (for GetMessage) also owns the Dialog.
Use AttachThreadInput.
In your example programm finish after create window.
But anyway in win32 all threads have own message queue.
And all message queues get messages for windows created in this thread.
see:
http://msdn.microsoft.com/en-us/library/ms644928(VS.85).aspx (Using Messages and Message Queues)
http://msdn.microsoft.com/en-us/library/ms644936(VS.85).aspx (GetMessage Function)
You can of course change the window procedure that handles messages for any window. Check the SetWindowLong function - http://msdn.microsoft.com/en-us/library/ms633591(VS.85).aspx - there are some rules as to what address space the new proc is. I suggest using a dll. Another way is to sub class the window message queue.
Of course you can !
Just use remote code injection ! (very classic !)