Using Delphi Arrays and Strings in C #

I am trying to call a method created in Delphi as follows:

function _Func1(arrParams: array of TParams): Integer;stdcall; type TParams = record Type: int; Name: string; Amount : Real; end; 

My code is:

 [DllImport("some.dll", EntryPoint = "_Func1", CallingConvention = CallingConvention.StdCall)] public static extern int Func( [MarshalAs(UnmanagedType.LPArray, ArraySubType=UnmanagedType.Struct)] TParams[] arrParams) 

And structure:

 [StructLayoutAttribute(LayoutKind.Sequential, CharSet = CharSet.Ansi)] public struct TParams { public int Type; [MarshalAs(UnmanagedType.AnsiBStr)] public string Name; public double Amount; } 

When I call this method, I get the error: Unable to display the "Name" field of type "TParams": Invalid combination of managed / unmanaged types (String fields must be paired with LPStr, LPWStr, BStr or ByValTStr).

However, none of these combinations work, since Delphi strings have a prefix of their length, and this is certainly Ansi (I tried this with other string parameters). Does anyone know how to solve this?

+4
source share
2 answers

There are two main problems: using open arrays and using a Delphi string .

Open arrays

Delphi open arrays are implemented by passing a pointer to the first element of the array and an additional parameter indicating the index of the last high element in Delphi terminology. See this answer for more information.

Delphi Strings

A C # router cannot interact with a Delphi string. Delphi strings are private types that can only be used inside the Delphi module. Instead, you should use a string with a terminating zero, PAnsiChar .


Putting it all together, you can write it like this:

Delphi

 type TParams = record _Type: Integer;//Type is a reserved word in Delphi Name: PAnsiChar; Amount: Double; end; function Func(const arrParams: array of TParams): Integer; stdcall; 

FROM#

 [StructLayoutAttribute(LayoutKind.Sequential)] public struct TParams { public int Type; public string Name; public double Amount; } [DllImport("some.dll")] public static extern int Func(TParams[] arrParams, int high); TParams[] params = new TParams[len]; ...populate params int retval = Func(params, params.Length-1); 
+7
source

To compliment David, you can marshal a Delphi string, but it's ugly. In C #, you need to replace all your lines in an IntPtr structure.

 private static IntPtr AllocDelphiString(string str) { byte[] unicodeData = Encoding.Unicode.GetBytes(str); int bufferSize = unicodeData.Length + 6; IntPtr hMem = Marshal.AllocHGlobal(bufferSize); Marshal.WriteInt32(hMem, 0, unicodeData.Length); // prepended length value for (int i = 0; i < unicodeData.Length; i++) Marshal.WriteByte(hMem, i + 4, unicodeData[i]); Marshal.WriteInt16(hMem, bufferSize - 2, 0); // null-terminate return new IntPtr(hMem.ToInt64() + 4); } 

This can be sent directly to Delphi, where it will be correctly read as a string.

Remember that you must free this line when you are done with it. However, GlobalFree() cannot be called directly on a pointer to a string, because it does not indicate the beginning of the selection. You will have to overlay this pointer with a long one, then subtract 4, and then return it to the pointer. This compensates for the length prefix.

+1
source

Source: https://habr.com/ru/post/1412372/


All Articles