2008-12-14 102 views
18

我不理解使用DLL的.def文件的要点。.def文件C/C++ DLL

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

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

回答

17

我的理解是,.def文件提供了__declspec(dllexport)语法的替代方法,并且可以显式指定导出函数的序号。如果仅通过序号(ordinal)导出某些函数,该函数不会显示有关函数本身的很多信息(例如:许多操作系统内部DLL的导出函数仅由序数),这会非常有用。

查看reference page

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

+0

正如M.克鲁尼说的:“别的什么?”。 +1。作为一名Windows开发人员,我的经验是DEF文件是“旧的方式”,应该被弃用(如果尚未)。 – paercebal 2008-12-14 15:20:22

+0

是的.def文件早于__declspec语言扩展。在.def文件中,您无法使用__declspec或#pragmas做任何事情。这包括花哨的链接器技巧,如重命名或转发API。 – 2008-12-14 20:22:06

3

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

+1

是的。重载能力=在.def文件中烦人指定的装饰名称。 C和C++在这里没有根本的区别,但是人机工程学和文化差异很大。 – 2012-07-04 12:21:00

3

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

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

3

我的理解是,.def文件实际上并不指定哪个apis需要导出。它只包含导出的apis和它们的序号。如果你想实际导出一个特定的api,你需要在声明的api和__declspec(dllimport)的定义中指定__declspec(dllexport)。

def文件的优点是,它可以帮助您保持与已经存在的dll的后台字符兼容性。即它维护apis的序数。假设你在dll中添加了一个新的api,那么链接器会查看你的.def文件生成ne wapi的序号,以便旧apis的序号完好无损。

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

7

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

lib /machine:i386 /def:sqlite3.def 

希望这可以帮助别人。

+0

+1。那正是我要找的 – fmuecke 2012-04-17 12:49:55

22

我发现将__declspec(dllexport)和.def文件一起用于创建可移植DLL,即可以从使用不同编译器编译或使用不同编译器设置编译的代码中调用的DLL。

只要在你的函数声明中加入__declspec(dllexport),就可以使你的DLL“至少在Windows”上“导出”这些函数,以便可以从DLL之外调用它们。

但是,向build中添加一个列出所有导出函数的.def文件后,您可以停止Microsoft编译器(例如)将导出的下划线和尾随参数宽度信息添加到导出的函数名称中(至少在何时结合__stdcall指令,对便携性也很有用)。例如。函数声明

void foo(int i); 

可能最终会被导出为“_foo @ 4”,如果你不小心调用约定和DEF文件使用。

将GetProcAddress()调用作为加载并在运行时显式连接到DLL的一部分时,在符号表中保留导出的函数名称时没有这种名称修饰会非常方便。即获得一个指向上述函数foo()在运行时(假设它是出口的话),你最好只是想呼吁:

HANDLE dllHandle = LoadLibrary("mydll.dll"); 
void* fooFcnPtr = GetProcAddress(dllHandle, "foo"); 

有了一些相应的错误的情况下,当然检查!

在构建DLL时,在函数声明中使用.def文件加上__stdcall,__declspec(dllexport)和extern“C”将确保上述客户端代码适用于各种编译器和编译器设置。