【问题标题】:C - properly importing stdcall functions from unmanaged DLLC - 从非托管 DLL 正确导入 stdcall 函数
【发布时间】:2014-10-08 15:11:58
【问题描述】:

我正在尝试通过创建一个指定我需要使用的函数的 .def 文件来将非托管 DLL 中的函数导入 C 项目。我正在练习来自user32.dll 的WinAPI 函数MessageBoxA。它是一个 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 函数?

【问题讨论】:

    标签: c winapi dll dllexport stdcall


    【解决方案1】:

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

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

    LIBRARY USER32.dll
    EXPORTS
      MessageBoxA
    

    如果我不指出正确的主要声明是,那也是我的疏忽

    int main(void)
    

    【讨论】:

    • @vaxquis 需要避免不必要的名称装饰
    • 我已经重读了几次 OPs 问题......我仍然认为这个问题很糟糕,我仍然认为这里不需要 DEFs - OP 可以只使用MinGW 的直接 DLL 链接根本不用担心自己的 DEF,或者使用__declspec(dllimport)(或任何替代品),它允许在没有 DEF 文件的情况下导入外部。然而我注意到他使用了 M$ 工具,所以我撤回了我关于 DEF 不是完全需要的声明,因为它有点太强了 - DEF 可以省略,我认为 OP 使用了错误的方法,但如果他坚持 使用它们,你的答案是 100% OK。
    【解决方案2】:

    我仍然不完全确定为什么,但是删除_ 将序号添加到我的.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);
    

    【讨论】:

    • 这是正确答案,但我只能在 2 天内接受。同时,有人知道为什么声明序数如此重要吗?
    • 真的是正确答案吗?如果你不明白,也许不是。
    • @DavidHeffernan:这是正确的答案,因为它有效。为什么它有效是一个完全不同的问题;)
    • @Mints97 之所以有效是因为using DEF changes the mangling scheme
    【解决方案3】:

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

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

    【讨论】:

    • DEF 文件:LIBRARY user32.dll EXPORTS _MessageBoxA@16 并如您所说更改了项目中的行。现在它说找不到__imp__MessageBoxA@16
    • 您也可以尝试将该符号添加到 DEF 文件的 EXPORTS 部分
    • def 文件:LIBRARY user32.dll EXPORTS _MessageBoxA@16 __imp__MessageBoxA@16 完全相同的错误
    • 嗯。我见过一些使用MessageBoxA@16 的汇编程序,即没有前导下划线。不幸的是,我无法使用 Windows 机器 ATM,所以我无法测试任何东西。
    • 也不要使用dllimport,他没有声明函数指针。只需将其完全删除。
    【解决方案4】:

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

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

    【讨论】:

    • WINAPI 只是 stdcall: #define WINAPI __stdcallWINUSERAPIDECLSPEC_IMPORT,而 __declspec(dllimport)。因此,def 中函数名称之前的内容基本上是__declspec(dllimport) int __stdcall。我已将 dllexport 更改为 dllimport,但它仍然无法正常工作(请参阅其他答案)。但是,我不确定变量中的 _In_opt__In_ 是否会导致任何问题...
    • @Mints97 _In_opt__In_ 是空宏(它们用于SAL annotations
    • +0 - 虽然你完全正确,Shao,但这本身并不是一个答案,因为它没有直接解决问题。这应该是对 OP IMO 的评论。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-10-14
    • 1970-01-01
    • 1970-01-01
    • 2015-05-28
    • 1970-01-01
    相关资源
    最近更新 更多