4

次の方法で、Delphi で作成されたメソッドを呼び出そうとしています。

 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 とペアにする必要があります)。

ただし、Delphi の文字列には長さがプレフィックスとして付けられており、確実に Ansi であるため、これらの組み合わせはどれも機能しません (他の文字列パラメーターで試しました)。誰もこれを解決する方法の手がかりを持っていますか?

4

2 に答える 2

7

これには、オープンアレイの使用とDelphiの使用という2つの主な問題がありますstring

オープンアレイ

Delphiオープン配列は、配列の最初の要素へのポインタと、highDelphiの用語で最後の項目のインデックスを指定する追加のパラメータを渡すことによって実装されます。詳細については、この回答を参照してください。

Delphi文字列

C#マーシャラーはDelphi文字列と相互運用できません。Delphi文字列はプライベートタイプであり、Delphiモジュールの内部でのみ使用されます。代わりに、nullで終了する文字列を使用する必要があります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);
于 2012-05-14T14:10:36.747 に答える
1

デビッドの答えを補完するために、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);
}

これは Delphi に直接送信でき、そこで文字列として適切に読み取られます。

使い終わったら、この文字列を解放する必要があることに注意してください。ただし、GlobalFree()割り当ての開始を指していないため、文字列へのポインターで直接呼び出すことはできません。そのポインターを long にキャストしてから 4 を引いてから、ポインターにキャストし直す必要があります。これにより、長さのプレフィックスが補正されます。

于 2012-10-03T14:05:34.227 に答える