Window Messages (Get Started with Win32 and C++)
A GUI application must respond to events from the user and from the operating system.
- Events from the user include all the ways that someone can interact with your program: mouse clicks, key strokes, touch-screen gestures, and so on.
- Events from the operating system include anything «outside» of the program that can affect how the program behaves. For example, the user might plug in a new hardware device, or Windows might enter a lower-power state (sleep or hibernate).
These events can occur at any time while the program is running, in almost any order. How do you structure a program whose flow of execution cannot be predicted in advance?
To solve this problem, Windows uses a message-passing model. The operating system communicates with your application window by passing messages to it. A message is simply a numeric code that designates a particular event. For example, if the user presses the left mouse button, the window receives a message that has the following message code.
Some messages have data associated with them. For example, the WM_LBUTTONDOWN message includes the x-coordinate and y-coordinate of the mouse cursor.
To pass a message to a window, the operating system calls the window procedure registered for that window. (And now you know what the window procedure is for.)
The Message Loop
An application will receive thousands of messages while it runs. (Consider that every keystroke and mouse-button click generates a message.) Additionally, an application can have several windows, each with its own window procedure. How does the program receive all these messages and deliver them to the correct window procedure? The application needs a loop to retrieve the messages and dispatch them to the correct windows.
For each thread that creates a window, the operating system creates a queue for window messages. This queue holds messages for all the windows that are created on that thread. The queue itself is hidden from your program. You cannot manipulate the queue directly. However, you can pull a message from the queue by calling the GetMessage function.
This function removes the first message from the head of the queue. If the queue is empty, the function blocks until another message is queued. The fact that GetMessage blocks will not make your program unresponsive. If there are no messages, there is nothing for the program to do. If you have to perform background processing, you can create additional threads that continue to run while GetMessage waits for another message. (See Avoiding Bottlenecks in Your Window Procedure.)
The first parameter of GetMessage is the address of a MSG structure. If the function succeeds, it fills in the MSG structure with information about the message. This includes the target window and the message code. The other three parameters let you filter which messages you get from the queue. In almost all cases, you will set these parameters to zero.
Although the MSG structure contains information about the message, you will almost never examine this structure directly. Instead, you will pass it directly to two other functions.
The TranslateMessage function is related to keyboard input. It translates keystrokes (key down, key up) into characters. You do not really have to know how this function works; just remember to call it before DispatchMessage. The link to the MSDN documentation will give you more information, if you are curious.
The DispatchMessage function tells the operating system to call the window procedure of the window that is the target of the message. In other words, the operating system looks up the window handle in its table of windows, finds the function pointer associated with the window, and invokes the function.
For example, suppose that the user presses the left mouse button. This causes a chain of events:
- The operating system puts a WM_LBUTTONDOWN message on the message queue.
- Your program calls the GetMessage function.
- GetMessage pulls the WM_LBUTTONDOWN message from the queue and fills in the MSG structure.
- Your program calls the TranslateMessage and DispatchMessage functions.
- Inside DispatchMessage, the operating system calls your window procedure.
- Your window procedure can either respond to the message or ignore it.
When the window procedure returns, it returns back to DispatchMessage. This returns to the message loop for the next message. As long as your program is running, messages will continue to arrive on the queue. Therefore, you must have a loop that continually pulls messages from the queue and dispatches them. You can think of the loop as doing the following:
As written, of course, this loop would never end. That is where the return value for the GetMessage function comes in. Normally, GetMessage returns a nonzero value. When you want to exit the application and break out of the message loop, call the PostQuitMessage function.
The PostQuitMessage function puts a WM_QUIT message on the message queue. WM_QUIT is a special message: It causes GetMessage to return zero, signaling the end of the message loop. Here is the revised message loop.
As long as GetMessage returns a nonzero value, the expression in the while loop evaluates to true. After you call PostQuitMessage, the expression becomes false and the program breaks out of the loop. (One interesting result of this behavior is that your window procedure never receives a WM_QUIT message. Therefore, you do not have to have a case statement for this message in your window procedure.)
The next obvious question is when to call PostQuitMessage. We’ll return to this question in the topic Closing the Window, but first we have to write our window procedure.
Posted Messages versus Sent Messages
The previous section talked about messages going onto a queue. Sometimes, the operating system will call a window procedure directly, bypassing the queue.
The terminology for this distinction can be confusing:
- Posting a message means the message goes on the message queue, and is dispatched through the message loop (GetMessage and DispatchMessage).
- Sending a message means the message skips the queue, and the operating system calls the window procedure directly.
For now, the difference is not very important. The window procedure handles all messages. However, some messages bypass the queue and go directly to your window procedure. However, it can make a difference if your application communicates between windows. You can find a more thorough discussion of this issue in the topic About Messages and Message Queues.
How do I intercept messages being sent to a window?
I want to intercept messages that are being sent to a window in a different process. What is the best way to do this? I can’t see the messages when I use the WH_GETMESSAGE hook, and I’m not sure if I can subclass across processes? Any help would be much appreciated.
2 Answers 2
You need to inject your own code into the process that owns the windows you wish to intercept messages from. Fortunately, SetWindowsHookEx() makes this fairly easy, although you may have a bit of trouble at first if you’ve only used it for in-process hooking up to now.
I can recommend two excellent articles on the subject:
If the message is sent rather than posted WH_GETMESSAGE won’t see it. You need WH_CALLWNDPROC . If you’re working across processes you’ll need a system-wide hook in a DLL. You don’t mention how you invoked SetWindowsHookEx, so I don’t know if your hooking failed because it wasn’t global, or because the message you were looking for was sent.
If you haven’t worked with system-wide hooks before, I have an example of a system-wide hook on my website here. That’s a keyboard hook, but the principle is the same.
Stop me if I’m unintentionally talking down here — your question was so short I can’t infer your expertise level. But messing around with hooks does imply some experience.
Intercepting Window Messages for another Window
I’m using CefGlue to make an application with an embedded webkit browser in it, and I need to listen for mousemovements in the browser window. The winforms control doesn’t pass down mouse events to the control so I can’t listen to them.
However, I found a bug/feature request with a solution in it, but it’s beyond me on how to implement it, I’m not familiar with working directly in the WinAPI. The developer says I need to:
2. OS-specific (windows) — after browser created (CefLifeSpanHandler.OnAfterCreated) get window handle and subclass them (window subclassing technique). Actually now we have native window with class CefBrowserWindow (returned by CefBrowser.GetHost().GetWindowHandle()), then child window Chrome_WidgetWin_0, and then Chrome_RenderWidgetHostHWND. For intercepting WM_MOUSEMOVE you are interesting in Chrome_WidgetWin_0 window, which can be easily obtained via CefBrowserWindow. Just play with Spy++ to see precisely.
I’ve done some googling, but I’m not sure how to hook into this. I have this function on my form:
But I never see a mouse move when I’m over the control. I suspect I need to be listening on the WndProc of CefBrowser.GetHost().GetWindowHandle() But I’m not sure how to do that.
1 Answer 1
I found a solution. I call this function in the OnAfterCreated event from the WebLifeSpanHandler.
The function I was looking for is GetWindowThreadProcessId which I can use the window handle provided by browser.CefBrowser.GetHost().GetWindowHandle()
One thing to note is that the Hook Procedure needs to have some sort of larger scope. C# doesn’t see it hooked into the native process and will Garbage Collect it if you let it go out of scope. To fix this, I made them class properties.
- http://support.microsoft.com/kb/318804How to set a Windows hook in Visual C# .NET Provided the basis for hooking into the mouse events. Modified to use the browser thread id
- http://msdn.microsoft.com/en-us/library/ms644988(v=vs.85).aspxMouseProc callback function How to handle the mouse windows hook
- http://msdn.microsoft.com/en-us/library/ms644984(VS.85).aspxKeyboardProc callback function How to handle the keyboard windows hook. Includes structure of lParam. Important note: wParam contains the Virtual Key Code of the key entered.
- http://msdn.microsoft.com/en-us/library/dd375731(v=vs.85).aspxVirtual-Key Codes List of keys and their Virtual Key Code, so I could tell what button the user was pressing.
My two hooks (not good code, I don’t do anything with the marshaled data):
This is the externs and the code needed to expose the hooks, this is class level scope:
I’m not sure this is entirely required, especially since the thread being hooked to is going away on close, but just for good measure, don’t forget to cleanup your hook after you’re done listening. I do this on my form’s dispose method:
Global hooks to intercept windows messages
#1
- Gender: Male
- Location: Romania
- Interests: Help and learn!
- Coding: C/C++,PHP,ASM x86,MySql,HTML5 + CSS3,JAVA,jQuery/javascript,COM
Hardware engineer,Network architect
It has been a long time since i’ve posted something interesting,so i decided to do so.
From what i understand,people are very interested in windows hooks and their great functionality.I’ve made an example for low level keyboard hooks some time ago here (http://www.rohitab.c. good-keylogger/) and i see a lot of interest in the idea.
Someone here asked if it can be possible to intercept something else,like messages passed in windows apps procedures,like WM_CREATE,WM_CLOSE,WM_COMMAND,etc.
The answer is yes,it can be done.But with global hooks.This is done using a dll that contains the proper hook procedure and an executable,of course that loads and shares this resource with every other windows application that will be interogated.
Ok so no more talk,i will explain for everyone that doesn’t understand something at the time.For now,the source code:
And the exe that implements it:
This will intercept any WM_CLOSE message in all applications and will show a message box each time,with the closed process name in the title!
Edited by Athenian, 14 November 2014 — 05:54 PM.
mov problems,hell
sub me,pain
add me,love
inc pleasure
inc adrenaline
push limits
call hopeForAbetterTomorrow
#2
- Gender: Male
- Location: Hungary
- Interests: C++, x86asm, Reverse Engineering
- Coding: AutoIt, C/C++
Could you explain me whatthose pragmas are, and why did you put that there?
I’ve just never heard of them
Hmm, as I read, it puts data into a different section, but why? Is there any reason, or just for fun?
Edited by Unc3nZureD, 14 November 2014 — 10:10 PM.
#3
- Gender: Male
- Location: Romania
- Interests: Help and learn!
- Coding: C/C++,PHP,ASM x86,MySql,HTML5 + CSS3,JAVA,jQuery/javascript,COM
Hardware engineer,Network architect
Sorry for that.They ware from another project,so you can remove them.There is no need for them!
You can store variables in your own defined section.For example:
will put i to .rohitab section in the PE file.
mov problems,hell
sub me,pain
add me,love
inc pleasure
inc adrenaline
push limits
call hopeForAbetterTomorrow
#4
- Gender: Male
- Location: Romania
- Interests: Help and learn!
- Coding: C/C++,PHP,ASM x86,MySql,HTML5 + CSS3,JAVA,jQuery/javascript,COM
Hardware engineer,Network architect
But i dont recomand it.What i can suggest you,if you want to force the linker to add some variables into a certain section,is this:
This will create a section with the name rohitab and with read,write and execute attributes.With __declspec(allocate(«.rohitab»)),we specify that we want the next line to be added to that section,in this case the ‘w’ character.Use PE Explorer to look into the sections of the executable or dll and you will see the result!
mov problems,hell
sub me,pain
add me,love
inc pleasure
inc adrenaline
push limits
call hopeForAbetterTomorrow
#5
- Gender: Male
- Location: Hungary
- Interests: C++, x86asm, Reverse Engineering
- Coding: AutoIt, C/C++
Only the next line will be added, or all of the others coming after this? (Sorry I can’t test it currently)
#6
- Gender: Male
- Location: Romania
- Interests: Help and learn!
- Coding: C/C++,PHP,ASM x86,MySql,HTML5 + CSS3,JAVA,jQuery/javascript,COM
Hardware engineer,Network architect
mov problems,hell
sub me,pain
add me,love
inc pleasure
inc adrenaline
push limits
call hopeForAbetterTomorrow
#7
- Gender: Male
- Location: Las Vegas, NV
- Interests: Coding
- Coding: C++, PHP
Suppose instead of «if (data->message == WM_CLOSE)» we’d have «if (data->message == WM_SETTEXT)»:
Is there any way to get the lParam value from data->lparam ?
Sometimes applications set text on windows with WM_SETTEXT, anyway to get this text ?
#8
- Gender: Male
- Location: Romania
- Interests: Help and learn!
- Coding: C/C++,PHP,ASM x86,MySql,HTML5 + CSS3,JAVA,jQuery/javascript,COM
Hardware engineer,Network architect
Yes my friend!
It can be done.Just modify the dll:
mov problems,hell
sub me,pain
add me,love
inc pleasure
inc adrenaline
push limits
call hopeForAbetterTomorrow
#9
- Gender: Male
- Location: Mars
- Interests: too many!
- Coding: Python,C,asm,java
this and the other hook tuts should be put together into one complete tutorial on windows hooks. like the one RosDevil did on dll injection.
Edited by Null_00, 08 August 2015 — 10:22 AM.
Shadow Network
Shadow Society
#10
- Gender: Male
- Location: Romania
- Interests: Help and learn!
- Coding: C/C++,PHP,ASM x86,MySql,HTML5 + CSS3,JAVA,jQuery/javascript,COM
Hardware engineer,Network architect
@null
Copy all source codes you think are the best on rohitab (all time) and put them into a huge archive.
Maybe one day rohitab wouldnt exist anymore and some people may seek knowledge.
If you got the time ofcourse!
mov problems,hell
sub me,pain
add me,love
inc pleasure
inc adrenaline
push limits
call hopeForAbetterTomorrow
#11
- Gender: Male
- Location: Las Vegas, NV
- Interests: Coding
- Coding: C++, PHP
Screenshot:
Edited by eclessiastes, 29 August 2015 — 06:08 AM.
#12
Oh. I am so happy, because I tried you code and it worked. Recently I want to get gesture information from Surface pro 3(like zoom), I think I need to use the hook :WH_GETMESSAGE. So I just replaced WH_CALLWNDPROC with WH_GETMESSAGE in you code. The hook can be installed, but the function in dll:
extern «C» __declspec(dllexport) LRESULT WINAPI procedure(int nCode, WPARAM wParam, LPARAM lParam)
can not be called.
So my friend,do you have any idea why it happened? I have been confused by this topic for almost one month! Waiting for your suggestion. Thank you so much!