【问题标题】:How can I marshal a Delphi short string using p/invoke?如何使用 p/invoke 编组 Delphi 短字符串?
【发布时间】:2015-06-01 08:42:54
【问题描述】:

我在 C# 中导入的 dll 中的变量类型存在问题。它是用面向对象的 pascal 编写的,并且说它是使用 Delphi 开发工具开发的。

在库的手册上说shortstring 是一个压缩的字节数组。第一个字节是长度,接下来是 255 个字节,包含我需要的字符串数据。

所以在 C# 中导入 dll 后,我写道:

[return: MarshalAs(UnmanagedType.LPArray)]

然后从dll中调用函数:

public static extern byte[] xxxx();

但我收到以下错误:

无法封送“返回值”:托管/非托管类型组合无效

另一方面,我尝试了以下方法:

[return: MarshalAs(UnmanagedType.SafeArray)]

这次我得到了错误:

等级为 18727 的 SafeArray 已传递给期望等级为 1 的数组的方法

你能告诉我我做错了什么,首先像这样从编译的库中获取短字符串是正确的吗?

问候

【问题讨论】:

    标签: c# delphi pinvoke marshalling


    【解决方案1】:

    我认为编组 Delphi 短字符串的最简洁方法是将其包装在结构中。

    [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));
            }
        }
    }
    

    此类型不可 blittable,因此不能用作函数返回值。如果您可以控制 Delphi 代码,那么您可以确保不使用短字符串作为函数返回类型。但是,我怀疑您无法控制它。在这种情况下,您需要结构的 blittable 版本。看起来像这样:

    [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);
                }
            }
        }
    }
    

    这表明 DLL 导出 Delphi 短字符串的设计不佳。这表明该库的作者并未将其设计为与其他编译器兼容。这反过来表明这些函数很可能使用默认的 Delphi 调用约定。这是register,Microsoft 工具不支持。如果我的预感是对的,那么这个 DLL 是不能直接消费的。您需要编写一个适配器 DLL,以公开一个更友好的互操作接口。

    【讨论】:

    • 返回值部分我没看懂,能否详细说明我的代码类型[return: MarshalAs(UnmanagedType.LPArray)]和函数调用public static extern byte[] xxxx();
    • 函数返回类型需要是 blittable 值类型。数组不是。我的答案中的 blittable 值版本(不安全的结构)与作为返回类型的 Delphi 短字符串兼容。
    • 我认为您在不安全的byte[] bytes = Encoding.Default.GetBytes(value.Substring(0, length)); 行有错误。其次,现在我应该如何从 dll 调用函数?
    • 我对你的设置器感到不舒服,因为字节数组的长度可能与length 变量的值不同。特别是如果输入字符串包含 BMP 之外的字符,则结果数组可能比字符串短,从而导致 read-beyond-the-end 错误。我更喜欢将Math.Min(bytes.length, 255) 传递给Copy
    • @CodesInChaos 谢谢,这是一个好点。编辑怎么样?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-11-14
    • 1970-01-01
    • 2010-09-14
    • 2012-01-21
    • 2017-01-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多