Date: | January 9, 2004 / year-entry #11 |
Tags: | history |
Orig Link: | https://blogs.msdn.microsoft.com/oldnewthing/20040109-00/?p=41133 |
Comments: | 17 |
Summary: | As we learned yesterday, nonstatic member functions take a secret "this" parameter, which makes them incompatible with the function signature required by Win32 callbacks. Fortunately, nearly all callbacks provide some way of providing context. You can shove the "this" pointer into the context so you can reconstruct the source object. Here's an example: class SomeClass... |
As we learned yesterday, nonstatic member functions take a secret "this" parameter, which makes them incompatible with the function signature required by Win32 callbacks. Fortunately, nearly all callbacks provide some way of providing context. You can shove the "this" pointer into the context so you can reconstruct the source object. Here's an example:
class SomeClass { ... static DWORD CALLBACK s_ThreadProc(LPVOID lpParameter) { return ((SomeClass*)lpParameter)->ThreadProc(); } DWORD ThreadProc() { ... fun stuff ... } };
Some callback function signatures place the context parameter
(also known as "reference data") as the first parameter. How
convenient, for the secret "this" parameter is also the first
parameter. Looking at
the various calling conventions available to us,
it sure
looks like the
Well, "thiscall" doesn't match, but the two "__stdcall"s do.
Fortunately the compiler is smart enough to recognize this and
can optimize the
class SomeClass { ... static DWORD CALLBACK s_ThreadProc(LPVOID lpParameter) { return ((SomeClass*)lpParameter)->ThreadProc(); } DWORD __stdcall ThreadProc() { ... fun stuff ... } };
If you look at the code generation for the
?s_ThreadProc@SomeClass@@SGKPAX@Z PROC NEAR jmp ?ThreadProc@SomeClass@@QAGKXZ ?s_ThreadProc@SomeClass@@SGKPAX@Z ENDP
Now some people would take this one step further and just
cast the second parameter to
Although we took advantage above of a coincidence between the two
|
Comments (17)
Comments are closed. |
I would smack any fool around that tried to depend on how the assembly works. Just BEGGING for trouble.
The good thing is, there are now very, very few programmers who understand assembly to this level, and they are all old enough to know better. Or one would hope.
Grin.
Now, passing a non-static member function in for a callback, I’ve seen fools try that. But it never makes it out of programmer testing, because nothing works…..
(Duh, why am I getting a visit from the Doctor?)
Course, passing objects in WM_ type stuff can cause other problems with deleted objects. Those can get amusing to prevent.
What if there is already a message in the queue with that object reference as an LPARAM when you clean things up in the destructor? Can you say trap on shutdown?
You really need to have some form of validation for the "this" pointer.
Hello Raymon,
You shouldn’t disregard our precious software in public.
Bill.
Stupid and illiterate too!
Bill,
You seem to have a real problem with names, both RaymonDs and your own -> Willaim != William.
Thanks for playing,
Steve
Woah, wtf? A new BOOLEAN type? What happened to BOOL?
Most of the callbacks seems to allow user-data, but not the Service-API, which is too bad because when you put several services in one exe, it would be nice to have a class for each.
Do you know why the Service-API lacks this (no pun intended), is there a good reason or was it just "missed"?
BOOLEAN, like UNICODE_STRING, dates back to the days when Windows NT was called "OS/2 3.0 NT".
Andreas: I don’t know what the deal is with the service APIs. You can always fake it be instancing your callbacks. There are some other callbacks which frustratingly fail to pass reference data, like AbortProc, all the Windows hooks…
Andreas: Service callbacks take user-data. Put your this pointer in the lpContext.
DWORD WINAPI HandlerEx(
DWORD dwControl,
DWORD dwEventType,
LPVOID lpEventData,
LPVOID lpContext
);
What if SomeClass was derived from SomeParentClass and ThreadProc was a virtual member function? Would the compiler still do the stack optimization?
Try it and see! (and see if you can explain the codegen)
I’m wondering what’s happened to this in Win64.
Have all user data parameters been widened to 64 bit?
I mentioned this briefly in my old blog
http://blogs.gotdotnet.com/raymondc/permalink.aspx/d29da767-5c07-48ec-9b9c-5f514b7d1f6b
There’s more information on Win64 data types on MSDN, like here for example.
http://msdn.microsoft.com/library/en-us/dnnetserv/html/ws03-64-bitwindevover.asp
return ((SomeClass*)lpParameter)->ThreadProc();
really should be:
return reinterpret_cast<SomeClass*>(lpParameter)->ThreadProc();
Using new-style casts makes the code safer. Also it is easier to find casts in the source using a simple search.
See also B. Stroustrup, The C++ Programming Language 3rd edition, pg 819. No, C-style casts are not deprecated – yet…
??????,??????????? ?????????? calling convention: The history of calling conventions, part 1 The history of calling conventions, part 2 The history of calling conventions, part 3 The history of calling conventions, part 4: ia64 Why do member functions need to be…
Isn’t this passed in ebx?
Try it and see. My remarks applied to the Microsoft Visual C++ compiler – other compilers will probably do things differently.
Thereby ensuring that you match the calling convention.
Commenting closes after two weeks.
http://weblogs.asp.net/oldnewthing/archive/2004/02/21/77681.aspx