简体   繁体   中英

Delphi DLL function returning a record with open arrays to C# application

So I have the following record structure in a Delphi DLL that I am calling from C#.

  TItemPic = record
    x, y : Integer;
    id : Integer;
    Hue : Integer;
    Page : Integer;
    ElemNum : Integer;
  end;

  TItemToken =  record
    id : Cardinal;
    Arguments : String;
    ElemNum : Integer;
  end;


  TItemType = record
    ID : Integer;
    Hue : Integer;
    ItemPics : array of TItemPic;
    ItemToken : array of TItemToken;
end;

And function :

function GetItemInfo(index : Word) : TItemType;

Now I have the following translated structs in C#:

    public struct TItemPic
    {
        public int X { get; set; }
        public int Y { get; set; }
        public int ID { get; set; }
        public int Hue { get; set; }
        public int Page { get; set; }
        public int ElemNum { get; set; }
    }

    public struct TItemToken
    {
        public uint ID  { get; set; }
        public TPCharStr Arguments { get; set; }
        public int ElemNum { get; set; }
    }

    public struct TItemType
    {
        public uint ID { get; set; }
        public int Hue { get; set; }
        public TItemPic[] ItemPics { get; set; }
        public TItemToken[] ItemToken { get; set; }
    }


[DllImport("mydelphi.dll", EntryPoint = "GetItemInfo", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Unicode)]
internal static extern TItemType GetItemInfo(ushort index);

The goal being something like :

TItemType myItem = GetItemInfo(1);

I am unsure how to go about handling the open arrays in the TItemType record ItemPics : array of TItemPic; and ItemToken : array of TItemToken;

Reading some of David Heffernan's answers, I believe I probably should be declaring these as IntPtr then copying the data into an array -- however I'm not sure how to get the number of elements stored. He states that Delphi open arrays store a reference count and array size at a negative offset to the pointer that is passed ... but I am unsure how to retrieve this information and convert it :-)

For a start, those are dynamic arrays rather than open arrays.

You cannot use managed Delphi types for interop. So, string and dynamic arrays cannot be used here.

For strings, you need to use PWideChar on the Delphi side, pointer to null terminated wide character array. On the C# side use string if passing text to Delphi, StringBuilder if the text goes in the other direction.

For arrays you need to declare the parameter/field as pointer to element type, and then walk the array using pointer arithmetic. The marshaller will pass by value a pointer to the first element in the array.

You need to allocate all buffers in the calling C# code. If you need to work out how big the buffers need to be, provide a function that gives that information.

Your other problem is that you must make the two function calling conventions match. Choose stdcall, which is the default on the C# side. And you cannot return a record from your Delphi DLL since Delphi uses a non-standard ABI for complex return types. You'll have to pass your records as var parameters.

I suspect that the marshaller won't handle a struct containing an array of non-blittable structs, which is what you'd have even if you followed the advice above. Personally I'd probably ask for one struct at a time and avoid complex nesting. In outline, there would be a function named GetItemCount that returned the number of items. And one called GetItem that returned the i-th item, via a var parameter, of course.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM