什么`从DLL导出函数'是什么意思?

时间:2009-12-21 10:09:16

标签: .net com dll

我对dll知之甚少。我正在读一本关于COM的书。作者指的是Exporting a function from a DLL。他告诉我该怎么做,但他不知道它是什么或为什么要这样做?

他建议的方法是: a)使用extern "C" 标记函数(不知道为什么?) b)创建一个DEF文件并在此DEF文件的EXPORTS部分中添加函数名称。 再次不知道为什么以及究竟发生了什么?

我不理解的另一件事是术语symbols / symbol table

  1. Exporting a function from a DLL是什么意思?
  2. 什么是symbols / symbol table
  3. 如果有人能用简单明了的方式解释我,我将感激不尽。细节条款。任何网站链接或教程也受到欢迎。

    修改

    我在.NET中使用过DLL。其中我只包括使用命名空间行并将dll添加到引用并且它们工作。这是我知道如何使用dll的唯一方法。我不知道如何在.net中使用dll与在COM中使用它们不同。任何人都可以与.NET联系吗?

6 个答案:

答案 0 :(得分:8)

.NET中的DLL与本机DLL完全不同。 .NET DLL包含字节码,称为CIL,它有足够的信息供其他程序(如csc编译器)使用,以计算出包含在其中的类,类型,接口等。

这与本机DLL完全不同。原生DLL包含二进制指令&主要是非结构化数据,并且(通常)没有办法计算出数据意味着什么 - 例如,DLL中的某个地方可能是两个字节(十六进制)43 23并且有无法判断程序是将这些字符解释为字符C#还是整数17187或内存地址,甚至是指向CPU的指令。

所以,继续你的问题:

  1. symbol table是DLL的一段元数据;它告诉编译器/链接器如何将void myDllFunc (int bar)转换为DLL中的地址。它基本上是一个查找表。从DLL中导出函数就是如何告诉你希望在查找表中找到哪些函数 - 这些是其他代码能够调用的函数,因为它将能够找到他们。记住 - 没有其他信息,就无法告诉 myDllFunc开始的地方。
  2. 建议
  3. extern C因为名称解析的过程,特别是C ++如何处理函数重载。当你有两个功能时:
  4. 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);
    

    它们现在不再含糊不清(真正的编译器不使用这么简单的方案)。现在有两个问题:

    1. 你想把那个函数称为“square”,而不是“int + square + int”,更糟糕的是,
    2. 不同的C ++编译器使用不同的名称修改规则。
    3. 为了便于互操作,人们通常用extern C标记它们导出的函数,这使得编译器使用C的命名规则,其中函数的名称不会被破坏。

      编辑以解决评论: 声明函数签名extern C可以解决名称重整问题,因为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个函数:

  • 的DllRegisterServer。由Regsvr32.exe用于在注册表中注册服务器。
  • DllUnregisterServer的。如上所述,用于删除注册。
  • DllCanUnloadNow。由COM管道定期调用以检查服务器是否不再需要,并且可以从内存中卸载。
  • 的DllGetClassObject。这是重要的一个,当客户端调用CoCreateObject()时由COM调用它。 COM服务器通过创建COM coclass并返回指向所请求接口的接口指针来实现它。然后,COM客户端使用它来调用接口上的方法。接口指针指向接口实现的函数地址列表,就像接口在.NET中工作一样。

创建这4个导出函数通常是用于实现COM服务器的任何类库的工作。 C ++中的首选武器是ATL。

答案 3 :(得分:2)

每个.dll文件都有一个服务区,用于存储导出函数的名称及其实现的地址 - 这称为符号表。当消费应用程序想要从.dll文件调用函数时,它调用LoadLibrary(),然后调用GetProcAddress()来定位函数的地址。符号表用于方便查找。如果某个函数不在该表中,GetProcAddress()将无法找到它,并且使用的应用程序将无法调用它。

答案 4 :(得分:2)

  1. 对于第一个近似,DLL是一组有趣的函数集合。其中一些函数用于DLL的用户,其他函数由其他函数在内部使用,并且作者不打算让DLL的用户访问。在确定DLL的用户应该看到DLL中的哪些函数后,可以通过导出它们使它们可见。
  2. 符号是二进制文件(DLL,EXE等)中函数和其他blob的名称。符号表是存储这些名称集合的二进制文件内的数据结构,并为链接器和其他工具提供从名称到命名blob的映射。
  3. 您可以使用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”。