8

我一直在阅读许多关于 C++ 中非托管 DLL 的教程/文章。然而,就我的一生而言,我似乎无法理解这个概念。我很容易被关于它是否需要头文件、如何导出它、我是否需要 .lib 文件以及你有什么的看似分歧而感到困惑。

所以,让我们假设我只有一个这样的函数:

public int calculateSquare(int num)
{
    return num*num;
}

忽略实际代码,我需要什么才能将这个简单的函数本身变成一个我可以调用的 DLL?我只是将 __dllexport 或其他任何内容添加到第一行还是需要标题?我对这一切感到困惑。

4

4 回答 4

15

我不能强调这一点,C++ 编译器看不到头文件,在预处理器完成后,只有一个大源文件(也称为编译单元)。所以严格来说,您不需要标头来从 dll 导出此函数。您需要的是某种形式的条件编译来导出正在编译的 dll 中的函数并将其导入客户端代码中。

通常这是通过宏和头文件的组合来完成的。您创建了一个名为 MYIMPORTEXPORT 的宏,并通过使用宏条件语句使其像 dll 中的 __declspec ( dllexport ) 和客户端代码中的 __declspec ( dllimport ) 一样工作。

在文件 MYIMPORTEXPORT.h

#ifdef SOME_CONDITION
#define MYIMPORTEXPORT __declspec( dllexport )
#else
#define MYIMPORTEXPORT __declspec( dllimport )
#endif

在文件 MyHeader.h

#include <MyImportExport.h>

MYIMPORTEXPORT public int calculateSquare(int num)
{
    return num*num;
}

在 dll .cpp 文件中

#define SOME_CONDITION

#include <MyHeader.h>

在客户端代码 .cpp 文件中

#include <MyHeader.h>

当然,您还需要向链接器发出信号,表明您正在使用/DLL 选项构建 dll 。

构建过程还将生成一个 .lib 文件,这是一个静态库 - 在这种情况下称为存根 - 客户端代码需要链接到它,就好像它正在链接到一个真正的静态库一样。运行客户端代码时会自动加载 dll。当然 dll 需要操作系统通过其查找机制找到,这意味着您不能将 dll 放在任何地方,而是放在特定位置。这里有更多内容。

dumpbin是一个非常方便的工具,可以查看您是否从 dll 中导出了正确的函数,以及客户端代码是否正确导入。分别使用 /EXPORTS 和 /IMPORTS 运行它。

于 2008-10-25T07:27:17.857 回答
6

QBziZ 的回答是正确的。请参阅C++ 中的非托管 DLL

完成它:在 C++ 中,如果你需要使用一个符号,你必须告诉编译器它存在,并且通常,它的原型

在其他语言中,编译器只会自行探索库,并找到符号,等等

在 C++ 中,您必须告诉编译器。

将 C/C++ 标头视为书籍目录

最好的方法是将所需的代码放在某个公共位置。“界面”,如果你愿意的话。这通常在头文件中完成,称为头文件,因为这通常不是独立的源文件。标头只是一个文件,其目的是包含(即由预处理器复制/粘贴)到真正的源文件中。

实际上,您似乎必须声明两次符号(函数、类等)。与其他语言相比,这几乎是异端。

您应该将其视为一本书,带有摘要表或索引。在表中,您拥有所有章节。在文本中,您有章节及其内容。

有时,您很高兴拥有章节列表。

在 C++ 中,这是标题。

DLL 呢?

所以,回到 DLL 问题:DLL 的目的是导出代码将使用的符号。

因此,以 C++ 方式,您必须在编译时导出代码(例如,在 Windows 中,使用 __declspec)并“发布”导出内容的表(即具有包含导出声明的“公共”标题) .

于 2008-10-25T08:55:56.383 回答
1

导出函数的清单:

  • 调用约定是否适合调用者?(这决定了如何传递参数和结果,以及谁负责清理堆栈)。您应该明确说明您的调用约定。
  • 将以哪个名称导出符号?C++ 通常需要修饰(“mangle”)符号的名称,例如区分不同的重载。
  • 告诉链接器使函数作为 DLL 导出可见

在 MSVC 上:

  • __stdcall(这是帕斯卡调用约定)是导出符号的典型调用约定 - 我猜大多数客户端都支持。
  • extern "C" 允许您在不修改名称的情况下导出符号 C 样式
  • 用于__declspec(dllexport)标记要导出的符号,或链接一个单独的 .def 文件,其中列出了要导出的符号。使用 .def 文件,您还可以仅按序号(而不是名称)导出,并更改导出符号的名称。
于 2008-10-25T09:42:19.600 回答
0

您需要使用__declspec( dllexport )或将函数添加到模块定义文件 (.def) 来导出函数。然后将项目编译为 DLL。

在客户端,您有两种选择。使用编译 DLL 时生成的导入库 (.lib)。只需使用此库与您的客户端项目链接,您就可以访问从 DLL 导出的函数。你需要一个头文件,因为编译器需要知道你的函数的签名——它返回 int 并接受一个 int。回顾一下,您需要链接一个导入库 (.lib) 和一个包含函数头的头文件。

另一种方法是使用WinAPIcall动态加载 DLL LoadLibrary,然后GetProcAddress获取指向函数的指针。指向函数的指针必须具有正确的类型,以便编译器可以为其提供正确的参数并使用正确的调用约定。

于 2008-10-25T07:29:40.223 回答