【问题标题】:Why pragma comment(linker,"/export ...") unresolved external symbol为什么 pragma comment(linker,"/export ...") 无法解析外部符号
【发布时间】:2022-06-11 17:07:07
【问题描述】:

头文件如下所示

#define CoverWinAPI extern "C" __declspec(dllexport)


CoverWinAPI  BOOL RunDll();
CoverWinAPI void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName,PROC pfnCurrent,PROC pfnNew,HMODULE hmodCaller);
#pragma comment(linker,"/export:MyCreateWindowExW=_MyCreateWindowExW@48")
CoverWinAPI  HWND WINAPI  MyCreateWindowExW(
        _In_opt_ DWORD     dwExStyle,
        _In_opt_ LPCWSTR lpClassName,
        _In_opt_ LPCWSTR lpWindowName,
        _In_ DWORD dwStyle,
        _In_ int X,
        _In_ int Y,
        _In_ int nWidth,
        _In_ int nHeight,
        _In_opt_ HWND hWndParent,
        _In_opt_ HMENU hMenu,
        _In_opt_ HINSTANCE hInstance,
        _In_opt_ LPVOID lpParam);

当我不使用#pragma 注释时,导出函数名称为_MyCreateWindowExW@48。
但是当我使用 #pragma comment(linker,"/export:MyCreateWindowExW=_MyCreateWindowExW@48") 时,MSVC 编译失败并显示 unresolved external symbol _MyCreateWindowExW@48。是什么导致了这个错误?

【问题讨论】:

  • 你(也)在MyCreateWindowExW函数的实际定义之前有那个#pragma comment()指令吗?不知道为什么会有所不同,但M/S docs 中给出的示例是(明显?)在BOOL CALLBACK PlainFuncName( Things * lpParams) 之后缺少分号。
  • @AdrianMole 我尝试将相同的代码添加到 cpp ,但不起作用。
  • 构建此 DLL 的 x64 风格将失败并出现此链接器错误,它不会使用 stdcall 装饰。用#ifndef _WIN64 包裹#pragma 以避免。
  • @HansPassant 我认为我使用 x32 编译并尝试在我的代码中运行 #ifndef _WIN64 std::cout<<"Hello 32"<<std::endl; #endif。它实际上输出“Hello 32”。
  • WINAPI 是否扩展为 __stdcall

标签: c++ c winapi visual-c++


【解决方案1】:

您遇到的问题是由于编译器修饰函数名称的方式。 _MyCreateWindowExW@48 是一个 x86 风格的修饰名称,仅适用于 x86 构建,它的 x64 对应物只是 MyCreateWindowExW(x64 只有一个调用约定,类似于 __cdecl,调用者是一个负责管理堆栈分配)。

与官方文档相反,export #pragma/EXPORT 链接器选项都不能工作未修饰的名称,它们都希望名称被完全修饰。这是mentioned here

小记

从Dll导出有几种方式,如:

  • __declspec(dllexport)
  • #pragma comment(linker,"/export:...")link.exe /EXPORT
  • DEF 文件

通常只使用其中一个就足够了,因此在您的示例中 __declspec(dllexport) 是多余的,考虑到它导出一个装饰名称,这不是您想要的,更是如此。

解决方案 #1(IMO 很麻烦)

使用#ifdef 为每个平台提供适当的修饰名称:

#define CoverWinAPI extern "C"

CoverWinAPI  BOOL RunDll();
CoverWinAPI void ReplaceIATEntryInOneMod(PCSTR pszCalleeModName, PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller);

#ifndef _WIN64
#pragma comment(linker,"/export:MyCreateWindowExW=_MyCreateWindowExW@48")
#else
#pragma comment(linker,"/export:MyCreateWindowExW")
#endif

CoverWinAPI  HWND WINAPI  MyCreateWindowExW(...);

解决方案 #2(建议)

将 DEF 文件添加到您的项目中:

LIBRARY   CoverWinAPI
EXPORTS
    MyCreateWindowExW

那么你的声明可以这样重写:

#define CoverWinAPI WINAPI

BOOL CoverWinAPI RunDll();
void CoverWinAPI ReplaceIATEntryInOneMod(PCSTR pszCalleeModName, PROC pfnCurrent, PROC pfnNew, HMODULE hmodCaller);

HWND CoverWinAPI  MyCreateWindowExW(...);

这很有效,因为显然链接器确实尝试将 DEF 文件条目与修饰和未修饰的名称相匹配。

【讨论】:

    猜你喜欢
    • 2014-06-14
    • 1970-01-01
    • 2018-07-01
    • 2018-01-16
    • 2018-02-21
    • 1970-01-01
    • 2021-08-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多