.def文件C / C ++ DLL

时间:2008-12-14 06:31:53

标签: c++ c dll

我不明白使用带有DLL的.def文件。

它似乎取代了在您的DLL代码中使用显式导出的需要(即显式__declspec(dllexport))但是我不能在不使用这些时生成lib文件,然后在使用DLL时会创建链接器问题。

那么在与客户端应用程序链接时如何使用.def,它们是否取代了使用头文件或.lib文件的需要?

6 个答案:

答案 0 :(得分:24)

我发现同时使用__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”将确保上述客户端代码适用于各种编译器和编译器设置。

答案 1 :(得分:20)

我的理解是.def文件提供了__declspec(dllexport)语法的替代方法,还有一个额外的好处是能够显式指定导出函数的序数。如果仅通过序数导出某些函数,这可能很有用,它不会显示有关函数本身的大量信息(例如:许多OS内部DLL的导出函数仅按顺序)。

请参阅reference page

请注意,.def文件中的名称必须与二进制文件中的名称匹配。因此,如果您使用带有'extern“C”{...}'的C或C ++,名称将不会被破坏;否则,您必须为用于生成DLL的特定编译器版本使用正确的受损名称。 __declspec()函数全部自动完成。

答案 2 :(得分:8)

对于那些感兴趣的人......要能够链接到dll和def文件,你还需要一个lib文件。在Windows中,这可以使用'LIB'工具从def进行。请参阅下面的命令行方式示例。

lib /machine:i386 /def:sqlite3.def

希望这有助于其他人。

答案 3 :(得分:4)

.DEF文件在16位窗口中更常见,它们通常是指定应导出哪些符号的唯一方法。

此外,它们提供了一种通过序数值(@ 1,@ 2等)而不是名称来指定出口的方法。当性能非常重要时,例如在视频驱动程序中,使用这种查找符号的方法。

答案 4 :(得分:3)

我没有使用过很多DLL,但我的理解是,对于导出的C ++函数,你应该使用“__declspec(dllexport)”,对于导出的C函数,你应该编写.def文件。这可能是因为C ++函数支持重载,但C函数不支持。

答案 5 :(得分:3)

我的理解是.def文件实际上并没有指定需要导出哪个api。它只包含导出的api及其序数。如果要实际导出特定的api,则需要在声明的api和__declspec(dllimport)的定义中指定__declspec(dllexport)。

def文件的优点在于,它可以帮助您保持与已经实现的dll的后备兼容性。即它保持apis的序数。假设您在dll中添加了一个新api,然后链接器会查看您的.def文件,该文件会生成ne wapi的序号,以便旧api的序数完好无损。

因此,如果客户端代码使用最新的dll,则不会破坏现有的apis。