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

问题描述 投票:2回答:6

我对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

如果有人能用简单,清晰和详细的术语解释我,我将感激不尽。任何网站链接或教程也非常受欢迎。

编辑:

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

.net com dll
6个回答
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. 建议使用extern C,因为名称解析过程,特别是C ++如何处理函数重载。当你有两个功能:
int square (int x);
double square (double x);

编译器需要某种方式来区分它们 - 名称“square”现在是模糊的,并且不能解析为单个代码地址。 C ++通过名称修改处理它 - 编译器获取函数的名称square,然后添加一些与函数签名中的类型对应的魔术字符串。因此,例如,编译器可以将您的两个函数看作:

int int+square+int (int x);
double dbl+square+dbl (double x);

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

  1. 你想把那个函数称为“square”,而不是“int + square + int”,更糟糕的是,
  2. 不同的C ++编译器使用不同的名称修改规则。

为了便于互操作,人们通常用extern C标记它们导出的函数,这使得编译器使用C的命名规则,其中函数的名称不会被破坏。

编辑以解决注释:声明函数签名extern C解决了名称重整问题,因为C没有名称重整。因为声明了两个函数,C可以在没有名称修改的情况下逃脱

int square (int x);
double square (double x);

是一个错误;编译器/链接器不必 - 也不会 - 处理这种歧义。

Exporting a function from a DLL只不过是将函数添加到符号表中。这使得DLL外部的代码可以调用该函数,因为现在外部代码可以查找函数启动的位置。因此,该功能被“导出”,因为其他人可以调用它。


4
投票

从DLL导出函数只是意味着DLL的使用者将能够调用此函数。

从概念上讲,它类似于将函数添加到DLL公开的“接口”。

符号表是包含这些导出函数的表,其名称,ID和地址。

试试这个:http://msdn.microsoft.com/en-us/library/z4zxe9k8(VS.80).aspx


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。


2
投票

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


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

您可以使用VS附带的dumpbin实用程序或通过depends.exe工具(更容易)查看导出的函数,您可以下载here


0
投票

只是添加2c,因为我很难学习“从DLL中导出函数”作为非英语母语人士的概念。

当你用__declspec(dllexport)声明一些东西时,它实质上意味着函数的定义转到DLL而不是来自它。我仍然无法弄清楚为什么人们使用“从DLL导出函数”而不是“将函数导出到DLL”

“将A输出到B”意味着A将进入B.“从B导入A”表示A来自B.但是“从B导出A”我真的不知道。

© www.soinside.com 2019 - 2024. All rights reserved.