如何正确地在Delphi中静态链接C语言?

7

我应该如何正确地将C与Delphi静态链接? Delphi会输出以下错误信息:

[dcc32 错误] Project1.dpr(15): E2065 未满足的前向或外部声明: 'Test'

C编译器是带有COFF目标文件的Visual C++。

Delphi:

program Project1;

{$L C:\Source.obj}

function Test(): Integer; cdecl; external;

begin
  WriteLn(Test);
end.

C:

extern "C" int __cdecl Test()
{
    return 12;
}

int main()
{
    return 0;
}

Delphi x64 支持 COFF,但 Delphi x86 呢? - Arioch 'The
@Arioch'The 这是32位的,所以是的。 - user15124
@Arioch'The 它对某些 COFF 格式的支持比其他格式更好。但在 mingw COFF 格式上表现不太好。 - David Heffernan
@user15124 我使用mingw处理简单的事情时取得了成功,但当我的源代码变得更加复杂时,我遇到了问题。无论如何,祝你好运! - David Heffernan
1
我记不起来了。自从我尝试过以后已经很久了。如果我没记错的话,当时我正在专注于x64,因为在x86上我仍然使用bcc32。我曾经遇到过麻烦,无法让mingw输出Delphi可以处理的.obj文件。另一方面,msvc生成的文件通常都能被很好地使用。但是,在这个领域有很多细节和 nuances。需要使用有趣的堆栈探测、函数声明顺序等各种技巧和魔法才能使复杂的库链接起来。无论如何,如果你遇到了困难,你可以在这里提问并得到很好的帮助,我相信。 - David Heffernan
显示剩余2条评论
1个回答

8

这取决于您所使用的任何C编译器使用的名称修饰方式。例如,32位 bcc32 编译器会将 Test 修饰为 _Test。因此,与之链接的 Delphi 代码应该是:

function Test(): Integer; cdecl; external name '_Test';

但是装饰在编译器之间是不同的,您没有说明您使用的是哪个编译器。如果上面的代码没有帮助,那么您应该使用C编译器的工具来转储obj文件并检查其中的函数名称。

另一个问题是您实际上正在使用C++编译器而不是C编译器。这可以从您使用的方式中看出

extern "C" 

这段代码不是有效的C代码。你应该删除它并切换到一个C编译器。将扩展名从.cpp改为.c通常就足以让编译器将代码视为C代码。

如果你开始调用C标准库中的函数,比如malloc和其它函数,那么你需要在Delphi代码的使用子句中添加System.Win.Crtl单元。

还要注意的是,在你的C代码中,不需要也不应该实现一个main函数。如果你想将C函数编译成一个单独的C程序,那么把这些函数放在单独的源文件中,与包含主函数的源文件分开。这样你就可以将源文件编译成对象,并将它们链接到C程序或者你的Delphi代码中。但你不需要在你的Delphi程序中携带一个没有调用的main函数。

在C语言中,一个无参的main函数的正确签名是:

int main(void)

同样地,你的另一个C函数应该有这个签名:
int __cdecl Test(void)

当然,__cdecl是默认值,所以我们完全可以省略它:
int Test(void)

让我们把所有内容都整合起来: C
int Test(void)
{
    return 12;
}

很重要的一点是您需要使用C编译器进行编译,而不是作为C++进行编译。如果您现在编辑中声明的编译器是MSVC,则命令行应为:

cl /c source.c

Delphi

(暂未提供需要翻译的内容)

{$APPTYPE CONSOLE}

{$L Source.obj}

function Test: Integer; cdecl; external name '_Test';

begin
  WriteLn(Test);
end.

输出

12

1
是的,这被称为函数名修饰。我不确定规则是什么,或者为什么要这样做。历史原因。stdcall修饰包括一个@XX,其中XX是要清理的堆栈大小。无论如何,它就是它!在x64上,您的函数将不会被修饰!想想看。 - David Heffernan
1
因此,对于需要针对x86和x64目标的代码,您通常会得到类似这样的内容: const PU = {$IFDEF CPUX86}'_'{$ELSE}''{$ENDIF}; function foo; external name PU+'foo'; - David Heffernan
1
是的,你挑选了我最喜欢的主题之一,也是我在工作中不得不深入研究的主题之一,所以只要有人愿意听,我就很乐意继续谈论这个话题!;-) - David Heffernan
我一直在针对x64。我们正在看一个非常简单的例子。当涉及到真实世界的代码时,情况会变得更加棘手。 - David Heffernan
1
祝你好运。自从我上次尝试以来,情况可能已经发生了变化。被编译的代码差异很重要,这也是非常有可能的。 - David Heffernan
显示剩余6条评论

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