How to link statically a Delphi procedure in a C++Builder project - delphi

I want to link statically a Delphi function from a C++Builder source. I added the .pas file to the C++Builder project. I tried the following declarations:
In Delphi:
procedure SayHi; stdcall;
In C++Builder:
extern "C" {
void __stdcall SayHi();
}
All modules compile fine, but when I link it I get the following error:
[ilink64 Error] Error: Unresolved external 'SayHi' referenced from C:\DEV\PACKSWAP\WIN64\DEBUG\MAIN.O
What am I missing?

When you compile a .pas file in a C++Builder project, an .hpp header file is automatically created that you can then #include in your C++ code where needed. There is no need to declare the function manually.
The linker error is due to your use of extern "C" on the C++ side. That is affecting how the C++ compiler emits name mangling/decoratation for that function, so it does not match the name mangling/decoration that the Delphi compiler emits. That is why the linker cannot find the function implementation. Remove the extern "C" and then both compilers should emit the same name mangling/decoration to allow the linker to match them up.

Related

C++ Builder 11.2 assigning C++ method to Delphi event

We have an older project with mixed C++/Delphi code. We are trying to compile it with the latest C++Builder.
This code was OK with RAD Studio 10.1:
NewObject->OnDraw = (TDrawEvent)&(CObj->MyVRMLShapeDraw);
Edit: actual declarations:
TDrawEvent = procedure (Sender: TGLObject; Face: GLenum; Draft: boolean; State: TGLDrawState ) of object;
private
FOnDraw: TDrawEvent;
published
property OnDraw: TDrawEvent read FOnDraw write FOnDraw;
class TManager : public TBaseObject {
public:
void __fastcall MyVRMLShapeDraw(TGLObject *Sender, DWORD Face, bool Draft, char DrawState);
Where NewObject is a class instance from a Delphi class, TDrawEvent is a Delphi event method type (TMethod basically with some parameters), CObj is our C++ object, MyDraw is its C++ method.
TGLDrawState is an enum with a few values.
The latest C++Builder compiler drops this error message:
[bcc64 Error] xy.cpp(1021): cannot compile this unexpected cast lvalue yet
How can we solve this? Is it possible at all to call a C++ method from Delphi code in the usual event handler method style?
EDIT: I solved it with wrapping the call in a global function, but if someone knows the elegant syntax of this, it would be appreciated.
EDIT: My mistake - the newer 11.2 compiler required using unsigned instead of DWORD, and glclasses::TGLDrawState instead of char in the C++ method parameter list. Older compilers accepted less strict type matching.

Delphi IOS linker error library not found

I'm trying to use a third party framework, that needs a library, in Delphi IOS. To get Delphi to link it in i've entered the following statement:
procedure StubProc1; cdecl; external 'AerServSDK.a' dependency 'libxml2.2';
When i build it i get the following error:
[DCC Error] E2597 ld: library not found for -llibxml2.2
The library is in the usr\lib directory, and it doesn't matter whitch library i try. Apparently some searchpath needs to be updated, but where and how??
The solution is, to omit the "lib" part of the library name. So it's:
procedure StubProc1; cdecl; external 'AerServSDK.a' dependency 'xml2.2';
You must add the path to the library in the source path of the project (like you do with .pas file).
add theses 2 rows :
procedure StubProc1; cdecl; external 'AerServSDK.a';
procedure StubProc2; cdecl; external 'libxml2.2';
AerServSDK.a and libxml2.2 (ie: the file libxml2.2 without any extension) must be in your source path

The initialization part is not called

I'm maintaining the VirtualTreeView component for Delphi and C++Builder. With Delphi everything is okay but when I compile the packages with the C++Builder the code in the initialization part in the Delphi units is not called. Any ideas?
When a Delphi unit's initialization/finalization sections are not being called in a C++Builder project, it usually means the Delphi unit is not being linked into the final executable, typically because the C++ code is not directly referencing any code in the unit, so it gets optimized out. C++Builder is a bit more aggressive about removing unused code than Delphi is. In Delphi, simply adding a unit to a uses clause forces that unit to be linked to. That is not the case in C++. #includeing a Delphi unit's .hpp file in C++ code is not enough to guarantee the unit is linked to, if the C++ code does not use anything from the .hpp file.
Indy ran into this problem in several of its units, most notably IdAllAuthentications, IdAllFTPListParsers, and IdAllHeaderCoders. These units all contain only initialization/finalization code, no interface code, so their generated .hpp files were basically empty. To force linkage, I had to add {$HPPEMIT} statements to the interface section to output #pragma link statements in the generated .hpp files. For example:
unit IdAllAuthentications;
interface
{
Note that this unit is simply for listing ALL Authentications in Indy.
The user could then add this unit to a uses clause in their program and
have all Authentications linked into their program.
ABSOLUTELY NO CODE is permitted in this unit.
}
{$I IdCompilerDefines.inc}
// RLebeau 2/14/09: this forces C++Builder to link to this unit so
// the units can register themselves correctly at program startup...
{$IFDEF HAS_DIRECTIVE_HPPEMIT_LINKUNIT}
{$HPPEMIT LINKUNIT}
{$ELSE}
{$HPPEMIT '#pragma link "IdAllAuthentications"'}
{$ENDIF}
implementation
// uses units that self-register in their initialization sections ...
end.
{$HPPEMIT LINKUNIT} was introduced in XE5 Update 2, to help with linking units that use unit-scope names:
New: You can now use HPPEMIT Delphi compiler directives for linking and generating C++ namespace declarations.
...
{$HPPEMIT LINKUNIT} replaces #pragma link for the iOS device target platform.
For more information, see HPPEMIT.
For C++ applications, {$HPPEMIT LINKUNIT} replaces #pragma link on mobile platforms.
The Delphi run time has units that must be linked in order to enable some functionality. In C++, auto-linking was previously achieved using the following directive:
{$HPPEMIT '#pragma link "<unitname>"'}
Now you should use the following directive instead:
{$HPPEMIT LINKUNIT}
LINKUNIT generates a #pragma link statement that references the calling unit using the correct decorated/namespaced unit name.

How to properly statically link C with Delphi?

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;
}
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

Why doesn't the new compiler recognize "NULL" in this old code?

I just downloaded the ADSI and it seems to be that it is not compatible with Delphi Embarcadero XE4.
When I try to compile one of the examples, I get this error:
[dcc32 Error] adshlp.pas(128): E2003 Undeclared identifier: 'NULL'
And this is the line:
varArr := NULL;
What's wrong?
Null used to be declared in the System unit, so it was available globally. In Delphi 6, all Variant-related code moved out of that unit and into the new Variants unit. Since Null is a function that returns a Variant, Null was included in the move, so it is no longer available implicitly.
To fix the old code, simply add Variants to your uses clause in any unit that needs it:
uses ..., Variants;

Resources