我对dll知之甚少。我正在读一本关于COM的书。作者指的是Exporting a function from a DLL
。他告诉我该怎么做,但他不知道它是什么或为什么要这样做?
他建议的方法是:
a)使用extern "C"
标记函数(不知道为什么?)
b)创建一个DEF文件并在此DEF文件的EXPORTS部分中添加函数名称。 再次不知道为什么以及究竟发生了什么?
我不理解的另一件事是术语symbols
/ symbol table
。
Exporting a function from a DLL
是什么意思?symbols
/ symbol table
?如果有人能用简单明了的方式解释我,我将感激不尽。细节条款。任何网站链接或教程也受到欢迎。
修改
我在.NET中使用过DLL。其中我只包括使用命名空间行并将dll添加到引用并且它们工作。这是我知道如何使用dll的唯一方法。我不知道如何在.net中使用dll与在COM中使用它们不同。任何人都可以与.NET联系吗?
答案 0 :(得分:8)
.NET中的DLL与本机DLL完全不同。 .NET DLL包含字节码,称为CIL,它有足够的信息供其他程序(如csc编译器)使用,以计算出包含在其中的类,类型,接口等。
这与本机DLL完全不同。原生DLL包含二进制指令&主要是非结构化数据,并且(通常)没有办法计算出数据意味着什么 - 例如,DLL中的某个地方可能是两个字节(十六进制)43 23
并且有无法判断程序是将这些字符解释为字符C#
还是整数17187
或内存地址,甚至是指向CPU的指令。
所以,继续你的问题:
symbol table
是DLL的一段元数据;它告诉编译器/链接器如何将void myDllFunc (int bar)
转换为DLL中的地址。它基本上是一个查找表。从DLL中导出函数就是如何告诉你希望在查找表中找到哪些函数 - 这些是其他代码能够调用的函数,因为它将能够找到他们。记住 - 没有其他信息,就无法告诉 myDllFunc
开始的地方。extern C
因为名称解析的过程,特别是C ++如何处理函数重载。当你有两个功能时:int square (int x);
double square (double x);
编译器需要某种方式来区分它们 - 名称“square”现在是不明确的,并且无法解析为单个代码地址。 C ++通过 name mangling 处理它 - 编译器获取函数的名称square
,然后添加一些与函数签名中的类型对应的魔术字符串。因此,例如,编译器可以将您的两个函数看作:
int int+square+int (int x);
double dbl+square+dbl (double x);
它们现在不再含糊不清(真正的编译器不使用这么简单的方案)。现在有两个问题:
为了便于互操作,人们通常用extern C
标记它们导出的函数,这使得编译器使用C的命名规则,其中函数的名称不会被破坏。
编辑以解决评论:
声明函数签名extern C
可以解决名称重整问题,因为C没有名称重整。因为声明两个函数
int square (int x);
double square (double x);
是错误;编译器/链接器不必 - 也不会 - 处理这种歧义。
Exporting a function from a DLL
只不过是将函数添加到符号表中。这使得DLL外部的代码可以调用该函数,因为现在外部代码可以查找函数启动的位置。因此,该功能被“导出”,其他人可以调用它。
答案 1 :(得分:4)
从DLL导出函数只是意味着DLL的使用者将能够调用此函数。
从概念上讲,它类似于将函数添加到DLL公开的“接口”。
符号表是一个包含这些导出函数的表,其名称,ID和地址。
试试这个:http://msdn.microsoft.com/en-us/library/z4zxe9k8(VS.80).aspx
答案 2 :(得分:4)
正确声明的导出函数如下所示:
extern "C" __declspec(dllexport)
void __stdcall Foo() {
// etc...
}
声明者按顺序执行此操作:
extern "C"
抑制C ++名称修饰。在32位机器上,导出的名称将是_Foo @ 0,P / Invoke编组程序可以轻松找到这样的名称。没有它,导出的名称将是?Foo @@ YGXXZ。 marshaller找不到这样的名称,你必须使用[DllImport]属性中的EntryPoint属性来帮助它。 C ++装饰这样的名称以支持方法重载和类型安全链接。__declspec(dllexport)
是链接器提示将函数放在DLL导出表中的提示。它与.DEF文件完全相同,没有维护这样一个文件的麻烦。__stdcall
设置调用约定,即参数传递给函数的方式。 32位代码有5种可能的调用约定(__cdecl, __stdcall, __fastcall, __thiscall, __clrcall
)。几乎所有外部软件都假定__stdcall
为默认值,包括P / Invoke编组程序。但是,C / C ++代码的默认值为__cdecl
。 DllImportAttribute.CallingConvention可用于覆盖托管端的默认值。解决此类问题的主要工具是Dumpbin.exe。使用/ exports选项从DLL上的Visual Studio命令提示符运行它。它列出了DLL导出表中找到的函数的名称。
你提到COM,这是一个完全不同的故事。 COM服务器不导出其功能,它使用“类工厂”[原文如此]。进程内服务器仅导出4个函数:
创建这4个导出函数通常是用于实现COM服务器的任何类库的工作。 C ++中的首选武器是ATL。
答案 3 :(得分:2)
每个.dll文件都有一个服务区,用于存储导出函数的名称及其实现的地址 - 这称为符号表。当消费应用程序想要从.dll文件调用函数时,它调用LoadLibrary(),然后调用GetProcAddress()来定位函数的地址。符号表用于方便查找。如果某个函数不在该表中,GetProcAddress()将无法找到它,并且使用的应用程序将无法调用它。
答案 4 :(得分:2)
您可以使用VS附带的dumpbin实用程序或通过depends.exe工具(更容易)查看导出的函数,您可以下载here。
答案 5 :(得分:0)
只需添加2c,因为我很难以非英语母语者的身份学习“从DLL导出函数”这一概念。
当你用
__declspec(dllexport)
从本质上讲,它意味着函数的定义转到DLL,而不是来自DLL。我仍然无法弄清楚为什么人们使用“从DLL导出功能”而不是“将功能导出到DLL”
“将A导出到B”表示A要去B。 “从B导入A”表示A来自B。 但是我真的不知道“从B导出A”。