Hey guys,
I was just thinking it would be fun to replace DispatchMessage. Funny thing is, it works :)

This is a very simple example:


; invoke DispatchMessage,addr msg
invoke GetWindowLong,msg.hwnd,GWL_WNDPROC
push msg.lParam
push msg.wParam
push msg.message
push msg.hwnd
call eax


I think this is legit. It doesn't handle the possibility of a TimerProc but that can come later. Comments?

--Chorus
Posted on 2002-08-05 12:40:38 by chorus
Yes, that will work.
Posted on 2002-08-05 13:40:19 by comrade
Sure that will work, but what else does DispatchMessage do besides this ?
Thats the part that would concern me.
Posted on 2002-08-05 17:32:37 by James_Ladd
What I understand from the win32api is DispatchMessage does only a few things. It depends on the window handle. If the window is a dialogbox, it calls the dialogbox procedure given by DWL_WNDPROC. If it's a regular window, it calls the window procedure given by GWL_WNDPROC. Otherwise, if it's a timer message with no window associated with it, it calls the Time Proc (I think it's in lParam).

As far as I know that's all it does. Of course, I'm guessing. Hence the post.

There is one thing that I noticed after I made the post. Not an error per se. You see, I call the proc directly (call eax) whereas the win32api insists that you must call CallWindowProc,eax,etc. instead. I've experienced no problems however. I read a bit into GetWindowLong and it says it could return a handle to the WndProc instead of the pointer. In which case call eax wouldn't work.

Does anyone know when GetWindowLong returns a handle? It hasn't happened in my app. The app has my own Window Class, plus a list box and a combo box.

Thanks

--Chorus
Posted on 2002-08-05 18:00:15 by chorus
Hum, disassembly of DispatchMessageA/W from win2ksp2, it seems that there's quite a bunch more going on than just directly calling the wndproc of the hwnd in the msg structure. There's special handling for a bunch of WM_* messages, other code paths can end up in int 2Eh calls (NT syscall).

There's a whole bunch of code, and I don't feel like tracing much around in it. The code is there, and most likely it has a purpose ;). You might break stuff calling the wndproc directly (no, I can't think of any immediate reason why this could cause a break, but I advise caution). Since you're probably not going to get any substantial speedups from manual calling, why do it? DispatchMessage is short :).

Do optimizations where they matter, and write solid programs...
Posted on 2002-08-05 18:30:10 by f0dder
There probably is no problem if you call one of your own window routines, but CALL EAX definitely would not work if you got the WndProc address of a window you didn't create (e.g., an IE window).
Posted on 2002-08-05 18:38:23 by tenkey
Why F0dder? Just 'cause.
As for writing "solid programs" -- that's what the thread was about. Whether or not it's a solid way of doing things. If it isn't then I won't bother. If it *is* though, and it's faster, then why not?

If you're telling me it's a bad idea, I'll accept that and do things the "normal" way. But there's no harm in trying.

As for performance, I was checking things out with QueryPerformanceCounter (my AMD crashes with RDTSC) and it seemed to give favourable results (bout 3x faster). But I had to lose the GetWindowLong and use a table of WndProcs instead. For some reason, GetWindowLong takes more time than DispatchMessage. I figured different.

BTW, thanks for digging into DipatchMessage ;)

tenkey, as for other windows... I think instead of call eax you could do invoke CallWindowProc,eax,etc. this would work. But I guess it's kinda moot. :)

--Chorus
Posted on 2002-08-05 19:08:20 by chorus

If you're telling me it's a bad idea, I'll accept that and do things the "normal" way. But there's no harm in trying.

Well, dunno if it's "a bad idea". I can't see any logic reasons why it would be bad... I don't think you'd get messages from other processes, so even if you got messages from windows you didn't register, the code should be mapped in your process, and the wndproc should be valid. However, this is an assumption, and sometimes (often) code breaks when you're making assumptions.

You're saying you get a 3x speedup when using some lookup tables... this sounds like a lot - but can you *feel* this speedup? Does the user interface seem more responsive? That sorta stuff. Of course this would be hard to measure on todays fast processors...

And well, there's already "some" code in DispatchMessage. I think you could break "some stuff" by not using it (this might not be very obvious things though, and might only happen if you're using more advanced features). But who knows what will be happening in windows in the future?

Weird that the GetWindowLong approach takes longer than DispatchMessage... DispatchMessage has "some" code, and has to get the wndproc from somewhere :P

Btw, messing around is fun - I not discouraging people to do this, and I always find it interesting to read threads like this. What I discourage, however, is the use of methods that *might* be flaky, or end up flaky in a later windows version, especially if not much gain comes from it.

