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

标签 c delphi c++builder .obj

我有一个 Delphi 单元,它使用 {$L xxx} 指令静态链接 C .obj 文件。 C 文件是用 C++Builder 的命令行编译器编译的。为了满足 C 文件的运行时库依赖项(_assert、memmove 等),我包含 Allen Bauer 提到的 crtl 单元 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 必须已经包含必要的函数。

编辑

对于任何想在家玩的人,可以在 Abbrvia 的 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} block ,然后尝试编译 C++构建器“Abbrevia.cbproj”项目。

最佳答案

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

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

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

编辑 1

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

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

如果您只缺少少数功能,这可能是一个有吸引力的选择。通常,获取它们的最巧妙方法是将它们从 msvcrt.dll 链接到其中,msvcrt.dll 是当今的标准系统组件。当然,仅仅为了获取 memset() 链接到 msvcrt.dll 似乎有点重量级。 , 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

我很快就说话了吗?您可以拉一个完美可用的sprintf无论如何,几乎所有进程都已经加载了 user32.dll。确保您选择了 wsprintfA 如果您需要的是 ANSI 版本。

编辑4

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

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

有时,您会因包含 .obj 文件的顺序而陷入困境,解决方案是使用前向声明,但那是另一个故事了。

关于c - 如何在C++Builder项目的Delphi单元中使用Crtl? (或链接到 C++Builder C 运行时库),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5303568/

相关文章:

java - 使用调试信息反编译 C 代码?

c - OpenSSL 将现有分配的数组清零

delphi - 如何将 TByte 转换为 RawByteString?

delphi - RAD Studio IDE 键盘快捷键

c++ - 使用 Clang 编译 Boost 时出错

c - Windows : preventing assert() failures from opening the debug popup

c - 变量 "numbers"周围运行时检查失败 #2-stack

forms - 如何让表单始终位于最前面

delphi - 传递对 TObjectDictionary<TKey, TValue>.TValueEnumerator 的引用

c++ - 在没有 Midas.dll C++ 的情况下部署项目