I'm using Delphi to load a dll (that I created in Delphi XE-3) for the purposes of interfacing with some C code. My problem is figuring out why my arrays aren't being passed to the c functions - they're the only ones not to. The delphi file (simplified) looks like this:
program CallcCode
uses
SysUtils, Windows,
DLLUnit in 'DLLUnit.pas'; // Header conversion
var
DLLHandle: cardinal;
n: Integer;
A: TArray<Integer>;
result1: Integer;
begin
// Initialize each Array
SetLength(A,n);
A[0] = ...;
// Load the DLL (and confirm its loaded)
DLLhandle := LoadLibrary('dllname.dll');
if DLLhandle <> 0 then
begin
result1 := dll_func1(n,A); // A and B are not passed correctly
end
FreeLibrary(DLLHandle);
end.
I successfully "Trace into" dll_func1 the first time, entering DLLUnit, which has:
const
nameofDLL = 'dllname';
function dll_func1(n: Integer; A: TArray<Integer>): Integer; cdecl; external nameofDLL;
"Tracing-into" again, I arrive at the c file, which still has the correct n and DLLdefs values, but A (under the "Local Variables" heading) has become:
[-] A :(Aplha-Numeric)
..[0] 0 (0x00000000)
I know that I'm at least accessing the DLL (hopefully) correctly because other function calls work as they should and I am able to trace into the dll_func1.c file without a problem. I tried changing the function to
function dll_func1(n: Integer; A: PInteger): Integer; cdecl; external nameofDLL;
...
result1 := dll_func1(n,PInteger(A))
or
function dll_func1(n: Integer; A: PInteger): Integer; cdecl; external nameofDLL;
...
result1 := dll_func1(n,#A[0])
(using both TArray and array of Integer or A) but there is no change, which leaves me to believe this is related to a problem I'm not seeing. The whole thing compiles and runs, but result1 is incorrect because of the TArray failures. Any ideas on what is going wrong?
EDIT The function in C as:
int dll_func1(int n, int A [])
Your question contains two Delphi declarations for the external function. One of them uses TArray<T> as a parameter. That is completely wrong. Don't do that. You cannot use a Delphi dynamic array as an interop type. The reason being that TArray<T> is a complex managed type that can only be created and consumed by Delphi code.
You need to do as I do below, and as I explained in my answer to your previous question, and declare the array parameter as pointer to element type. For example, PInteger, PDouble, etc.
There's quite a lot of confusion here, and needless complexity. What you need is the simplest possible example that shows how to pass an array from your Delphi code to C code.
Here is is.
C code
//testarray.c
void printDouble(double d); // linker will resolve this from the Delphi code
void test(double *arr, int count)
{
int i;
for (i=0; i<count; i++)
{
printDouble(arr[i]);
}
}
Delphi code
program DelphiToC;
{$APPTYPE CONSOLE}
uses
Crtl;
procedure _printDouble(d: Double); cdecl;
begin
Writeln(d);
end;
procedure test(arr: PDouble; count: Integer); cdecl; external name '_test';
{$L testarray.obj}
var
arr: TArray<Double>;
begin
arr := TArray<Double>.Create(1.0, 2.0, 3.0, 42.0, 666.0);
test(PDouble(arr), Length(arr));
Readln;
end.
Compile the C code using, for example, the Borland C compiler like this:
bcc32 -c testarray.c
And the output is:
1.00000000000000E+0000
2.00000000000000E+0000
3.00000000000000E+0000
4.20000000000000E+0001
6.66000000000000E+0002
Note that I linked to the C code statically because that was easier for me. Nothing much changes if you put the C code in a DLL.
The conclusion is that the code I gave you in my answer to your previous, and that I repeat here, is correct. That approach succeeds in passing an array from Delphi code to C. It looks like your diagnostics and debugging is in error.
You are only inspecting A[0] so it's hardly surprising that you only see one value. If only you would look at A[1], A[2], ... , A[n-1] you would see that that all the values are being passed correctly. Or perhaps your debugging was carried out on the erroneous declaration of the external function that used TArray<T> as a parameter.
Related
I have a problem.
I can pass 2D array from Delphi to C++ DLL that created by CodeBlocks.
My function in C++ DLL is :
double __declspec(dllexport) __cdecl mainFunction(double** arr, int64 length)
And my Function In Delphi to call DLL is :
type
DynMatrixDouble = array of array of double;
.
.
function arr(X:DynMatrixDouble; Y: integer):double; stdcall; external 'Array_dll.dll' name 'mainFunction';
This code works well and the values of the variables are transferred between the program and the dll.But when I convert a function that has a 2D array in input in Matlab to DLL or /to C++ to make DLL with CodeBlocks, It's no longer like the code above, this is :
double calc_det(int64m_T n, const emxArray_real_T *arr)
It seems I should make 2D Double array Variable In "emxArray_real_T" Type in Delphi and pass to DLL.
I searched and reached the link below:
How to convert float[][] type array to "emxArray_real_T *x"
But I couldn't find the answer to my question.My question is how can define "emxArray_real_T" type in Delphi for 2D Array and send it to dll ?
Can anyone help me how to do it right?
Thanks.
I need to pass a structure from C to Ada in an Ada binding application. Thus, I have declared the structure in both Ada and C so that both the source sides can decipher the structure composition.
In C,
typedef struct {
int Status_Code;
int Error_Code;
} Edit_Result_Type;
In Ada,
type Edit_Result_Rec_Type is
record
Status : Integer;
Error_Code : Integer;
end record;
pragma Convention (Convention => C,
Entity => Edit_Result_Rec_Type);
for Edit_Result_Rec_Type use
record
Status at 0 range 0 .. 31;
Error_Code at 0 range 32 .. 63;
end record;
type Edit_Result_Rec_Type_Ptr is access all Edit_Result_Rec_Type;
When I am passing the structure from C to Ada through call by reference, I wanted to know:
Is it ok if I declare an "access all" vector type for the structure (as done above - Edit_Result_Rec_Type_Ptr) in Ada and use it directly as a formal parameter in Ada function. For Eg:
procedure Process_Data (Edit_Result_Ptr : in out Edit_Result_Rec_Type_Ptr) is
begin
Edit_Result_Ptr.Status := 1;
Edit_Result_Ptr.Error_Code := 0;
end Process_Data;
Is this approach FATAL? I know it is, just wanted to know "how" in depth!
Any other (better) approaches for passing through call by reference? I believe I can pass it as a "System.Address" parameter and do an "unchecked conversion" into Edit_Result_Rec_Type_Ptr in a local vector inside the Ada function and then read/write record members? Does this approach has any fatalities?
When interfacing Ada and C, you should really read the RM, Annex B.3, which says:
An Ada parameter of a record type T, of any mode, other than an in parameter of a type of convention C_Pass_By_Copy, is passed as a t* argument to a C function, where t is the C struct corresponding to the Ada type T.
So, in your procedure, just do:
procedure Process_Data (Edit_Result : in out Edit_Result_Rec_Type) is
begin
Edit_Result.Status := 1;
Edit_Result.Error_Code := 0;
end Process_Data;
and
pragma Export(C, Process_Data);
(or use the aspect, if Ada 2012)
That being said, you shouldn't use Integer in your record definition, Interfaces.C.int is the way to go:
type Edit_Result_Rec_Type is
record
Status : Interfaces.C.int;
Error_Code : Interfaces.C.int;
end record;
which will match the C int on your platform (assuming your C compiler is compatible with the Ada compiler)
As for your questions:
That would work, but why mess around with pointers?
No, that wouldn't work, access types (and access values) in Ada are not addresses. Converting a System.Address to an access value requires System.Address_To_Access_Conversions, but again, why mess around with pointers?
Below are few overloaded functions.
Try to guess which function of those would get called.
program Project2;
{$APPTYPE CONSOLE}
uses
Types, SysUtils;
procedure Some(const Buf); overload;
begin
Writeln('const-typeless')
end;
//procedure Some(var Buf); overload;
// begin
// Writeln('var-typeless')
// end;
//procedure Some(Buf :TByteDynArray); overload;
// begin
// Writeln('Byte dynamic array');
// end;
procedure Some(Buf :array of Byte); overload;
begin
Writeln('Byte open array');
end;
procedure Some(Buf :TArray<Byte>); overload;
begin
Writeln('TBytes AKA byte generic array');
end;
//procedure Some(Buf :TBytes); overload;
// begin
// Writeln('TBytes AKA byte generic array');
// end;
var p: pointer;
begin
try
{ TODO -oUser -cConsole Main : Insert code here }
WriteLn ('Calling overloaded procedure with Pointer parameter:');
Write(' * nil: '); p := nil; Some(p);
Write(' * garbage: '); p := Pointer(1); Some(p);
except
on E: Exception do
Writeln(E.ClassName, ': ', E.Message);
end;
ReadLn;
end.
In fact, the 2nd one is called and throws AV on its 2nd call.
Given old VCL pattern of using Pointer and Integer interchangeably (such as TList and TStrings.Objects and TWinControl.Tag) that may cause unexpected AVs on a rather regular code.
{$T+} does not change the behavior, so it was not that Delphi thinks ^Byte is Pointer.
However declaring p: PInteger; fixes it. Also the open-array variant is not called for pointer, and is treated/name-mangled differently than generic-array variant. Dynamic array is name-mangled differently from generic array, so both can be used, yet at the call site ambiguous overload error would happen if both uncommented. However, if to compile with generic array disabled and dinamic array uncommented - the same weird behavior happens.
Why does the compiler resolve to dynamic/generic array when parameter is a Pointer, and resolve to constant typeless when parameter is PInteger ?
Related: How does Delphi resolve overloaded functions with integral parameters?
Related: why two aliases to "array of string" treated differently?
Related: QC 108978
Code from: http://www.sql.ru/forum/actualthread.aspx?tid=970289
PS. Opened QC 109019
There's no documentation for this, so the best we can do is poke at the compiler and try to guess the reasoning behind its behaviour.
Now, a procedure with a typeless parameter can be passed any parameter, irrespective of its type. So, any sane overload resolution scheme has to consider the typeless parameter last, only when it has exhausted all other possible candidates. Otherwise it would always be chosen.
So with that, the behaviour can be explained.
When your parameter is of type, Pointer, that is assignment compatible with a dynamic array. Which means that the dynamic array overload can be selected.
When your parameter is any other pointer type, it is not assignment compatible with a dynamic array. And so the overload resolution falls back to the final possible candidate, the typeless parameter.
Ultimately this behaviour comes down to the fact that the compiler considers Pointer to be assignment compatible with any dynamic array. That this statement is fact is easy to confirm by experiment, however, I cannot find documentation for it.
I am translating some C header files to Delphi.
In those header files, two files share their struct with each other.
When I tried this in Delphi, it gave me circular reference error.
So I am currently writing both header's translation in single .pas file.
Is there any other way to get around this problem?
This is a small example.
The actual header files are more complicated :\
==File1.h==
struct a
{
int data;
}
int compare(struct a,struct b);
==File2.h==
struct b
{
int data;
}
int compare(struct A,struct b);
==File1.pas==
uses File2;
type
a = packed record
data: Integer;
end;
compare = function(d1: a; d2: b): Integer; cdecl;
==File2.pas==
uses File1;
type
b = packed record
data: Integer;
end;
compare = function(d1: a; d2: b): Integer; cdecl;
As Eugene points out circular interface unit references are not possible in Delphi. There are a few possible solutions:
Move the shared struct to a third unit and let the other two units include this unit.
Move one of the unit references to the implementation section (if this is possible).
Keep all in one file as you mention
Note that the fact that Delphi forces you to think about circular references is not a bad thing IMHO. In many cases, these circular references draw your attention to a flaw in your design.
Yes, you can't make the units reference each other in "interface" section. Move "uses" clause of one file to implementation section. This is a limitation of pascal.
I've run across this "Incompatible types" error in the comment below a few times, and never been happy with why this isn't directly supported in Delphi 2007:
program Project1; {$APPTYPE CONSOLE}
type TCharArray = array of Char;
procedure DoArray(Chars: array of Char);
begin
end;
function ReturnTCharArray: TCharArray;
var CharArray: TCharArray;
begin
Result := CharArray;
end;
begin
DoArray(ReturnTCharArray); // [DCC Error] Project1.dpr(18): E2010 Incompatible types: 'Array' and 'TCharArray'
end.
Shouldn't it be possible to make an array type "aliased" to another array type compatible with each other? Assuming I can't change the declaration of DoArray (it is part of a third party library), how do I write a function returning an array of char compatible with DoArray's param? The straightforward "function ReturnAChar: array of Char;" results in an "Identifier expected but 'ARRAY' found" error. I even tried changing the function returning the array to a procedure with a var "array of Char" pram, but that also doesn't allow setting the length of the "array of Char" param in the procedure ("Constant object cannot be passed as var parameter").
This may actually be a compiler bug (or a limitation that was never documented properly). I did some experimentation and found that you can pass a dynamic array (typed or not) to a procedure expecting an open array for almost every type... except Char and WideChar.
See Is a dynamic array of Char allowed when the parameter type is open array of Char? for a description of the problem and a possible work around.
When typed # operator is off the compiler does not check what you assign to a pointer, so you can call a procedure with wrong parameters:
program Project1; {$APPTYPE CONSOLE}
type TCharArray = array of Char;
procedure DoArray(Chars: array of Char);
begin
end;
function ReturnTCharArray: TCharArray;
var CharArray: TCharArray;
begin
Result := CharArray;
end;
type TFakeDoArray = procedure(Chars: TCharArray);
var
FakeDoArray: TFakeDoArray;
begin
FakeDoArray := #DoArray;
FakeDoArray(ReturnTCharArray);
end.
While the compiler won't complain, for the very reason 'Jeroen' indicates in his comment for Mason's answer, this will not work.
You can then try declaring your fake procedure compatible with one with an open array parameter:
program Project1; {$APPTYPE CONSOLE}
type TCharArray = array of Char;
procedure DoArray(Chars: array of Char);
begin
end;
function ReturnTCharArray: TCharArray;
var CharArray: TCharArray;
begin
Result := CharArray;
end;
type
TFakeDoArray = procedure(AnArray: Pointer; High: Integer);
var
FakeDoArray: TFakeDoArray;
Tmp: TCharArray;
begin
FakeDoArray := #DoArray;
Tmp := ReturnTCharArray;
FakeDoArray(Tmp, High(Tmp));
end.
Credits are due Rudy for his great article. And the relevant documentation (from Program Control):
An open-array parameter is passed as
two 32-bit values. The first value is
a pointer to the array data, and the
second value is one less than the
number of elements in the array.
You don't. Pascal handles array types by name, not by description, and always has. Why can't you change the declaration of DoArray? (And why was it written like that in the first place?)
One point that I didn't see mentioned yet is that TCharArray is a dynamic array type, while in
procedure DoArray(Chars: array of Char);
Chars is an open array parameter. There is no syntax to declare a dynamic array parameter. To have a dynamic array parameter, it needs to be declared as a type.
type
TMyDynArray = array of Integer;
procedure DoArray(Integers : TMyDynArray);