views:

818

answers:

3

Please could someone give me a few tips for creating function pointers for MS winapi functions? I'm trying to create a pointer for DefWindowProc (DefWindowProcA/DefWindowProcW) but getting this error:

LRESULT (*dwp)(HWND, UINT, WPARAM, LPARAM) = &DefWindowProc;

error C2440: 'initializing' : cannot convert from 
'LRESULT (__stdcall *)(HWND,UINT,WPARAM,LPARAM)' 
to 'LRESULT (__cdecl *)(HWND,UINT,WPARAM,LPARAM)'

I can't figure out what I need to use because I am not used to the MS ascii/wide macros. By the way, I'm creating a function pointer to make a quick hack, and unfortunately I don't have time to explain why - but regardless, I think this question will be helpful to people who need to create winapi function pointers.

Update:

This code works, but I'm worried that it is bad practice (and does not adhere to unicode/ascii compile options). Should I define two specifications?

LRESULT (__stdcall* dwp)(HWND, UINT, WPARAM, LPARAM) = &DefWindowProc;

Update 2:

This is nicer (thanks to nobugz):

WNDPROC dwp = DefWindowProc;
+1  A: 

You lack __stdcall in your prototype. You need to have a matching calling convention apart from a matching prototype. WINAPI functions are all __stdcall, while the default for C++ is __cdecl.

Using extern "C" { code } is a viable alternative.

Kornel Kisielewicz
I always thought that extern "C" and __stdcall were two different things. extern "C" being needed to force the C ABI (as opposed to the name mangled c++ ABI. __stdcall is to enforce the normal windows calling convention where the called function is responsible for cleaning up the stack as opposed to the __cdecl C calling convention where the calling function cleans up the stack.
doron
You're correct, Deus Ex Machina. Calling convention has nothing whatsoever to do with linkage, which is what "extern C" controls. "Extern C" affects name mangling, and so does the calling convention, but we're not interested in name mangling here since neither DefWindowProc nor dwp is being exported or imported in any of the code shown.
Rob Kennedy
What's worse is that stdcall and cdecl are sufficiently compatible that if you use extern "C" without matching conventions, the program will link, and the call will work perfectly -- but on return from the call, you've got corrupted local variables (and if the call was the last thing in the method, you wouldn't even notice, since the return will usually work properly). A big trap waiting for the unwary.(And yes, I've had to debug code afflicted with this problem.)
Miral
Also, if you let the compiler optimise away the standard stack framing code, the return can crash your app. It's debatable whether this is better or worse -- at least it's easier to find. :)
Miral
A: 

This isn't the 'crazy' MS ascii/wide macro, you've just got 2 different function declarations.

If you decorate your internal function with "extern C" around it, you'll expose your function with the same calling type as the original and it'll compile.

gbjbaanb
Compile, yes. Work, not exactly. See my comment on Kornel's answer.
Miral
+11  A: 

Fix the calling convention mismatch like this:

LRESULT (__stdcall * dwp)(HWND, UINT, WPARAM, LPARAM) = DefWindowProc;

A typedef can make this more readable:

typedef LRESULT (__stdcall * WindowProcedure)(HWND, UINT, WPARAM, LPARAM);
...
WindowProcedure dwp = DefWindowProc;

But, <windows.h> already has a typedef for this, you might as well use it:

WNDPROC dwp = DefWindowProc;
Hans Passant
Best answer I think; I will use `WNDPROC dwp = DefWindowProc;`
nbolton
Probably best to use "CALLBACK" rather than "__stdcall", if needing to write the types out by hand, since this is what the headers use...
brone