2013-05-17 118 views
3

我試圖調用一個方法德爾福DLL具有以下簽名德爾菲法:調用的DLL從C#

function SMap4Ovr(const OverFileName  : ShortString ; 
        const Aclay    : Integer  ; 
        const Acarbon   : Double   ; 
        out errstr    : ShortString): WordBool; 

我正在使用C#以下導入:

 [DllImport("SMap.dll", CallingConvention = CallingConvention.StdCall, CharSet = CharSet.Ansi)] 
    public static extern bool SMap4Ovr(
     string OverFileName, 
     int Aclay, 
     double Acarbon, 
     out string errstr 
     ); 

但我得到一個AccessViolationException。

我似乎可以調用DLL中的幾個簡單的方法,它們具有字符串參數但不是整數或雙精度。

我也嘗試過CallingConvention = CallingConvention.Cdecl,但是這給了我同樣的錯誤。

回答

8

在編寫互操作代碼,它對於界面的各個方面來說都是至關重要的。以下是您必須在雙方達成一致的主要問題:

  1. 調用約定。
  2. 參數列表。
  3. 參數類型和語義。

第一個觀察結果是您的調用約定不匹配。您在Delphi端有register,在C#端有stdcall。德爾福register公約是德爾福私人,所以你應該使用stdcall

其次,你的字符串參數類型不匹配。德爾福shortstring是一個數據類型,在Delphi 2發佈時變得遺留下來,應該被認爲是上個世紀的遺物。它從來不是一個有效的互操作類型,並且p/invoke框架中沒有任何可用於匹配它的東西。儘管您可以嘗試手動進行編組,但是當有簡單的解決方案時,這是很多不需要的工作。你應該儘量忘記shortstring的全部內容。

您需要使用接口兩側都可以使用的字符串類型。您可以使用以空字符結尾的C字符串,但更好和更簡單的選擇是Delphi中的 COM BSTR

所以最後的結果如下。

德爾福

function SMap4Ovr(
    OverFileName: WideString; 
    Aclay: Integer; 
    Acarbon: Double; 
    out errstr: WideString 
): WordBool; stdcall; 

C#

[DllImport("SMap.dll")] 
public static extern bool SMap4Ovr(
    [MarshalAs(UnmanagedType.BStr)] 
    string OverFileName, 
    int Aclay, 
    double Acarbon, 
    [MarshalAs(UnmanagedType.BStr)] 
    out string errstr 
); 

我沒有打擾指定的DllImport調用約定,因爲默認是stdcall。如果你喜歡,你可以明確這一點。

使用WideString時要小心你的don't attempt to use it as a return value。由於Delphi對返回值使用非標準語義,因此只能使用適合寄存器的簡單類型作爲返回值。

+1

感謝David對我很有幫助。 – Mike

0

Delphi中的默認調用約定是register,而不是stdcall。這似乎calling conventions details告訴我們,微軟FASTCALL是不一樣的Borland FASTCALL(註冊)

和C#字符串類型從德爾福ShortString短不同(其內部包含一個字節長度+串體)

+0

'fastcall'確實與'register'不同,並且在任何情況下都不被pinvoke編組所支持 –