How to properly statically link C with Delphi?
Asked Answered
E

1

7

How can I properly statically link C with Delphi? Delphi spits out an error saying:

[dcc32 Error] Project1.dpr(15): E2065 Unsatisfied forward or external declaration: 'Test'

The C compiler is Visual C++ with COFF object file.

Delphi:

program Project1;

{$L C:\Source.obj}

function Test(): Integer; cdecl; external;

begin
  WriteLn(Test);
end.

C:

extern "C" int __cdecl Test()
{
    return 12;
}

int main()
{
    return 0;
}
Extradition answered 21/10, 2015 at 14:24 Comment(7)
Delphi x64 does support COFF, but does Delphi x86 ?Coccidiosis
@Arioch'The This is 32 bit so yes.Extradition
@Arioch'The It supports some COFF better than others. It's not so hot on mingw COFFRosemarierosemary
@DavidHeffernan It works with ICC which is compatible with MSVC. All I need.Extradition
@Extradition I've had success with simple things using mingw, but when my source code gets a bit more complex, I had problems. Anyway, good luck!!Rosemarierosemary
@DavidHeffernan Interesting can you mention how to do it with MingW I am sure it will help people. Any special treatment is needed? :)Extradition
I can't remember. It's been an age since I tried. If I recall, I was concentrating on x64, since on x86 I still use bcc32 for my sins. I had trouble getting mingw to spit out .obj files that Delphi could handle. On the other hand, the ones that msvc emitted were generally consumed just fine. There are loads of wrinkles and nuances in this area though. Funny stack probing, order of function declarations, all sorts of trickery and wizardry is needed to get complex libraries to link. Anyway, if you get stuck, you can ask here and get good help I am sure.Rosemarierosemary
R
8

It depends on the name decoration used by whichever C compiler you are using. For example, the 32 bit bcc32 compiler will decorate Test as _Test. So the Delphi code to link to it should be:

function Test(): Integer; cdecl; external name '_Test';

But the decoration does vary between compilers, and you did not say which compiler you are using. If the code above doesn't help, then you should use your C compiler's tools to dump the obj file and inspect the names of the functions within.

Another problem is that you are actually using a C++ compiler rather than a C compiler. That can be discerned from your use of

extern "C" 

which is not valid C. You should remove this and switch to a C compiler. Changing the extension from .cpp to .c will usually suffice to persuade the compiler to treat the code as C.

If you start calling functions from the C standard library, such as malloc and friends, then you will want to add the System.Win.Crtl unit to your Delphi code's uses clause.

Note also that you need not, and indeed probably should not, implement a main function in your C code. If you want to compiler your C functions into a separate C program then place the functions in separate source files, apart from the source file that contains the main function. That way you can compile the source files into objects. You can link them into either a C program, or your Delphi code. But you don't need to carry around a main function in your Delphi program that you don't call.

In C the correct signature for a parameterless main is

int main(void)

Similarly, your other C function should have this signature:

int __cdecl Test(void)

Of course, the __cdecl is the default, so we are perfectly at liberty to omit it:

int Test(void)

Let's put it all together:

C

int Test(void)
{
    return 12;
}

Important that you compile with a C compiler and do not compile as C++. If your compile is, as you now state in an edit, MSVC, the command line would be:

cl /c source.c

Delphi

{$APPTYPE CONSOLE}

{$L Source.obj}

function Test: Integer; cdecl; external name '_Test';

begin
  WriteLn(Test);
end.

Output

12
Rosemarierosemary answered 21/10, 2015 at 14:28 Comment(11)
By the way why is the "_" needed? Does the C compiler add these?Extradition
Yeah, it's known as function name decoration. I'm not sure what the rules are, or why it's done. Historical reasons. stdcall decoration includes an @XX where XX is the size of the stack to be cleaned up. Anyway, it just is what it is! On x64, your function would not be decorated! Go figure.Rosemarierosemary
So, for code that needs to target x86 and x64 you typically end up with something like this: const PU = {$IFDEF CPUX86}'_'{$ELSE}''{$ENDIF}; function foo; external name PU+'foo'; Rosemarierosemary
Very helpfull insight. Thanks!Extradition
Yeah, you've managed to pick out one of my favourite subjects, and one that I've had to dig deep into through my work, so I'll happily rabbit on about this topic as long as anyone cares to listen!! ;-)Rosemarierosemary
I just tried it with MingW. It works with this gcc -m64 -O3 -c test.c -o test.oExtradition
I was targeting x64. And we are looking at a very simple example. It gets more tricky with real world code.Rosemarierosemary
Works both ways! Finally. I am only trying to port some basic algorithms to C with SSE.Extradition
Well, good luck. It's quite possible that thinks have changed since I last tried. It's also quite possible that differences in the code being compiled are important.Rosemarierosemary
I don't know. And it's decoration rather than mangling. Mangling is a form of decoration done by C++ compilers.Rosemarierosemary
Yes I have to say FPC has better support for GCC. I've managed to link OpenSSL for it sometime ago.Extradition

© 2022 - 2024 — McMap. All rights reserved.