How to use Crtl in a Delphi unit in a C++Builder project? (or link to C++Builder C runtime library)
I have a Delphi unit that is statically linking a C .obj file using the {$L xxx}
directive. The C file is compiled with C++Builder's command line compiler. To satisfy the C file's runtime library dependencies (_assert, memmove, etc), I'm i开发者_运维知识库ncluding the crtl
unit Allen Bauer mentioned 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.
If I compile that unit in a Delphi project (.dproj) everthing works correctly.
If I compile that unit in a C++Builder project (.cbproj) it fails with the error:
[ILINK32 Error] Fatal: Unable to open file 'CRTL.OBJ'
And indeed, there isn't a crtl.obj
file in the RAD Studio install folder. There is a .dcu, but no .pas. Trying to add crtdbg
to the uses clause (the C header where _assert is defined) gives an error that it can't find crtdbg.dcu
.
If I remove the uses clause, it instead fails with errors that __assert
and _memmove
aren't found.
So, in a Delphi unit in a C++Builder project, how can I export functions from the C runtime library so they're available for linking?
I'm already aware of Rudy Velthuis's article. I'd like to avoid manually writing Delphi wrappers if possible, since I don't need them in Delphi, and C++Builder must already include the necessary functions.
Edit
For anyone who wants to play along at home, the code is available in Abbrevia's Subversion repository at https://tpabbrevia.svn.sourceforge.net/svnroot/tpabbrevia/trunk. I've taken David Heffernan's advice and added a "AbCrtl.pas" unit that mimics crtl.dcu when compiled in C++Builder. That got the PPMd support working, but the Lzma and WavPack libraries both fail with link errors:
[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, all of them are declared correctly, and the _beginthreadex one is actually declared in AbLzma.pas, so it's used by the pure Delphi compile as well.
To see it yourself, just download the trunk (or just the "source" and "packages" directories), disable the {$IFDEF BCB} block at the bottom of AbDefine.inc, and try to compile the C++Builder "Abbrevia.cbproj" project.
My take on this is that you only need the Delphi unit in the Delphi version of the project.
In the C++ builder version you just compile and link foolib.c as if it was a C file (it is!) In the Delphi version of the program you create the .obj with bcc32, use ctrl etc. as described.
Why do you want to wrap it up a C library up in a Delphi wrapper to be consumed in C++?
EDIT 1
You've added clarifications in the comments.
Another option to consider would be to avoid crtl and implement the missing functions in FooWrapper. I do it that way rather than using crtl because that gives me more control and I understand what is being called. For example, I don't want any calls to printf()
leaking into my GUI app or my DLL.
This might be an attractive option if you are only missing a handful of functions. Often the neatest way to get them is to link them in from msvcrt.dll which is a standard system component these days. Of course it seems a bit heavyweight to link in msvcrt.dll just to get at memset()
, memcpy()
etc.
How many missing functions are there when you compile the Delphi unit without crtl?
EDIT 2
I'm adding this to the answer to show some code. From my own code base I offer this:
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. *)
For ftol
I link in ftol.obj which I presume I extracted from one of the lib files in the BCC55 compiler that I use.
I think strncmp
should be pretty routine to implement in plain Pascal.
sprintf
is more difficult in full generality, but you might find that it is only used for something trivial like integer to string. In which case you could fudge the C code to call a routine dedicated for that and implement it trivially.
To be honest with you, I think 'msvcrt.dll' looks pretty attractive!
EDIT 3
Did I speak to soon? You can pull a perfectly serviceable sprintf
out of user32.dll which almost all processes have loaded anyway. Make sure you pick out wsprintfA
if it's an ANSI version you need.
EDIT 4
I notice _beginthreadex
. You say this is defined in a different Delphi unit. In order to get the compiler to see it you need to redeclare it in AbCtrl.pas and from there call the real version in AbLzma.pas.
When you include a .obj in a Delphi .pas file the compiler has to be able to resolve all the references in the .obj file from within the Delphi unit which links to the .obj. This whole game is dealt with by the compiler rather than the linker.
Sometimes you get tangled in knots with the order in which you include the .obj files and the solution is to use forward declarations, but that's another story.
In this case, the functions you are interested are assumed to be available directly from the C RTL, so faking out the linker with a dummy (empty) obj file should work as it will satisfy the linker looking for the obj file which Delphi told it you need, but still find the functions in the RTL.
Late, but more complete: crtl.dcu works without problems from D2005 up until XE2.
For D6 and D7 there is a dependency on midaslib.dcu. Well, not really, the dcu is distributed with a dirty uses clause.
For D6 and D7 you should create an EMPTY midaslib.pas surrogate, like:
unit midaslib;
interface
implementation
end.
Now you can use crtl.dcu without the internal errors!
精彩评论