【发布时间】:2010-09-26 20:16:11
【问题描述】:
我不明白将 .def 文件与 DLL 一起使用的意义。
似乎它取代了在您的 DLL 代码中使用显式导出的需要(即显式 __declspec(dllexport))但是当我不使用这些时我无法生成 lib 文件,然后在使用 DLL 时会产生链接器问题.
那么在与客户端应用程序链接时如何使用 .defs,它们是否取代了使用头文件或 .lib 文件的需要?
【问题讨论】:
我不明白将 .def 文件与 DLL 一起使用的意义。
似乎它取代了在您的 DLL 代码中使用显式导出的需要(即显式 __declspec(dllexport))但是当我不使用这些时我无法生成 lib 文件,然后在使用 DLL 时会产生链接器问题.
那么在与客户端应用程序链接时如何使用 .defs,它们是否取代了使用头文件或 .lib 文件的需要?
【问题讨论】:
我发现同时使用 __declspec(dllexport) 和 .def 文件有助于创建可移植 DLL,即可以从使用不同编译器或不同编译器设置编译的代码调用的 DLL。
只需将 __declspec(dllexport) 放在您的函数声明中,就会导致这些函数被您的 DLL(至少在 Windows 上)“导出”,以便可以从 DLL 外部调用它们。
但是,将列出所有导出函数的 .def 文件添加到构建中可以让您阻止 Microsoft 编译器(例如)将前导下划线和尾随参数宽度信息添加到导出函数名称(至少在组合时)使用 __stdcall 指令,对于可移植性也很有用)。例如。函数声明
void foo(int i);
如果您不注意调用约定和 .def 文件的使用,最终可能会导出为“_foo@4”。
在将 GetProcAddress() 调用作为在运行时显式加载和挂钩到 DLL 的一部分时,保持符号表中导出的函数名称不受此类名称修饰非常方便。即在运行时获取指向上述函数 foo() 的指针(假设它已被导出),理想情况下您只想调用:
HANDLE dllHandle = LoadLibrary("mydll.dll");
void* fooFcnPtr = GetProcAddress(dllHandle, "foo");
当然还有一些适当的错误案例检查!
在构建 DLL 时在函数声明中使用 .def 文件以及 __stdcall、__declspec(dllexport) 和 extern "C" 将确保上述客户端代码适用于各种编译器和编译器设置。
【讨论】:
extern "C"。
extern "C" 不会完全阻止名称重整,它只会阻止 C++ 名称重整,留下 C 重整。例如,MSVC 32 位编译器为 stdcall 和 fastcall 函数添加所有这些 @ 装饰,即使使用 extern "C"。
#pragma comment(linker, ...),例如如here 所述
我的理解是 .def 文件提供了 __declspec(dllexport) 语法的替代方案,另外还有一个好处是能够显式指定导出函数的序号。如果您仅按序号导出某些函数,这可能会很有用,这不会透露有关函数本身的太多信息(例如:许多操作系统内部 DLL 的导出函数仅按序号)。
请参阅reference page。
请注意,.def 文件中的名称必须与二进制文件中的名称匹配。因此,如果您将 C 或 C++ 与 'extern "C" { ... }' 一起使用,则名称不会被破坏;否则,您必须为用于生成 DLL 的特定版本的编译器使用正确的名称。 __declspec() 函数会自动完成这一切。
【讨论】:
对于那些仍然感兴趣的人...为了能够链接到 dll 和 def 文件,您还需要一个 lib 文件。在 Windows 中,这可以使用“LIB”工具从 def 中创建。有关执行此操作的命令行方式的示例,请参见下文。
lib /machine:i386 /def:sqlite3.def
希望这对其他人有所帮助。
【讨论】:
lib /DEF:sqlite3.def /MACHINE:X86 /SUBSYSTEM:WINDOWS /LTCG /NOLOGO。生成的 .lib 可以很容易地用于动态链接 sqlite3.dll。
.DEF 文件在 16 位窗口中更为常见,它们通常是指定应导出哪些符号的唯一方法。
此外,它们提供了一种按序数值(@1、@2 等)而不是名称指定导出的方法。当性能非常重要时使用这种查找符号的方法,例如在视频驱动程序中。
【讨论】:
我没有过多地使用 DLL,但我的理解是,对于导出的 C++ 函数,您应该使用“__declspec(dllexport)”,而对于导出的 C 函数,您应该编写 .def 文件。这可能是因为 C++ 函数支持重载,而 C 函数不支持。
【讨论】:
我的理解是.def文件实际上并没有指定所有api需要导出。它只包含导出的 api 及其序号。如果要实际导出特定的 api,则需要在 api 的定义中指定 __declspec(dllexport) 并在声明中指定 __declspec(dllimport)。
def 文件的优点是,它可以帮助您保持与已经实现的 dll 的反向兼容性。即它维护 apis 的序数。假设您在 dll 中添加了一个新的 api,然后链接器查看您的 .def 文件为新 wapi 生成序号,以便旧 api 的序号保持不变。
所以,如果客户端代码使用最新的dll,它不会破坏现有的api。
【讨论】:
__declspec(dllexport),而只有一个用于导出的.def 文件。所以我认为你的第一段是不正确的。