1

这已经在很多地方进行了部分讨论,但它仍然对我不起作用。

我有一个从 delphi 源代码编译的 dll,它以两个名称导出一个函数,使用检查 dll

>> dumpbin /EXPORTS MyLibrary.dll

我得到以下输出:

...
17    3 00070A88 MyFunction
...
46   24 00070A88 _MyFunction@48
...

所以我创建了一个名为 MyLibrary.def 的文件,其内容如下:

EXPORTS
MyFunction
_MyFunction@48

并使用生成了一个导入库

>> lib /def:MyLibrary.def /OUT:MyLibrary.lib /MACHINE:x86

使用 dumpbin 检查新的 lib 文件,我看到以下内容:

...
_MyFunction
...
__MyFunction@48
...

所以不知何故,lib 应用程序在函数名称前添加了一个下划线。(为什么?)

然后我尝试在 c++ 程序中使用这个函数,用 Microsoft Visual Studio C++ 2010 Express 编译(使用 lib 文件):

// MyLibrary.h
# define DllImport(Type) __declspec (dllimport) Type __stdcall

extern "C" DllImport(void)MyFunction(...);
// main.cpp
#import "MyLibrary.h"

...
MyFunction(....);
...

据我所知,这现在应该可以工作,但是我收到以下链接器错误:

... error LNK2001: Unresolved external sympol "__imp__MyFunction@48".

我不明白为什么会出错(我真的不明白整个事情是如何运作的......)但我又尝试了两件事。

  1. 将 MyLibrary.h 和 main.h 中的函数从 MyFunction 重命名为 _MyFunction
    • 结果:有效!但为什么?我不想依赖这个,因为有些事情显然是错误的。  
  2. 将我的函数重命名为 MyFunction 并删除了 def-File 中的下划线,再次生成 lib 文件并尝试编译
    • 结果:编译成功,但启动程序时我得到

MyApp - Entry Point Not Found
---------------------------
The procedure entry point MyFunction@48 could not be located
in the dynamic link library MyLibrary.dll. 

我认为需要对 lib 工具和链接器的内部工作有更深入的了解,但到目前为止我找不到任何关于此的信息。

4

1 回答 1

4

KB131313解释了尝试使用该lib实用程序时会遇到的一个问题:

唯一可以使用 .DEF 文件从没有源代码或目标模块的 .DLL 创建导入库的情况是 .DLL 通过 C 接口导出函数。具体来说,需要声明函数以使用 C 调用约定。

@您的函数使用 stdcall,而不是 cdecl,从函数名称中的可以看出。尽管如此,知识库文章解释了该怎么做:

  1. 像在 C++ 中一样声明该函数,但用于export而不是import

    大多数情况下,您已经这样做了。你有正确的调用约定,但@48最后意味着它需要有 48 个字节的参数。该函数将期望将该数据推送到堆栈中,并且在它返回之前,该函数将弹出那么多。您...在声明中的使用与此不兼容。

    如果你不知道具体的参数列表到底应该是什么,那么继续定义 12 个int参数,这样至少堆栈结构是正确的。(但如果你不知道该列表到底应该是什么,那么无论如何你都快要完蛋了。)

  2. 用 C++ 编写一个虚拟实现。

    实现可以为空。唯一的要求是代码编译和链接。

  3. 从该虚拟代码编译您自己的MyLibrary.dll版本。

    确保它与原始 DLL 兼容。在它上面运行dumpbin并看到它至少导出了您在原始 DLL 中看到的函数名称的一个版本。(您不需要两者;您的程序只会使用其中一个名称,并且如dumpbin输出所示,两个名称都位于二进制文件中的相同位置,因此您的程序最终使用哪个名称并不重要.)

  4. 丢弃 DLL,只保留 LIB 文件。

  5. 使用 lib 文件与真实的 DLL 链接。

如果链接器仍然抱怨它找不到,则从您的声明__imp_MyFunction@48中删除该部分。dllimport那应该删除__imp_前缀,使名称看起来更像 Delphi 名称。


如果一切都失败了,您可以使用运行时动态链接而不是加载时。为函数声明一个指针类型,然后使用LoadLibraryandGetProcAddress来访问它。

于 2012-08-29T13:07:35.937 回答