如何使用 p/invoke 编组 Delphi 短字符串?
How can I marshal a Delphi short string using p/invoke?
我在 C# 中导入的 dll 中的变量类型有问题。它是用面向对象的 Pascal 编写的,它说它是用 Delphi 开发工具开发的。
在库的手册上说 shortstring
是一个打包的字节数组。第一个字节是长度,后面是 exaclty 255 个字节,其中包含我需要的字符串数据。
所以在用 C# 导入 dll 之后我写道:
[return: MarshalAs(UnmanagedType.LPArray)]
然后调用dll中的函数:
public static extern byte[] xxxx();
但我收到以下错误:
Cannot marshal 'return value': invalid managed/unmanaged type combination
另一方面,我尝试了以下方法:
[return: MarshalAs(UnmanagedType.SafeArray)]
这次我得到错误:
SafeArray of rank 18727 has been passed to a method expecting an array of rank 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,因此不能用作函数 return 值。如果您可以控制 Delphi 代码,那么您可以确保不使用短字符串作为函数 return 类型。但是,我怀疑您无法控制它。在这种情况下,您需要结构的 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 来公开一个更易于互操作的界面。
我在 C# 中导入的 dll 中的变量类型有问题。它是用面向对象的 Pascal 编写的,它说它是用 Delphi 开发工具开发的。
在库的手册上说 shortstring
是一个打包的字节数组。第一个字节是长度,后面是 exaclty 255 个字节,其中包含我需要的字符串数据。
所以在用 C# 导入 dll 之后我写道:
[return: MarshalAs(UnmanagedType.LPArray)]
然后调用dll中的函数:
public static extern byte[] xxxx();
但我收到以下错误:
Cannot marshal 'return value': invalid managed/unmanaged type combination
另一方面,我尝试了以下方法:
[return: MarshalAs(UnmanagedType.SafeArray)]
这次我得到错误:
SafeArray of rank 18727 has been passed to a method expecting an array of rank 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,因此不能用作函数 return 值。如果您可以控制 Delphi 代码,那么您可以确保不使用短字符串作为函数 return 类型。但是,我怀疑您无法控制它。在这种情况下,您需要结构的 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 来公开一个更易于互操作的界面。