How to use Crtl in a Delphi unit in a C++Builder project? (or link to C++Builder C runtime library)

StackOverflow https://stackoverflow.com/questions/8777651

  •  15-04-2021
  •  | 
  •  

سؤال

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 including 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!

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top