.NET 中的 DLL 与本机 DLL 完全不同。 .NET DLL 包含称为 CIL 的字节码,它为其他程序(例如 csc 编译器)提供了足够的信息来计算其中包含的类、类型、接口等。
这与本地 DLL 完全不同。本机 DLL 包含二进制指令和大部分非结构化数据,并且(通常)无法计算出数据 的含义 - 例如,DLL 中的某处可能是两个字节(十六进制)@ 987654321@ 并且无法判断程序是否会将这些解释为字符 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);
它们现在不再模棱两可了(真正的编译器不使用这种简单的方案)。现在这里有两个问题:
- 您想将该函数称为“square”,而不是“int+square+int”,更糟糕的是,
- 不同的 C++ 编译器使用不同的名称修饰规则。
为了方便互操作,人们一般将导出的函数标记为extern C,这使得编译器使用C的命名规则,其中函数的名称不会被弄乱。
编辑地址 cmets:
声明函数签名extern C 解决了名称修改问题,因为 C 没有名称修改。由于声明了两个函数,C 可以在不修改名称的情况下逃脱
int square (int x);
double square (double x);
是一个错误;编译器/链接器不必——也不会——处理这种歧义。
Exporting a function from a DLL 无非就是将函数添加到符号表中。这使得 DLL 外部的代码可以调用该函数,因为现在外部代码可以查找函数的开始位置。因此,该函数被“导出”,其他人可以调用它。