我有一个用x86汇编语言编写的DLL。所有导出均引用stdcall
函数。所有出口均未装饰。
我特别需要使用导入库将此DLL链接到C项目。 LoadLibrary
或更改导出名称等都不可行。
我有2个问题
cdecl
!我找不到将它们指定为未经修饰的stdcall
的方法。这些修饰的名称都不存在于DLL中,因此,在运行时,即使可以编译,也不会起作用。ExampleFunction
的调用变成了_ExampleFunction@12
,我需要仅是ExampleFunction
。幸运的是,由于Windows系统DLL(例如kernel32.dll,user32.dll等)遵循完全相同的约定,因此我必须能够做到。每个导出的函数都是stdcall且未修饰。
我已经读过:https://qualapps.blogspot.com/2007/08/how-to-create-32-bit-import-libraries.html
我遇到了以下问题:
extern "C"
不适用于此处,因为这是一个C项目。该说明符甚至无法工作,尽管这是一个C项目,但无论如何都会修改名称。 example-lib.def
文件如下所示:
LIBRARY example-lib.dll
EXPORTS
ExampleFunction
.lib
文件是使用以下命令生成的:
lib.exe /def:example-lib.def /OUT:example-lib.lib
使用dumpbin.exe /headers example-lib.lib
Version : 0
Machine : 14C (x86)
TimeDateStamp: 5CDBA30C Wed May 15 06:26:36 2019
SizeOfData : 00000016
DLL name : example-lib.dll
Symbol name : _ExampleFunction
Type : code
Name type : no prefix
Hint : 20
Name : ExampleFunction
我通过以下声明进行导入:
extern void __stdcall ExampleFunction(int a, int b, int c);
我这样调用函数:
ExampleFunction(1, 2, 3);
它所在的lib文件和目录是在VC项目设置中指定的。
当编译调用ExampleFunction
的代码时,链接器将无法完成,因为它无法在我的DEF文件中找到_ExampleFunction@12
。即使DEF文件中存在该文件,该程序也不会运行,因为_ExampleFunction@12
并未在实际的DLL的导出部分中导出。仅导出普通ExampleFunction
。
那么,如何实现完全从DLL完全从DLL调用ExampleFunction的C程序呢?当C项目从user32.dll调用函数时,该怎么做?我可以使用相同的步骤来实现这一目标吗?
答案 0 :(得分:-1)
在我上面发布的文章链接中找到确实有用的信息。 (https://qualapps.blogspot.com/2007/08/how-to-create-32-bit-import-libraries.html)
我已经按照文章所述创建了一个虚拟/存根库...但是我没有按照应有的方式将模块定义文件添加到虚拟库中。我忽略了这样做,但是,我什至不知道为什么它会起作用!我在Visual Studio外部创建了它,并在Visual Studio外部为其生成了.lib。没运气。在Visual Studio中完成所有工作即可。
只需添加如下所示的模块定义...
LIBRARY example-lib.dll
EXPORTS
ExampleFunction
...导致输出的.lib文件报告此错误,而不是我最初在问题中列出的内容:
Version : 0
Machine : 14C (x86)
TimeDateStamp: FFFFFFFF
SizeOfData : 00000019
DLL name : example-lib.dll
Symbol name : _ExampleFunction@12
Type : code
Name type : undecorate
Hint : 0
Name : ExampleFunction
这很奇怪! 如文章所述,这本质上是未记录的行为,但确实可以解决我的问题。
如果按照我的方式操作文章时遇到麻烦,那么使用未修饰的stdcall
导入.lib文件的解决方案是:
创建一个新的Visual C DLL项目(不是静态库),将其命名为要为其创建.lib的DLL。
在主要的源代码文件(这是C ++文件)中(不管是这种情况),复制所需功能的签名,如下所示。例如lib-lib,这就是整个源文件!
include "stdafx.h"
extern "C" __declspec(dllexport) void _stdcall ExampleFunction(int a, int b, int c) { }
这是关键部分。在Visual C的解决方案资源管理器中,右键单击“源文件”>“添加”>“新建项...”。在出现的对话框中,选择“代码”>“模块定义文件(.def)”。使其与您的DLL名称匹配,并按如下所示导出名称。由于此步骤不需要更改任何设置即可引用模块定义,因此我真的不知道为什么这样做。但这确实可以。。。似乎没有正式的记录证明这种方法行得通,但是该功能至少在2005-2007年(撰写本文时)就已存在于MSVC中。
LIBRARY example-lib.dll
EXPORTS
ExampleFunction
设置为发布模式并构建库。导航到Windows资源管理器中的项目目录,您现在应该看到.dll文件和.lib文件。您可以运行dumpbin /headers example-lib.lib
来验证ExampleFunction
未被修饰,如上所示。
要使用虚拟.lib文件引用真实的DLL,请通过转到“项目属性”>“链接器”>“输入”>“其他依赖项”将其添加到原始项目中(并在必要时添加依赖项目录)。然后在这样的代码中声明对它的引用:
extern void __stdcall ExampleFunction(int a, int b, int c);
构建您的项目,它应该可以工作。如果您在实用程序中查看项目EXE / DLL,您会看到.idata
部分将使用未经修饰的函数名引用DLL。之所以可行,是因为编译器将始终生成诸如_ExampleFunction@12
之类的符号引用,但是当该符号引用与专用的.lib文件一起到达Microsoft的链接器时,它将看到name type
的值为undecorate
,并且它应将此符号解析为实际名称ExampleFunction.