C - 从非托管DLL正确导入stdcall函数

时间:2014-08-14 20:28:00

标签: c winapi dll dllexport stdcall

我试图通过创建指定我需要使用的函数的.def文件,将函数从非托管DLL导入到C项目中。我正在MessageBoxA的WinAPI函数user32.dll上练习。它是一个stdcall函数,就像其他WinAPI函数一样。 以下是我创建.def文件的方法:

LIBRARY user32.dll
EXPORTS
_MessageBoxA@16

然后我从中创建一个.lib:lib /def:"C:\Path\to\def\user32.def" / out:"C:\path\to\project\user32-mb.lib"成功创建user32-mb.libuser32-mb.exp。然后,在我的C项目中,我执行以下操作:

#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);
}

但是,在链接时,会出现以下错误:

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

但是,当我将.def中的声明更改为:

LIBRARY user32.dll
EXPORTS
MessageBoxA

将我的C代码中的函数原型更改为cdecl而不是stdcall

EXTERNC __declspec(dllexport) int __cdecl MessageBoxA(void *hWnd, char *lpText, char *lpCaption, int uType); 消息框实际出现,但在关闭时,它会抛出一个错误:

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. 这表示使用cdecl调用它也是一个坏主意,因为它毕竟需要stdcall

问题是,我应该在.def文件或项目中更改哪些内容以避免这两个错误并导入并正确调用stdcall函数?

4 个答案:

答案 0 :(得分:1)

您需要将__declspec(dllexport)更改为__declspec(dllimport),因为您从DLL导入函数,而不是导出

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

答案 1 :(得分:1)

您需要使用dllimport而不是dllexport,但在这种情况下,您应该完全删除__declspec(...)

您需要为函数MessageBoxA指定正确的名称。

LIBRARY USER32.dll
EXPORTS
  MessageBoxA

如果不指出正确的主要声明是

,那将是我的疏忽
int main(void)

答案 2 :(得分:0)

我仍然不完全确定原因,但删除_将序号添加到函数名称my .def文件修复了所有内容。我的解决方案是:

LIBRARY USER32.dll
EXPORTS
MessageBoxA@16 @2093

功能定义:

#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);

答案 3 :(得分:-1)

This页面表明winuser.h是标题。从那里,您可以看到使用了一些宏,包括WINUSERAPIWINAPIWINUSERAPI条件#define - d在该标头的开头。可以在WINAPI标题中找到winbase.h,根据平台,可以看到它与调用约定相关联。

但更好的问题是:您为什么使用dllexport而不是dllimport