2012-05-14 29 views
4

我一直在試圖調用已在德爾福通過以下方式創建了一個方法:使用Delphi的stuct數組和字符串在C#中

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(我已經用其他字符串參數試過了)。有沒有人有線索如何解決這個問題?

回答

7

這有兩個主要問題,使用開放數組和使用Delphi string

打開陣列

Delphi的開放陣列通過將指針傳遞到所述陣列的所述第一元件和一個額外的參數,指定的最後一個項目的Delphi中術語的索引,high實現。欲瞭解更多信息,請參閱this answer

德爾福串

的C#編組不能與德爾福字符串互操作。 Delphi字符串是私有類型,只能在Delphi模塊內部使用。相反,您應該使用以空字符結尾的字符串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); 
+0

感謝您的回答和編輯。我會嘗試這個解決方案。據我所知,我需要創建新的Delphi庫,將暴露使用字符串類型的函數,並將其替換爲PAnsiChar?那是對的嗎? – xurc

+0

是的,這是正確的。魔鬼是在細節中,但在很高的層次上,你所說的是正確的。如果你這樣做,我可能會避開開放數組,並顯式地接收指向第一個元素和元素個數的指針。 –

1

恭維大衛的回答,您可以將編成一個德爾福字符串,但它很醜。在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,然後將其轉換回指針。這補償了長度前綴。