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
Related
I want to consume c obj files in delphi xe3.
When linked obj files, shows this error:
`[dcc32 Error] Unit1.pas(149): E2065 Unsatisfied forward or external declaration: '_exit'`
can i implement _exit function?
Yes you can indeed do this. Typically you will link the .obj file to a single unit in your project. Implement the exit function in that unit and the Delphi linker will find it.
....
implementation
....
{$LINK foo.obj}
procedure _exit(status: Integer); cdecl;
begin
// your implementation goes here
end;
As I've illustrated this you place the function in the implementation section of the unit. It does not need to be visible externally to the unit.
You might have multiple different units that link to C objects, in which case you can place your C runtime functions, like exit, in a single unit, and use that from each other unit that links to C objects. In that scenario then you need to expose each function in the interface section so that the linker can see the function.
In a new Win32 project, I have the following Delphi function:
procedure SetValue(value1, value2 : Extended);
begin
end;
In the same project but from a C++ unit, I call this function:
SetValue(10, 40);
When I check value1 when compiling with BCC32C (CLang), I get 1.68132090507504896E-4932, that is not correct.
Compiling with BCC32 (classic), I get 10.
The second parameter is 40 in both cases.
It seems that there is a problem with Extended values and the parameter stack loading.
I use RAD Studio 10.1 Berlin.
How can I solve this?
UPDATE
I did not include the declaration because the hpp is created automatically when compiling. In any case, the declaration is:
extern DELPHI_PACKAGE void __fastcall SetValue(System::Extended value1, System::Extended value2);
To replicate the project:
1-Create a C++ project in Rad Studio
2-Add a Delphi unit with the above SetValue function
3-From C++ unit, add the hpp header with #include and call SetValue
It is all.
I need to use Extended type. I am using an external Delphi library, so I cannot change the types. The above code is a simplification of the problem. In reality, the problem is calling a function of this library that uses Extended in the parameters. Extended is a native type in Delphi but in C++ it is mapped as long double, 10 bytes (for Win32).
This seems to be an error in the BCC32C compiler. I guess it was not extended properly to handle Extended as Delphi needs it.
If you look at the CPU window, then the BCC32 compiler generates:
File5.cpp.14: SetVal(10.0L, 40.0L);
00401B8F 6802400000 push $00004002
00401B94 68000000A0 push $a0000000
00401B99 6A00 push $00
00401B9B 6804400000 push $00004004
00401BA0 68000000A0 push $a0000000
00401BA5 6A00 push $00
00401BA7 E80C000000 call Unit12::SetVal(long double,long double)
and that is correct. It first pushes 10, then 40 in Extended format. Note that each Extended occupies 12 byte on the stack.
But now look at the output for the BCC32C compiler:
File5.cpp.14: SetVal(10.0L, 40.0L);
00401B5D 89E0 mov eax,esp
00401B5F D90554F14E00 fld dword ptr [$004ef154]
00401B65 DB38 fstp tbyte ptr [eax]
00401B67 D90558F14E00 fld dword ptr [$004ef158]
00401B6D DB780A fstp tbyte ptr [eax+$0a]
00401B70 E81F000000 call Unit12::SetVal(long double,long double)
00401B75 83EC18 sub esp,$18
It first reads the 32 single precision float 40 and stores it as Extended at [ESP]. So far, so good. But then it reads in the next 32 bit single precision float 10 (still OK) but then stores it at [ESP+$0A], which is clearly wrong (for Delphi)! It should be stored at [ESP+$0C]! That is why the first value, which is read by the Pascal function at [ESP+$0C], but which is stored by BCC32C at [ESP+$0A], is wrong.
So this seems to be a bug. Reported as https://quality.embarcadero.com/browse/RSP-15737
Note that this is the normal way BCC32C pushes and expects such values. In a C++ function in the same module, i.e. compiled with BCC32C too, this works nicely:
void __fastcall Bla(long double a, long double b)
{
printf("%Lf %Lf\n", a, b);
}
But Delphi expects a 10 byte Extended to occupy 12 bytes on the stack, not 10 bytes as BCC32C does.
Weirdly enough, if the function to be called is not a Delphi __fastcall function, but a normal C++ (cdecl) function, the BCC32C compiler will store the Extendeds (long doubles) in [ESP+$0C] and [ESP] respectively.
Workarounds
As David Heffernan commented, you can pass multiple extendeds in a record. Alternatively, you could pass them as var parameters. In both cases, it is not as simple as calling SetVal(10.0, 40.0);.
How can I use this function in C#?
function CheckCard (pPortID:LongInt;pReaderID:LongInt;pTimeout:LongInt): PChar;
This function included the dll.
I can try this way:
[DllImport("..\\RFID_107_485.dll", CharSet = CharSet.Auto,
CallingConvention = CallingConvention.ThisCall)]
public static extern char CheckCard(int pccPortID, int pccdReaderID, int pccTimeout);
char pccCheckCard = CheckCard(3, 129, 1000);
Console.WriteLine(pccCheckCard);
but i don't get a true answer...
please help me ? :)
There are many problems here. This is what I can see:
The Delphi code as written uses the Delphi register calling convention. That is only accessible from Delphi code and cannot be called by a p/invoke method. However, it is possible that you have omitted the calling convention from the code and it is in fact stdcall.
Your p/invoke uses CallingConvention.ThisCall which certainly does not match any Delphi function. That calling convention is not supported by Delphi.
You mistranslate PChar, a pointer to null-terminated array of characters as char, a single UTF-16 character.
The Delphi code looks suspicious. The function returns PChar. Well, who is responsible for deallocating the string that is returned. I would not be surprised if the Delphi code was returning a pointer to a string variable that is destroyed when the function returns, a very common error.
You refer to the DLL using a relative path. That is very risky because you cannot easily control whether or not the DLL will be found. Place the DLL in the same directory as the executable, and specify just the DLL's file name.
There is no error checking to be seen.
A variant that might work could look like this:
Delphi
function CheckCard(pPortID: LongInt; pReaderID: LongInt; pTimeout: LongInt): PChar;
stdcall;
C#
[DllImport("RFID_107_485.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr CheckCard(int pccPortID, int pccdReaderID, int pccTimeout);
....
IntPtr pccCheckCard = CheckCard(3, 129, 1000);
// check pccCheckCard for errors, presumably IntPtr.Zero indicates an error
// assuming ANSI text
string strCheckCard = Marshal.PtrToStringAnsi(pccCheckCard);
// or if the Delphi code returns UTF-16 text
string strCheckCard = Marshal.PtrToStringUni(pccCheckCard);
This leaves unresolved how to deallocate the pointer returned. You'll have to consult your documentation for the function to find that out. The question contains insufficient information.
I have Delphi 2010 built DLL with two methods:
function Foo1(a, b: Integer):PChar; export; stdcall;
function Foo2(a, b, c:Integer):PChar; export; stdcall;
exports Foo1, Foo2;
Each of them returns Result := PChar('Test') .
My C++\CLI code
in header
typedef const wchar_t* (*pFUNC1)(int a, int b);
pFUNC1 TestFoo1;
typedef const wchar_t* (*pFUNC2)(int a, int b, int c);
pFUNC2 TestFoo2;
Initialize by LoadLibrary and GetProcAddress functions.
Usage: TestFoo1(0,0) and TestFoo2(0,0,0);
Both works in Release mode.
But in Debug mode Foo2 is being aborted.
Please advise what is wrong.
Most likely you have calling convention mismatch. Change the stdcall in the Delphi to cdecl to match your C++/CLI code.
As an aside, you will need to be careful with the lifetime of your strings if ever you attempt to return a value from the DLL that is not a literal stored in read-only memory in the data segment. But that's not the problem here because PChar('Test') has the same lifetime as the DLL.
I am using Delphi 2010. Is it possible to tell Delphi to not generate a prologue for a function? I'm writing some pure assembly functions like this:
procedure SomeAssembly; stdcall;
begin
asm
...
end;
end;
and I would like to tell Delphi not to generate a prologue and epilogue for this function, like C++'s __declspec(naked) feature.
And so no one wastes their time, I don't need help getting these functions to work with the prologue; I can already do that. It's just a large inconvenience and will make maintenance an huge hassle. I'll have to manually inspect the prologues generated by the compiler to see their length, and if that changes, my program will crash.
I also know I can write the function as a series of bytes in a byte array, but that would be even worse than having to go find the length of Delphi's prologue.
Delphi doesn't generate prologues or epilogues for functions having no arguments and declared with the register calling convention. If you want functions without prologues, declare them as zero-argument, register-calling-convention functions. Also, skip the begin-end block and go straight into assembly.
procedure SomeAssembly; // register; (implied)
asm
// ...
end;
Since you're effectively lying about the nature of the functions, calling them may be tricky. If you've implemented a function as though it received parameters and used a different calling convention, then you'll have to make sure the compiler knows about that at the call site. To do that, declare a function pointer that reflects the "real" type of your function instead of the declared type. For example, if your function is really a two-argument stdcall function, declare something like this:
type
TSomeAssemblyFunc = function (Arg1: Integer; Arg2: PAnsiChar): Boolean; stdcall;
var
SomeAssemblyProc: TSomeAssemblyProc;
Now, assign that variable so it points at your function:
SomeAssemblyProc := TSomeAssemblyProc(#SomeAssembly);
if SomeAssembly(2, 'foo') then ...
In addition to skipping the prologue and epilogue, the compiler will generate the incorrect RET instruction for this function (because of the different calling convention), so you'll have to make sure you say ret 8 in your code instead of letting the compiler's default ret instruction occur.
Finding the length of Delphi's prologue is trivial, if you have a working debugger:
Set a breakpoint at the start of the function.
Call the function.
When the debugger stops at the breakpoint, switch to the CPU view.
Look at the instructions that make up the prologue.
Count the bytes displayed beside those instructions.
According to the this embarcadero docwiki you can skip the surrounding begin and end and the compiler will skip some of it's stuff. But if you really want pure assembler, why not put your function into a separate assembler file, assemble it with tasm (the exe is named tasm32) and link to it. You'll then use the assembler directive in the delphi code.
Doesn't
procedure SomeAssembly; stdcall;
asm
...
end;
do the trick?