Search code examples
cwinapidlldllexportstdcall

C - properly importing stdcall functions from unmanaged DLL


I am trying to import a function from an unmanaged DLL into a C project by creating a .def file specifying the function I need to use. I am practicing on the WinAPI function MessageBoxA from user32.dll. It is an stdcall function, like the other WinAPI functions. Here's how I create my .def file:

LIBRARY user32.dll
EXPORTS
_MessageBoxA@16

Then I create a .lib from it like this: lib /def:"C:\Path\to\def\user32.def" / out:"C:\path\to\project\user32-mb.lib" which successfully creates user32-mb.lib and user32-mb.exp. Then, in my C project, I do the following:

#pragma comment(lib, "user32-mb.lib")

#ifdef __cplusplus
#define EXTERNC extern "C"
#else
#define EXTERNC
#endif

EXTERNC __declspec(dllexport) int __stdcall MessageBoxA(void *hWnd, char *lpText, char *lpCaption, int uType);

void main(){
    MessageBoxA(0, "MessageBox test", "MessageBox test", 0x00000030L);
}

However, upon linking, it gives the following error:

error LNK2019: unresolved external symbol _MessageBoxA@16 referenced in function _main

However, when I change the declaration in the .def to this:

LIBRARY user32.dll
EXPORTS
MessageBoxA

And change the function prototype in my C code to cdecl instead of stdcall:

EXTERNC __declspec(dllexport) int __cdecl MessageBoxA(void *hWnd, char *lpText, char *lpCaption, int uType); The message box actually appears, but right on closing, it throws an error:

Run-Time Check Failure #0 - The value of ESP was not properly saved across a function call. This is usually a result of calling a function declared with one calling convention with a function pointer declared with a different calling convention. Which indicates that calling it with cdecl is also a bad idea since it requires stdcall after all.

The question is, what should I change in the .def file or in my project to avoid both errors and to import and call an stdcall function properly?


Solution

  • I am still not entirely sure why, but removing the _ adding the ordinal to the function name my .def file fixed everything. My solution is:

    LIBRARY USER32.dll
    EXPORTS
    MessageBoxA@16 @2093
    

    Function definition:

    #ifdef __cplusplus
    #define EXTERNC extern "C"
    #else
    #define EXTERNC
    #endif
    
    typedef void *PVOID;
    typedef PVOID HANDLE;
    typedef HANDLE HWND;
    typedef const char *LPCSTR;
    typedef unsigned int UINT;
    
    EXTERNC __declspec(dllimport)
    int
    __stdcall
    MessageBoxA(
        HWND hWnd,
        LPCSTR lpText,
        LPCSTR lpCaption,
        UINT uType);