简体   繁体   中英

How can I marshal a Delphi short string using p/invoke?

I have a problem with a variable type from a dll which i am importing in C#. It is written in object oriented pascal and it says it is developed with the Delphi Development Tool.

On the manual of the library it says that shortstring is a packed array of bytes. The first byte is the length and the following are exaclty 255 bytes with the string data that i need.

So after importing the dll in C# i wrote:

[return: MarshalAs(UnmanagedType.LPArray)]

Then called the function from the dll:

public static extern byte[] xxxx();

But i am getting the following error:

Cannot marshal 'return value': invalid managed/unmanaged type combination

On the other hand i tried the following:

[return: MarshalAs(UnmanagedType.SafeArray)]

This time i get the error:

SafeArray of rank 18727 has been passed to a method expecting an array of rank 1

Can you please tell me what i am doing wrong, and firstly is correct like this to get a shortstring from a compiled library?

Regards

I think the cleanest way to marshal a Delphi short string is to wrap it in a struct.

[StructLayout(LayoutKind.Sequential)]
public struct DelphiShortString
{
    private byte length;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst=255)]
    private byte[] payload;

    public string Value 
    {
        get 
        { 
            return Encoding.Default.GetString(payload, 0, length); 
        }
        set 
        {
            length = (byte)Math.Min(value.Length, 255);
            payload = Encoding.Default.GetBytes(value.Substring(0, length));
        }
    }
}

This type is not blittable and so cannot be used as a function return value. If you have control of the Delphi code, then you can ensure that you don't use short strings as function return types. However, I suspect that you don't have control of it. In which case you'd need a blittable version of the struct. Which would look like this:

[StructLayout(LayoutKind.Sequential)]
public unsafe struct DelphiShortString
{
    private byte length;

    private fixed byte payload[255];

    public string Value 
    {
        get 
        {
            {
                byte[] bytes = new byte[length];
                fixed (byte* ptr = payload)
                {
                    Marshal.Copy((IntPtr)ptr, bytes, 0, length);
                }
                return Encoding.Default.GetString(bytes, 0, length); 
            }
        }
        set 
        {
            byte[] bytes = Encoding.Default.GetBytes(value);
            length = (byte)Math.Min(bytes.Length, 255);
            fixed (byte* ptr = payload)
            {
                Marshal.Copy(bytes, 0, (IntPtr)ptr, length);
            }
        }
    }
}

It is indicative of poor design for a DLL to export a Delphi short string. That suggests that the author of the library has not designed it to be compatible with other compilers. Which in turn suggests that the functions may well use the default Delphi calling convention. Which is register and is not supported by Microsoft tools. If my hunch is right, then this DLL cannot be consumed directly. You would need to write an adapter DLL that exposed a more interop friendly interface.

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