在VC项目中调用Delphi函数

3
我在Delphi中创建了以下函数: function CheckResult():integer; stdcall; export; 编译器是Delphi 64,输出为CheckFunctions.dll。
然后,在VC++项目(VS2012)中,我写下了以下代码: extern "C" __declspec(dllimport) int __stdcall CheckResult(); 并在一个函数中使用它,但是我在C++项目中得到了编译器错误:
错误 LINK2019: 未解析的外部符号__imp_CheckResult,该符号在函数*中被引用。
我该怎么做?
谢谢。

你可以使用NTCore CFF Explorer来查看你的DLL,检查它是否真正是win64,并且它是否真正导出了你的函数以及导出的名称是什么。 - Arioch 'The
可能是如何在C++中使用dllimport的重复问题。 - Arioch 'The
有用的文章:https://dev59.com/OlHTa4cB1Zd3GeqPRnek 和 http://www.rsdn.ru/article/baseserv/dlluse.xml。 - Arioch 'The
3个回答

4
您缺少一个DLL的导入库(.lib文件)。这就是链接器给出该错误消息的原因。不幸的是,Delphi不会生成.lib文件,我认为这是一个弱点。
您可以通过以下两种方式解决问题:
1.使用LoadLibrary / GetProcAddress进行链接。 2.生成一个合适的.lib文件。
选项2非常容易。在Visual Studio中创建一个虚假的DLL项目。安排它导出与您的Delphi DLL相同的函数。使用空存根实现这些函数。使用.def文件而不是__declspec(dllexport)来避免导出名称装饰。
这真的很明显。制作一个具有与真实DLL相同接口的虚拟DLL。相同的名称,相同的函数。虚拟DLL不需要实现,因为您所做的一切就是让MS工具制作Delphi无法制作的.lib文件。
更多细节请参见:http://support.microsoft.com/kb/131313

就我所知,Delphi的导出修饰符会被忽略。建议使用exports子句代替。


1
有一种更简单的方法可以为现有的.dll文件创建.lib导入文件。使用VC的命令行LIB实用程序,它类似于C++Builder的命令行IMPLIB实用程序。 - Remy Lebeau
1
根据我链接的MS KB文章,这对于stdcall函数无法完成工作。在这种情况下,您需要为这些函数提供存根。 - David Heffernan
1
如果您使用延迟加载,您将获得两全其美的效果。静态链接到DLL函数,而无需手动调用GetProcAddress()(仍需要导入.lib),并在首次调用导出函数时运行时加载DLL,而不是在进程创建时加载。 - Remy Lebeau
谢谢,有了你的想法,我可以使用LoadLibrary/GetProcAddress成功地调用Delphi DLL。但是我还没有尝试第二种方法,这是否意味着我创建一个假的C++项目,其声明与Delphi相同并进行编译,然后VS会自动生成lib文件? - frank
1
@Warren Moskito的回答无法完成任务。使用stdcall时,您需要导出带有@n后缀的装饰名称函数。为了避免这种情况并使用普通名称,您需要使用我所描述的存根方法。 - David Heffernan
显示剩余8条评论

2
使用VC的命令行LIB工具为DLL创建VC兼容的导入.lib文件,然后将该文件添加到您的VC项目中。

1
根据我链接的MS KB文章,这对于stdcall函数无法完成任务。在这种情况下,您需要为这些函数提供存根。 - David Heffernan
那篇文章中没有任何内容表明它不支持stdcall函数。考虑到整个Win32 API(除了一些函数)都使用stdcall,所以如果不支持stdcall,那就太愚蠢了。此外,即使您采用存根方法,仍然需要使用LIB实用程序来编译实际的.lib文件,使用已编译的存根.obj文件,因此我的答案仍然适用。 - Remy Lebeau
1
从文章中可以看出,这些函数需要声明使用C调用约定。Win32 .lib文件可以从源代码构建,因为微软有这个功能。不需要反向工程.lib文件。当我最后一次查看时,我发现唯一的解决方案是存根。对于x86,您可以使用implib然后omf2coff(如果我记得正确的话)。对于x64,我认为需要存根。 - David Heffernan
是的,你可以使用 cl + lib,但关键是桩(stubbing)。你没有提到它,因为你认为它不需要。 - David Heffernan

0

使用 __declspec(dllimport) 可以设置命名方法前的 __imp__

在生成的 Delphi CheckFunctions.dll 中,只有一个名为 CheckResult 的函数。
VS C++ 2010 正在寻找 __imp__CheckResult。因此,链接器 VS C++ 找不到此函数。

您需要创建一个 .lib 文件。
使用程序 lib.exe 可以非常容易地创建这个 .lib 文件。
从 Visual-studio 命令提示符中运行 LIB.exe。

LIB 创建标准库、导入库和导出文件,您可以在构建程序时与 LINK 一起使用。

LIB 概述

如果我们已经使用 Visual-studio 命令提示符创建了:

C:\VisualStudio100\VC>lib /def:C:\CheckFunctions.def /OUT:C:\CheckFunctions.lib

可以找到 CheckFunctions.lib 文件中的 __imp__CheckResult

enter image description here

您可以按照以下步骤进行测试
在Delphi5和Win xp 32上它可以正常工作。

CheckFunctions.dpr

library CheckFunctions;

uses
  SysUtils,
  Classes;

{$R *.RES}

function CheckResult:integer; stdcall;
begin
   result:=12000;
end;

exports
CheckResult;

begin
end.

创建 def 文件
CheckFunctions.def
EXPORTS
CheckResult

创建 lib 文件
Visual Studio 命令
C:\Programme\VisualStudio100\VC>lib /def:C:\pathToProject\CheckFunctions.def /OUT:C:\pathToProject\CheckFunctions.lib

CheckFunctions.expCheckFunctions.lib 复制到您的 Visual C++ 2010 项目文件夹中

Visual C++ 2010 CallDll.cpp

#include "stdafx.h"

#define MY_DLL __declspec(dllimport)

extern "C"
{
MY_DLL int CheckResult();
} ;

int _tmain(int argc, _TCHAR* argv[])
{
    int myint = CheckResult();
    printf ("Decimals: %d \n", myint );
    return 0;
}

更新: 如果你想要使用 __stdcall

新的 Visual C++ 2010 CallDll.cpp

#include "stdafx.h"

#define MY_DLL __declspec(dllimport)

extern "C"
{
MY_DLL int __stdcall CheckResult();
} ;

int _tmain(int argc, _TCHAR* argv[])
{
    int myint = CheckResult();
    printf ("Decimals: %d \n", myint );
    return 0;
}

修改 Delphi 导出函数

exports
CheckResult name 'CheckResult@0';

Change
CheckFunctions.def

EXPORTS
CheckResult@0
  • 创建一个新的 CheckFunctions.lib

那也可以。


这就是为什么我在我的答案中创建了一个 CheckFunctions.lib,请看 "Create the lib file"。 - moskito-x
1
__imp__XXX不是DLL入口点,而是导入库中存根的名称。 - David Heffernan
我知道!OP的问题中的“unresolved external symbol __imp_CheckResult referenced in function”是什么意思?因此,您必须使用Visual Command创建一个.lib文件:lib /def:F:\stack\DllCall\CheckFunctions.def /OUT:F:\stack\DllCall\CheckFunctions.lib - moskito-x
@DavidHeffernan 请看一下我的带有__stdcall的更新。效果相同。 - moskito-x
是的,但现在你需要使用stdcall修饰符来修饰这些名称。这就是通过存根避免的问题。 - David Heffernan
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接