使用Delphi的stuct数组和字符串在C#(Using Delphi's stuct a

2019-09-17 05:29发布

我一直在试图调用已在德尔福通过以下方式创建的方法:

 function _Func1(arrParams: array of TParams): Integer;stdcall;    

 type 
   TParams = record
   Type: int;
   Name: string;
   Amount : Real;
 end;

我的代码是:

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

而结构是:

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

当我调用这个方法,我发现了错误:无法键入“TParams”的名帅场“名称”:无效的托管/非托管类型组合(字符串字段必须LPSTR,LPWSTR,BSTR或成对ByValTStr)。

然而这些都不组合的作品,如德尔福的字符串,其长度为前缀,这是安西肯定的(我与其他字符串参数尝试过)。 有没有人有一个线索如何解决这个问题?

Answer 1:

主要有两个问题,这一点,使用开放数组和使用Delphi的string

开放数组

Delphi的开放阵列通过将指针传递到所述阵列的所述第一元件和一个额外的参数,指定的最后一个项目,的指数来实现high在Delphi术语。 欲了解更多信息,请参阅这个答案 。

德尔福字符串

C#的编组不能与德尔福字符串互操作。 德尔福字符串是私有类型,只在内部使用的德尔福模块。 相反,你应该用一个空终止字符串, PAnsiChar


全部放在一起,你可以把它写这样的:

德尔福

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;

C#

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


Answer 2:

为了配合大卫的回答,您可以编组为一个Delphi字符串,但它的丑陋。 在C#中,您必须更换所有的字符串与结构IntPtr

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

这可以直接被发送到德尔福,在那里它会适当地理解为一个字符串。

请记住,当你用它做你必须释放此字符串。 然而, GlobalFree()不能直接对字符串指针调用,因为它并不指向分配的开始。 你必须是一个指针转换为长,然后再减去4,然后将它转换回一个指针。 这弥补了长度前缀。



文章来源: Using Delphi's stuct arrays and strings in C#