使用MSVC进行编译时,程序如何调用未修饰的stdcall DLL导出?

时间:2019-05-15 06:36:16

标签: c dll dllimport calling-convention

我有一个用x86汇编语言编写的DLL。所有导出均引用stdcall函数。所有出口均未装饰。

我特别需要使用导入库将此DLL链接到C项目。 LoadLibrary或更改导出名称等都不可行。

我有2个问题

  1. 当我从具有导出名称的DEF文件创建DLL的导入库(.lib)文件时,所有名称都修饰为cdecl!我找不到将它们指定为未经修饰的stdcall的方法。这些修饰的名称都不存在于DLL中,因此,在运行时,即使可以编译,也不会起作用。
  2. 当我编写代码以调用MSVC中的DLL函数时,无论是否指定stdcall,编译器都会修改这些函数的名称。因此,对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

我遇到了以下问题:

  1. 使用extern "C"不适用于此处,因为这是一个C项目。该说明符甚至无法工作,尽管这是一个C项目,但无论如何都会修改名称。
  2. 仅使用原始函数名称从DEF文件创建.lib文件不起作用。名称被修改。

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

时,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调用函数时,该怎么做?我可以使用相同的步骤来实现这一目标吗?

1 个答案:

答案 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文件的解决方案是:

  1. 创建一个新的Visual C DLL项目(不是静态库),将其命名为要为其创建.lib的DLL。

  2. 在主要的源代码文件(这是C ++文件)中(不管是这种情况),复制所需功能的签名,如下所示。例如lib-lib,这就是整个源文件!

    include "stdafx.h"
    extern "C" __declspec(dllexport) void _stdcall ExampleFunction(int a, int b, int c) { }
    
  3. 这是关键部分。在Visual C的解决方案资源管理器中,右键单击“源文件”>“添加”>“新建项...”。在出现的对话框中,选择“代码”>“模块定义文件(.def)”。使其与您的DLL名称匹配,并按如下所示导出名称。由于此步骤不需要更改任何设置即可引用模块定义,因此我真的不知道为什么这样做。但这确实可以。。。似乎没有正式的记录证明这种方法行得通,但是该功能至少在2005-2007年(撰写本文时)就已存在于MSVC中。

    LIBRARY example-lib.dll
    EXPORTS
    ExampleFunction
    
  4. 设置为发布模式并构建库。导航到Windows资源管理器中的项目目录,您现在应该看到.dll文件和.lib文件。您可以运行dumpbin /headers example-lib.lib来验证ExampleFunction未被修饰,如上所示。

  5. 要使用虚拟.lib文件引用真实的DLL,请通过转到“项目属性”>“链接器”>“输入”>“其他依赖项”将其添加到原始项目中(并在必要时添加依赖项目录)。然后在这样的代码中声明对它的引用:

    extern void __stdcall ExampleFunction(int a, int b, int c);
    

构建您的项目,它应该可以工作。如果您在实用程序中查看项目EXE / DLL,您会看到.idata部分将使用未经修饰的函数名引用DLL。之所以可行,是因为编译器将始终生成诸如_ExampleFunction@12之类的符号引用,但是当该符号引用与专用的.lib文件一起到达Microsoft的链接器时,它将看到name type的值为undecorate,并且它应将此符号解析为实际名称ExampleFunction.