2012-10-31 37 views
4

我有供應商的API調用,我想在我的C#Windows Forms應用程序中使用它。問題是無論我嘗試什麼P/Invoke,我都會在線路上發生崩潰,說明該通話不平衡。我嘗試過與供應商合作,但他們沒有Visual Studio 2012,也沒有任何C#或.NET的經驗,並且經過幾次來回電子郵件後,他們基本上都解決了問題。在C#中與C/C++例程接口導致翻譯

這裏是他們的官方C/C++聲明

const char *<dll name here>(int Param1, 
          const char *Param2, 
          const char *Param3, 
          unsigned long Param4, 
          unsigned short Param5, 
          unsigned short Param6, 
          unsigned short Param7, 
          unsigned short Param8, 
          unsigned short Param9); 

這裏是最近的P/Invoke的DllImport行我試過了。

[System.Runtime.InteropServices.DllImport(
    "<dll name here>", EntryPoint = "<AnsiFunctionName>", ExactSpelling = true, 
    CharSet = System.Runtime.InteropServices.CharSet.Ansi, SetLastError = true)] 
public static extern String <AnsiFunctionName>(int Param1, 
               String Param2, 
               String Param3, 
               ulong Param4, 
               Int16 Param5, 
               Int16 Param6, 
               Int16 Param7, 
               Int16 Param8, 
               Int16 Param9); 

下面是實際的錯誤消息:

主題行:PInvokeStackImbalance檢測

到P A呼叫/調用功能 '' 具有不平衡堆棧。這很可能是因爲託管的PInvoke簽名與非託管目標籤名不匹配。檢查P/Invoke簽名的調用約定和參數是否與目標非託管簽名相匹配。

我嘗試了各種各樣的東西,甚至把一個void返回類型。我試過不使用返回類型。我嘗試了而不是Strings,StringBuilders和IntPtrs。不管我嘗試過什麼,沒有任何工作。

我在閱讀堆棧上的文章後提到供應商 溢出返回字符串是危險的,不應該這樣做。他們的反應是:

「該網頁上提到的字符串返回問題並不適用於 我們的函數它們返回一個指向靜態分配的字符串,這樣你就不會需要釋放它(。絕不能試圖這樣做),它看起來像「UnmanagedType.LPStr」應該工作(雖然IntPtr的事情應該工作過,用適當的轉換或鑄造)

更新#1:如前所述在評論中,我使用uint,而不是ulong,直到這篇文章,我只是認爲我會比較好看,uint和ulong都不起作用

更新#2:也許給實際的函數名稱將有所幫助。它是犰狳CreateCodeShort3A。展望未來,我正在使用純.NET,但我仍然需要與之交互,但目前尚不可能。更重要的是,這個問題讓我很好奇,爲什麼我無法解決像P/Invoke這樣看似簡單的事情。畢竟,那裏有多少變化? (不是一個嚴肅的問題)。

+0

這可能不是主要的問題,而是「無符號長」在C++中通常會等同於「UINT」在C#中,不「ULONG」。 (同樣,C++中的「long」通常是C#中的「int」)。另一方面,「unsigned long long」將等同於C#中的「ulong」。 –

+0

直到這篇文章,我使用uint,而不是ulong,只是爲了寫這個問題,我把它改爲ulong,這也失敗了。我嘗試了數十億次,或者至少有不少排列,至今沒有任何效果。 –

+3

您忘記了調用約定 - 您需要'CallingConvention = CallingConvention.Cdecl'作爲DllImport的一部分,否則它使用StdCall調用約定,這是不正確的並導致此錯誤 – Petesh

回答

2

這裏是我能看到的可能性:

  1. 返回值不應String。使用IntPtr並轉換爲帶有Marshal.PtrToStringAnsi的字符串。
  2. C++類型unsigned long應與uint匹配。您在C#中使用了ulong,這是一個64位類型。在Windows上的C++中,unsigned long是一個無符號的32位類型。
  3. C++類型unsigned short應與ushort匹配。
  4. C++ DLL使用的調用約定大概是cdecl。將CallingConvention = CallingConvention.Cdecl添加到您的P/Invoke
  5. 請勿將SetLastError用於此功能。這用於通過GetLastError返回錯誤條件的Win32 API函數。這個DLL文件幾乎肯定不會。

因此在P/Invoke的應該是這個樣子:

[DllImport(@"mydll.dll", CallingConvention=CallingConvention.Cdecl)] 
public static extern IntPtr FunctionName(
    int Param1, 
    string Param2, 
    string Param3, 
    uint Param4, 
    ushort Param5, 
    ushort Param6, 
    ushort Param7, 
    ushort Param8, 
    ushort Param9 
); 
+0

問題在於缺少CallingConvention = CallingConvention.Cdecl。現在,我一直在我的頭上撞牆。我還發現該函數必須返回一個IntPtr,但可以接受一個字符串作爲參數。很好,我是對的。解決方案很簡單,很愚蠢,我只是沒有看到。感謝Petesh和David。我也實施了David的其他評論,並且作品也很棒。我不知道/意識到SetLastError是用於Win32 API的。我不確定UInt16和ushort之間的區別,但我也是這樣做的。謝謝。 –

+0

'UInt16'和'ushort'沒有什麼不同,但在你的問題中你有'Int16'。 –