3F / DllExport

.NET DllExport with .NET Core support (aka 3F/DllExport aka DllExport.bat)
MIT License
940 stars 131 forks source link

how to export a struct array in a struct? #116

Closed ymbus closed 4 years ago

ymbus commented 4 years ago

I want to export a structure from C# to an unmanaged delphi application, that I can not change.

The structure in unmanaged looks like this:

TDCRecEx = record
    Name: PChar; // name of item
    value: PChar; // value of item
        Socket: Integer; // socket to send to
        quality: Integer; // quality of value
end;
PDCRecEx = ^TDCRecEx;

TDCRecList = record
    Len : Integer;                     // count of pointer
    Ptr : Array of PDCRecEx;      // pointer array to record
end;
PDCRecList = ^TDCRecList;

The unmanaged code calls the exported C# function with a pointer to the TDCRecList struct -> PDCRecList: definition:

dRecList: PDCRecList;
procedure getRecList(dRL:PDCRecList); stdcall; external 'ClassLibrary1.dll';

call: getArrayStruct(dRecList); //pointer will be 'nil' -> a nullpointer

I did not manage to export the structure in C#. In delphi dRecList will be null. I am using an array with size of only 1 at this point. What I have is this:

private struct Rec
{
    public IntPtr name;
    public IntPtr value;
    public int socket;
    public int qual;
}

private struct RecList
{
    public int len;
    public IntPtr records;  //will point to an array of Intptr
}

My export function looks like this:

[DllExport]
public static void getRecList(IntPtr pdRL)
{
    Rec rec;
    rec.name = new UnmanagedString("Tag1", UnmanagedString.SType.Ansi);
    rec.value = new UnmanagedString("value 100", UnmanagedString.SType.Ansi);
    rec.socket = 123456;
    rec.qual = 192;

    RecList recList;
    recList.len = 1;

    //create a pointer array, to point to from recList.records
    //and add an unmanaged structure to it
    IntPtr[] recArr = new IntPtr[1];
    recArr[0] = new UnmanagedStructure(rec);

    //allocation for recList.records
    int size = Marshal.SizeOf(rec);
    recList.records = Marshal.AllocHGlobal(size);

    //point to the pointer array from recList.records
    Marshal.Copy(recArr, 0, recList.records, 1);

    //make an unmanaged structure for recList
    pdRL = new UnmanagedStructure(recList);
}

As said, in delphi there will be a nullpointer and I don't know why.

Can you please help me? How would you do it?

3F commented 4 years ago

Your expected region does not contain initialized data due to other address in heap. Well, looks like

................. (X) <--- addr1
 addr2 --->

just encapsulate this inside any immutable addr where your caller can get related data via the same pointer, or just use new allocated addr. Also don't forget to release all allocated resources when it will no longer be necessary to use

edited: something wrong with github email reply