如何在C ++ Builder项目中的Delphi单元中使用Crtl? (或链接到C ++ Builder C运行时库)

时间:2011-03-14 19:41:57

标签: c delphi c++builder .obj

我有一个Delphi单元,它使用{$L xxx}指令静态链接C .obj文件。 C文件是使用C ++ Builder的命令行编译器编译的。为了满足C文件的运行时库依赖关系(_assert,memmove等),我要包括crtl单位Allen Bauer提到的here

unit FooWrapper;

interface

implementation

uses
 Crtl; // Part of the Delphi RTL

{$L FooLib.obj}  // Compiled with "bcc32 -q -c foolib.c"

procedure Foo; cdecl; external;

end.

如果我在Delphi项目(.dproj)中编译该单元,则everthing工作正常。

如果我在C ++ Builder项目(.cbproj)中编译该单元,它将失败并显示错误:

[ILINK32 Error] Fatal: Unable to open file 'CRTL.OBJ'

事实上,RAD Studio安装文件夹中没有crtl.obj个文件。有一个.dcu,但没有.pas。尝试将crtdbg添加到uses子句(定义了_assert的C头)会出现无法找到crtdbg.dcu的错误。

如果我删除使用子句,则会失败,但找不到__assert_memmove的错误。

因此,在C ++ Builder项目的Delphi单元中,如何从C运行时库中导出函数以便它们可用于链接?

我已经知道Rudy Velthuis的article了。如果可能的话,我想避免手动编写Delphi包装器,因为我在Delphi中不需要它们,而C ++ Builder必须已经包含必要的功能。

修改

对于任何想在家中玩游戏的人来说,代码可以在https://tpabbrevia.svn.sourceforge.net/svnroot/tpabbrevia/trunk的Abbrevia的Subversion存储库中找到。我采用了David Heffernan的建议,并在C ++ Builder中编译时添加了一个模仿crtl.dcu的“AbCrtl.pas”单元。这使得PPMd支持工作,但Lzma和WavPack库都因链接错误而失败:

[ILINK32 Error] Error: Unresolved external '_beginthreadex' referenced from ABLZMA.OBJ
[ILINK32 Error] Error: Unresolved external 'sprintf' referenced from ABWAVPACK.OBJ
[ILINK32 Error] Error: Unresolved external 'strncmp' referenced from ABWAVPACK.OBJ
[ILINK32 Error] Error: Unresolved external '_ftol' referenced from ABWAVPACK.OBJ

AFAICT,所有这些都被正确声明,而_beginthreadex实际上是在AbLzma.pas中声明的,所以它也被纯Delphi编译使用。

要自己查看,只需下载trunk(或只是“source”和“packages”目录),禁用AbDefine.inc底部的{$ IFDEF BCB}块,并尝试编译C ++建造者“Abbrevia.cbproj”项目。

3 个答案:

答案 0 :(得分:4)

我对此的看法是,您只需要项目的Delphi版本中的Delphi单元。

在C ++构建器版本中,您只需编译并链接foolib.c,就好像它是一个C文件一样(它是!)在程序的Delphi版本中,您使用bcc32创建.obj,如上所述使用ctrl等。

为什么要在Delphi包装器中将C库包装起来以便在C ++中使用?

编辑1

您已在评论中添加了说明。

另一个需要考虑的选择是避免使用crtl并在FooWrapper中实现缺少的函数。我这样做而不是使用crtl,因为这给了我更多的控制权,我明白了被调用的内容。例如,我不希望任何对printf()的调用泄漏到我的GUI应用程序或我的DLL中。

如果你只缺少一些功能,这可能是一个很有吸引力的选择。通常,获取它们的最佳方法是将它们与msvcrt.dll链接起来,msvcrt.dll是目前的标准系统组件。当然,在msvcrt.dll中链接只是为了获得memset()memcpy()等等,似乎有点重量级。

编译没有crtl的Delphi单元时有多少缺少的函数?

编辑2

我将此添加到答案中以显示一些代码。从我自己的代码库中我提供了这个:

const
  __turboFloat: Longint=0;
  (* We don't actually know the type but it is 4 bytes long and initialised to zero.  This can be determined
     using tdump initcvt.obj.  It doesn't actually matter how we define this since it is ultimately not
     referred to and is stripped from the executable by the linker. *)

对于ftol我在ftol.obj中链接,我认为我是从我使用的BCC55编译器中的一个lib文件中提取的。

我认为strncmp在普通的Pascal中应该是非常常规的。

sprintf在完全普遍性方面更难,但您可能会发现它仅用于像整数到字符串这样的小事。在这种情况下,您可以捏造C代码来调用专用的例程并轻松实现它。

老实说,我认为'msvcrt.dll'看起来很有吸引力!

编辑3

我很快就说话了吗?您可以从user32.dll中提取一个完全可维护的sprintf,几乎所有进程都已加载。如果它是您需要的ANSI版本,请务必选择wsprintfA

编辑4

我注意到_beginthreadex。你说这是在不同的Delphi单元中定义的。为了让编译器看到它,你需要在AbCtrl.pas中重新声明它,并从那里调用AbLzma.pas中的真实版本。

当您在Delphi .pas文件中包含.obj时,编译器必须能够从链接到.obj的Delphi单元中解析.obj文件中的所有引用。整个游戏由编译器而不是链接器处理。

有时你会遇到包含.obj文件的顺序纠结,解决方案是使用前向声明,但这是另一个故事。

答案 1 :(得分:0)

在这种情况下,假设您感兴趣的函数可以直接从C RTL获得,因此使用虚拟(空)obj文件伪装链接器应该可以工作,因为它将满足链接器查找obj文件Delphi告诉你需要,但仍然在RTL中找到函数。

答案 2 :(得分:0)

晚了,但更完整: crtl.dcu从D2005到XE2都没有问题。

对于D6和D7,依赖于midaslib.dcu。 好吧,不是真的,dcu是用脏使用条款分发的。

对于D6和D7,您应该创建一个EMPTY midaslib.pas代理,例如:

unit midaslib;
interface
implementation
end.

现在你可以使用crtl.dcu而不会出现内部错误!