如何在 HPP header 中使用提供的 DLL 文件?

How to use a provided DLL file with HPP header?

我目前在尝试与 Verifone PoS 集成时遇到一些问题。

与我们合作的银行为我们提供了一个.dll文件、一个.h文件和一个.hpp文件。

我通常是 Java 或 PHP 开发人员,所以在过去的几天里,我消耗了我在网上找到的所有关于 DLL 文件以及如何使用它们的信息,但是 none 到目前为止似乎有效。我遇到了很多错误,其中很多都是类似“无效的 dll”之类的错误。

我在网上找到了一个dll文件旁边应该有一个.lib文件。我问了第三方这件事,但显然

There is no .lib file. The .dll file contains all the required info for an integration

从他们的图书馆文档中我发现了这个:

The form of the supplied binary is a dynamic library. By its nature, a dynamic library allows for easier updates and corrections, not requiring recompilation or relinking of the client (calling) code, as long as the procedures prototypes (function parameters and return types) remain the same.

The language used for the library implementation is C++.

To access the functionalities implemented in the library binary, a C-style header interface is provided. This is comprised of the function prototypes available to be called as well as the types of the result-structures through which the returned data needs to be interpreted to make sense for the previously accessed functionality (the specific requested transaction).

是的,.h 文件只包含数据类型,而 .hpp 文件包含一些如下所示的声明:

extern "C" __declspec(dllexport) bool doSomething(int param);

在他们的文档中也有一个实现的示例(而且相当简单):

bool someVar = doSomething(1); 

看起来那些函数可以这么简单地调用,但实际上不能。如果我尝试这样做,我会收到“未定义函数”(或类似的)错误。

在这一点上,唯一似乎以某种方式起作用(也许)的是使用 LoadLibrary 函数加载 DLL。但除了我尝试使用任何参数调用任何函数之外,它 returns 是错误的,我根本不使用 .hpp 文件似乎有点不对。

我们到了。我应该如何处理这个?有没有办法加载 DLL 并将提供的 HPP 文件用作函数定义?如果没有,除了 LoadLibrary + GetProcAddress 组合之外还有其他方法吗?

谢谢!

我假设 dll 是本机 dll,而不是托管程序集 (.net dll)。

通常,dll 作者会在构建系统中添加预处理器定义,如 DLL_EXPORT。因此,如果作者编译 dll,导入库(一个小的 .lib 文件)将包含使用 DLL_API 宏的所有函数。然后作者可以将完全相同的 header 发送给用户。因为该用户不会定义 DLL_EXPORT 宏,所以 DLL_API 将解析为 dllimport,它基本上表示注释函数是在导入库中定义的。

这样的 header 可能看起来像这样(整个 #if 条件通常在它自己的 header 文件中,然后包含在导出的所有 header 中函数):

#ifdef DLL_EXPORT
#   define DLL_API __declspec(dllexport)
#else
#   define DLL_API __declspec(dllimport)
#endif

extern "C" 
{
    void DLL_API SomeFunction(int x);
    void DLL_API AnotherFunction(int x);
}

如果作者构建项目(在 msvc 中),编译器将生成 dll 文件一个小的.lib 文件,这是导入库。这个库基本上会做你现在必须做的事情:调用 LoadLibraryGetProcAddress 来解析所有用 __declspec(dllexport).

注释的函数

以下部分有点推测,我在这里猜测了一点。

__declspec(dllimport) 所做的就是告诉消费者此 dll 包含那些函数。但是 linker 必须 link 对其定义(实现)的声明,因此函数必须 definedcompiletime[=90 的某个地方=].那个地方就是导入库 (.lib)。如果您不 link 使用导入库,您将在构建项目时收到 linker 错误。

这意味着简单地将 dllexport 更改为 dllimport 并不能解决您的问题。如果没有导入库,您唯一的选择是使用 LoadLibrary 手动加载 dll 并搜索每个函数。

如果我是你,我会向作者索要使用该 dll 的示例项目。据我所知,使用本机 dll 的唯一方法是 link 导入库或手动加载所有内容。

从dll手动生成导入库

我已经测试过它以确保它有效。

首先,修复 header 文件,以便像我在上面的示例中那样使用宏,或者直接使用 dllimport

其次,打开 VS 的开发人员命令提示符,然后按照 this 回答中的步骤操作。确保使用正确的文件名和目标架构(x64 或 x86)。现在你应该有一个 .lib 文件。

第三,将库添加到您的项目中。

  1. 添加lib的目录(将它放在靠近项目的地方,这样你就可以使用相对路径)。打开项目属性并按照此图中的步骤操作: 确保 ConfigurationPlatform 是正确的(您可能希望像图中那样)。您也可以使用相对路径。单击 Macros 按钮可查看所有可用的预定义路径。
  2. 将库添加到 linker 依赖项中:
  3. 将 header 放在项目中可以访问的地方。

现在您只需在项目的任何位置包含 header 并使用其中声明的函数。但请注意,dll 文件必须放在 LoadLibrary 可以找到的地方。最好与项目的可执行文件所在的目录相同。

奖金事实

定义文件(.def)其实很简单。我上面的示例代码的 def 文件是:

LIBRARY MyLibrary
EXPORTS
AnotherFunction
SomeFunction

如果我删除声明周围的 extern "C" 块,我的函数名称将被破坏并且 def 文件如下所示:

LIBRARY MyLibrary
EXPORTS
?AnotherFunction@@YAXH@Z
?SomeFunction@@YAXH@Z

如果将这些函数放在命名空间中(例如 FooSpace),该命名空间名称也将成为函数名称的一部分:

LIBRARY MyLibrary
EXPORTS
?AnotherFunction@FooSpace@@YAXH@Z
?SomeFunction@FooSpace@@YAXH@Z

请注意,所有 extern "C" 实体都将忽略名称空间,这意味着所有 extern "C" 函数、变量、类型...都将放入全局名称空间中,无论您是否将它们定义在是否命名空间。

如果您手动完成,这些也是您必须传递给 GetProcAddress 的名称。