2015-01-05 111 views
1

我被迫使用非託管的delphi dll。我沒有訪問源代碼。只有模糊的文檔:從C#應用程序調用Delphi函數時調用PInvokeStackImbalance

type 
    TServiceData = packed record 
    DBAlias: PChar; 
    LicKey: PChar; 
    Pass: PChar; 
    end; 
    PServiceData = ^TServiceData; 

function CreateRole(SrvData: PServiceData; var UserName: PChar): byte; stdcall; 

UserName應該是一個out PARAM。

我的C#代碼:

[StructLayout(LayoutKind.Sequential, Pack = 1)] 
public struct SERVICE_DATA 
{ 
    public string DBALias; 
    public string LicKey; 
    public string Pass; 
} 

[DllImport(dllname, CallingConvention = CallingConvention.StdCall)] 
public static extern byte CreateRole(SERVICE_DATA data, out string str);  

我不知道是什麼導致堆棧不平衡(除調用約定,這似乎是正確的)。我不知道我的結構中的字符串是否正確編組,但根據其他線程,這不會導致PStackImbalanceException。任何幫助將不勝感激:)

編輯。 從大衛我已經實現的建議,現在我得到的訪問衝突異常:

「未處理的異常:System.AccessViolationException:試圖讀取或寫入保護內存這通常是指示其他內存已損壞」

我的結構和方法的聲明僅僅是從答案拷貝粘貼,沒有什麼花哨的方式,我稱之爲:

 string str; 
     var data = new SERVICE_DATA(); 
     data.DBALias = "test"; 
     data.LicKey = "test"; 
     data.Pass = "test"; 
     var result = CreateRole(ref data, out str); 

回答

2

有幾件事情不對的翻譯:

  1. Delphi代碼接收指向記錄的指針。 C#代碼按值傳遞它。這是堆棧不平衡警告的原因。
  2. 用戶名參數可能在德爾福方面處理不正確。它需要是一個指向動態分配內存的指針,通過調用CoTaskMemAlloc在COM堆上分配。我猜你沒有這樣做,所以當編組試圖通過調用CoTaskMemFree來釋放指針時,你會遇到問題。

我可能會使用COM字符串類型的字符串。我也會避免打包記錄,因爲這是一般慣例。

我會寫這樣的:

德爾福

type 
    TServiceData = record 
    DBAlias: WideString; 
    LicKey: WideString; 
    Pass: WideString; 
    end; 

function CreateRole(const SrvData: TServiceData; out UserName: WideString): Byte; 
    stdcall; 

C#

[StructLayout(LayoutKind.Sequential)] 
public struct SERVICE_DATA 
{ 
    [MarshalAs(UnmanagedType.BStr)] 
    public string DBALias; 

    [MarshalAs(UnmanagedType.BStr)] 
    public string LicKey; 

    [MarshalAs(UnmanagedType.BStr)] 
    public string Pass; 
} 

[DllImport(dllname, CallingConvention = CallingConvention.StdCall)] 
public static extern byte CreateRole(
    [In] ref SERVICE_DATA data, 
    [MarshalAs(UnmanagedType.BStr)] out string str 
);  

下面是一個完整測試項目,表明該作品如預期的那樣:

德爾福

library Project1; 

type 
    TServiceData = record 
    DBAlias: WideString; 
    LicKey: WideString; 
    Pass: WideString; 
    end; 

function CreateRole(const SrvData: TServiceData; out UserName: WideString): Byte; 
    stdcall; 
begin 
    UserName := SrvData.DBAlias + SrvData.LicKey + SrvData.Pass; 
    Result := Length(UserName); 
end; 

exports 
    CreateRole; 

begin 
end. 

C#

using System; 
using System.Runtime.InteropServices; 

namespace ConsoleApplication1 
{ 
    class Program 
    { 
     const string dllname = @"..."; 

     [StructLayout(LayoutKind.Sequential)] 
     public struct SERVICE_DATA 
     { 
      [MarshalAs(UnmanagedType.BStr)] 
      public string DBALias; 

      [MarshalAs(UnmanagedType.BStr)] 
      public string LicKey; 

      [MarshalAs(UnmanagedType.BStr)] 
      public string Pass; 
     } 

     [DllImport(dllname, CallingConvention = CallingConvention.StdCall)] 
     public static extern byte CreateRole(
      [In] ref SERVICE_DATA data, 
      [MarshalAs(UnmanagedType.BStr)] out string str 
     ); 

     static void Main(string[] args) 
     { 
      SERVICE_DATA data; 
      data.DBALias = "DBALias"; 
      data.LicKey = "LicKey"; 
      data.Pass = "Pass"; 
      string str; 
      var result = CreateRole(ref data, out str); 
      Console.WriteLine(result); 
      Console.WriteLine(str); 
     } 
    } 
} 

輸出

 
17 
DBALiasLicKeyPass 
+0

aplying更改後的pinvokestackimbalance例外是走了,但現在我得到了AccessViolationException。 –

+0

也許你的代碼中有一個bug。我看不到你的代碼。你問到堆棧不平衡。 –

+0

你說得對,這不是我一開始就問的,所以我會把你的帖子標記爲答案。仍然 - 我已經添加了一些額外的代碼來解決我的問題。 –