Hrm, RDTSC failing on your athlon? That's odd. That instruction has been implemented already in th k6-2, and probably even before that. Works like a charm on my box in ring3... never seen a windows version that doesn't enable usermode rdtsc in <whatever control register>.
Posted on 2002-08-05 20:15:29 by f0dder
Can I feel the responsiveness? Unfortunately no. See the timing below. Can't say that I do :)

I'm only measuring from just before the call to DispatchMessage (or the replacement method) to the beginning of my window procedure. I have to use QueryPerformanceCounter cause as I mentioned above, my system *really* dislikes rdtsc (ex. Explorer shut down. Completely. Desktop gone. Ctrl+Alt+Delete gone. QEditor stayed open though :) )

When using DispatchMessage the elapsed time is 15-20. When using my method, the elapsed time is 3-6, but hovers around 5. There's my 3x figure. It's still not that much though. If I use GetWindowLong it comes in around 25-35.

What I'm sorta interested in is what else DispatchMessage does. I would assume it would test the hwnd to make sure it's a valid window. Then it should just pass off the parameters to the appropriate function. No?

PS. My CPU is not an Athlon. It's AMD -- but it's a K6 :) Yeah... 400 MHz OF PURE POWER!!! :D

--Chorus
Posted on 2002-08-05 22:39:14 by chorus
Chorus,

If for instance you handle WM_ERASEBKGND in your WinProc you must return eax=1 or eax=0 to the system.

In your method you return eax=1 or eax=0 to the next procedure probably GetMessage or DefWindowProc and they don't care about eax.

So, you can't handle all the messages.
Posted on 2002-08-06 18:39:30 by lingo12
I wonder why I didn't think of that...
:stupid:
Posted on 2002-08-06 18:57:31 by f0dder
Point taken, however, I didn't notice any problems while doing this *shrugs*.

Interestingly enough, while I was looking for something completely different I found this:
http://www.microsoft.com/msj/defaultframe.asp?page=/msj/0397/hood/hood0397.htm&nav=/msj/0397/newnav.htm

which contains a complete description of the DispatchMessage proc as well as some other interesting info (like where the window structures are stored... it seems DispatchMessage doesn't need to call GetWindowLong -- it can read the data directly)

--Chorus
Posted on 2002-08-06 19:27:44 by chorus
Chorus,
Unfortunately I didn't find answer about my example in the article. May be:


Figure 3 - Ring 3 DispatchMessage Pseudo code
.............
else // WM_PAINT, or something else...
{
if ( fAnsi )
_RtlMBMessageWParamCharToWCS( lpmsg->message, save_wParam );

_NtUserDispatchMessage( lpmsg );

}

"To summarize, DispatchMessageWorker handles the simple messages
that don't need much system knowledge and that can be dispatched with
a minimum of fuss. Anything more complicated gets shuttled off to the
NtUserDispatchMessage function in WIN32K.SYS at ring 0. While writing this
column, I did look into the NtUserDispatchMessage function and found it
rather tricky and complicated, so I won't attempt to describe it in this
limited space."
Posted on 2002-08-06 20:47:27 by lingo12
Maybe NT is more sensitive about this stuff. I'm running on 9x here and haven't tried it on NT.

Also, for stuff like WM_ERASEBKGND: while it is true that the return value is processed, I believe it'll mostly go unnoticed. Ex. the return value for WM_ERASEBKGND only sets fErase in PAINTSTRUCT when BeginPaint is called. I for one have never used this parameter while processing WM_PAINT, and hence I haven't noticed this "bug". I'm sure there are other examples however where this is more important (in particular, I think this would mess up Dialog Boxes)

--Chorus
Posted on 2002-08-06 22:18:59 by chorus
In typical use, DispatchMessage is used only for posted messages (via PostMessage or PostThreadMessage). The sender of such messages doesn't wait for a reply.

Messages which are sent (via SendMessageXXXX) are sent to the window proc directly by GetMessage or PeekMessage (or even SendMessage itself, if in the same thread). The sender of such messages waits for a return value.
Posted on 2002-08-07 15:41:50 by tenkey
Are you sure SendMessage doesn't go through GetMessage/TranslateMessage/DispatchMessage?
Posted on 2002-08-07 19:32:31 by f0dder
From SendMessage on MSDN:


If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine


It also suggests that if you want a message to go to the message queue of a window, use PostMessage.

--Chorus
Posted on 2002-08-07 20:32:50 by chorus
If you can find Pietrek's books on Windows internals, you'll see that SendMessage will either call the window proc directly, or put it on a special SendMessage queue. Messages in the SendMessage queue are handled with higher priority, and there must be a reply for each message. The messages in the SendMessage queue are handled by GetMessage/PeekMessage.
Posted on 2002-08-10 20:01:10 by tenkey