gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-11-30 16:17:52 25 4
gpt4 key购买 nike

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

我特别需要这个 DLL 使用导入库按原样链接到 C 项目。 LoadLibrary 或更改导出名称等不是一个选项。

我有两个问题

  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

那么如何让 C 程序完全按照原样从 DLL 调用 ExampleFunction 呢?当 C 项目从 user32.dll 调用函数时,这是如何完成的?我可以使用相同的过程来实现此目的吗?

最佳答案

事实证明我上面发布的文章链接中的信息确实有效。 (https://qualapps.blogspot.com/2007/08/how-to-create-32-bit-import-libraries.html)

正如文章中提到的,我已经创建了一个虚拟/ stub 库...但是我没有按照我应该的方式将模块定义文件添加到虚拟库中。我忽略了这样做,但我什至不明白为什么它会起作用!我在 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,这是整个源文件!

    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. 设置为 Release模式并构建库。导航到 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。

关于c - 使用 MSVC 编译时,程序如何调用未修饰的 stdcall DLL 导出?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56143055/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com