【问题标题】:How to use Crtl in a Delphi unit in a C++Builder project? (or link to C++Builder C runtime library)如何在 C++Builder 项目的 Delphi 单元中使用 Crtl? (或链接到 C++Builder C 运行时库)
【发布时间】:2011-07-15 06:50:05
【问题描述】:

我有一个 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) 中编译该单元,则一切正常。

如果我在 C++Builder 项目 (.cbproj) 中编译该单元,则会失败并出现错误:

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

事实上,RAD Studio 安装文件夹中没有crtl.obj 文件。有.dcu,但没有.pas。尝试将crtdbg 添加到uses 子句(定义_assert 的C 标头)会导致找不到crtdbg.dcu 的错误。

如果我删除 uses 子句,它会失败并出现 __assert_memmove 未找到的错误。

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

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

编辑

对于任何想在家一起玩的人,代码可在 Abbrevia 的 Subversion 存储库中获取,地址为 https://tpabbrevia.svn.sourceforge.net/svnroot/tpabbrevia/trunk。我采纳了 David Heffernan 的建议并添加了一个“AbCrtl.pas”单元,在 C++Builder 中编译时该单元模仿 crtl.dcu。这使 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编译使用。

要自己查看,只需下载主干(或仅下载“源”和“包”目录),禁用 AbDefine.inc 底部的 {$IFDEF BCB} 块,然后尝试编译 C++生成器“Abbrevia.cbproj”项目。

【问题讨论】:

    标签: c delphi c++builder .obj


    【解决方案1】:

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

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

    为什么要将其封装在 Delphi 包装器中的 C 库中以便在 C++ 中使用?

    编辑 1

    您已在 cmets 中添加了说明。

    另一个需要考虑的选项是避免 crtl 并在 FooWrapper 中实现缺失的功能。我这样做而不是使用 crtl 因为这给了我更多的控制权,而且我理解被调用的内容。例如,我不希望任何对 printf() 的调用泄漏到我的 GUI 应用程序或我的 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。你说这是在不同的德尔福单元中定义的。为了让编译器看到它,您需要在 AbCtrl.pas 中重新声明它,然后在 AbLzma.pas 中调用真实版本。

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

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

    【讨论】:

    • 我正在向 TurboPower Abbrevia(一个 Delphi zip/tar/gz 库)添加 Lzma 支持。所有与 Lzma (C) 库交互的代码都是 Delphi。我只是想让 Abbrevia 也支持 C++Builder。
    • 我有点不知所措。 Delphi 单元可以访问 C++Builder 项目中的 .c/.h 文件吗?也就是说,如果我在直接访问原始项目标头的 .c 文件中创建重复的“LzmaDecompress()”函数,我将如何在我的 Delphi 单元中引用它?
    • 将 C 代码静态链接到 Delphi 的唯一方法是使用 $L。
    • @Craig 你是在 sf 项目上做这个的吗?是开叉吗?从哪里可以得到你的贡献?我对 LZMA 支持很感兴趣,我也喜欢 TP Abbrevia,所以我认为它值得一看 :)
    • 我想我明白了。我使用“名称”指令和“外部”指令来匹配“_memcmp”到“memcmp”。删除“名称”部分解决了链接器错误。 sprintf 仍然使用 msvcrt 而不是 user32,但这没关系。如果它困扰 C++ 开发人员,他们可以解决。 :)
    【解决方案2】:

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

    【讨论】:

    • 我不确定。如果是这种情况,那么删除 crtl 也可以。
    • DavidH 是对的,失败并出现相同的“未解决的外部 'Crtl::_memmove' 类型错误。
    • "unresolved external" 实际上很有帮助。我在最初的回复中几乎提到了它。看起来 delphi 单元将方法放在不同的命名空间中。可以解决,但看起来你已经找到了可行的解决方案。
    • 如果有办法解决它,我很想听听。 msvcrt.dll 依赖项有效,但有点笨拙。
    • @David 我相信您缺少的是这些名称的解析是由 Delphi 编译器完成的,而不是由链接器完成的。如果在 Delphi 中有类似于 extern 的东西,那么就可以完成工作。但是没有。
    【解决方案3】:

    晚了,但更完整: crtl.dcu 从 D2005 到 XE2 都可以正常工作。

    D6 和 D7 依赖于 midaslib.dcu。 好吧,实际上并非如此,dcu 分发时带有脏使用条款。

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

    unit midaslib;
    interface
    implementation
    end.
    

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

    【讨论】:

    • 这个问题是关于在 C++Builder 中使用 Crtl.dcu。特别是使用 C++Builder 编译的 .pas 文件中的 .c 文件。仅使用 Delphi 时,我已经可以使用它了。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-24
    • 2013-01-26
    • 2022-01-23
    • 2019-01-07
    • 1970-01-01
    • 2016-08-23
    • 2011-04-02
    相关资源
    最近更新 更多