Delphi-to-C dll: Passing Arrays
Asked Answered
B

1

1

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 [])
Bryophyte answered 21/6, 2013 at 17:6 Comment(12)
You simply need to do what I told you in my answer to your previous question. Follow very closely what I wrote. What you have here is nothing like it. Well, the second half of the question is. The struct is passed wrong I suspect. Why don't you write a C function that takes a single array and try to pass that. Then get more ambitious.Blowup
I wrote PInteger in the function calls (unit file), defined them as TArray<Integer> in the program file (where I initialized them), and used solve(n, PInteger(Ap), PInteger(Ax)); only the arrays weren't passed to the function. What do you mean by the struct is passed incorrectly?Bryophyte
No. PInteger is a predefined type. Don't redefine it to something else. Delete all your declarations. It is declared as PInteger = ^IntegerBlowup
Oh, that's not what I meant by "adding Pinteger", I meant the function declarations had Ap and Ai set to PInteger type.Bryophyte
Anyway, you need to learn how to solve a problem. At the moment you are trying to work out how to pass an array from Delphi to C. The required a function with a single parameter. Don't even think about anything more complex until you fully understand that. Break the problem down into small pieces.Blowup
Edited the question. What I'm trying to figure out is what kind of error would lead to this inability for the arrays to be passed through, while everything else is. I've been trying to read links over the last few days, but most people had issues that were solved with your answer to my previous question, and others had to use the "ShareMem" (but I'm not using strings). I just want some idea of what is wrong with my interfacing. As for the "C array receiving it, it correctly", it's not because the first value of A should not be zero. Will make the other changes.Bryophyte
Next step is to remove all traces of TArray<T> at the interface boundary. Can you please do that for me? We've also got to the stage where I think you need to show complete code. Cut it down to a minimum and show both codes. Delphi and C.Blowup
I know that it should only be one value, that's not what I'm worried about. I wasn't trying to say your parameter passing was incorrect; if that's what you thought, then I'm sorry. I'll remove TArrays from the question, they aren't that in my code anymore. It was just an example change I made for testing.Bryophyte
If you can cut it all down, pass array parameter as PInteger, and show complete code for both side of the interface (using copy paste so that it is the code that you are actually running) it will be very easy and simple to fix, I am quite sure.Blowup
The array issue is settled, I cut/rewrote/swapped some things. There are some other issues (result1 is still not correct), but since everything is passing okay, I'll just debug function-by-function from here. I really, really appreciate putting up with me, thank you so much (and sorry). I'm not using the CSparse functions, but one of his other algorithms. I'm probably going to delete this question, it kind of circled around the answer to other SO questions (including my previous one).Bryophyte
You cannot delete a question that has an answer with an upvote.Blowup
Noted. Thank you again, will accept.Bryophyte
B
4

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.

Blowup answered 21/6, 2013 at 18:31 Comment(2)
I know that it's something wrong on my side, and trying out the "different" ways of passing the arrays in delphi code showed me that it isn't the array itself that's the problem (since none of them work). The problem is I'm new to dlls and interfacing and not sure what to test next, which is why I was asking for some other issues I could troubleshoot. The minimal examples (both this one and the one I did) work (no surprise there, really). As for the struct, that was a typo in the question (I fixed it); it has always worked in my code, it's still only the arrays.Bryophyte
So, what are you looking for here? I've shown you how to pass an array, twice now. I'm guessing here, but my guess is that your array is passed fine but you don't know how to display it on the C side of the fence. You neglected to show the C side of your interface. What type are your array parameters? Are they int*. Naturally if you look at *A, say, then you only see one value. But what about A[0], A[1] etc. Again, please remove complexity. The code in your question should work with a single array. Get rid of the struct, it is just distracting. Simplify.Blowup

© 2022 - 2024 — McMap. All rights reserved.