2014-04-02 49 views
1

我想在c#應用程序中使用c dll中的函數,每當我嘗試運行應用程序並調用有問題的函數時,我都會遇到此錯誤。
起初我想也許這是因爲我使用了錯誤的簽名,但我試圖儘可能簡單但沒有運氣。
爲了削減長話短說:
這是我的C DLL實際的源代碼:方法的類型簽名不是PInvoke兼容

#include <stdio.h> 
#include <string.h> 
extern "C" 
{ 
    struct teststruct 
    { 
     char acharacter; 
     int anumber; 
     char* astring; 
     char anarray[10]; 
     const char* conststring; 
    }; 

    __declspec(dllexport) teststruct TestDLL() 
    { 
     teststruct stc; 
     stc.acharacter = 'A'; 
     stc.anumber = 10; 
     strcpy(stc.anarray, "Test"); 
     stc.astring = "astring!"; 
     stc.conststring = "Crash?"; 

     return stc; 
    } 
} 

這是C#櫃檯部分:

[StructLayout(LayoutKind.Sequential)] 
public struct teststruct 
{ 
    public char acharacter; 
    public int anumber; 
    [MarshalAs(UnmanagedType.LPStr)] 
    public string astring; 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] 
    public char[] anarray; 
    [MarshalAs(UnmanagedType.LPStr)] 
    public string conststring; 
} 

namespace Tcp_connections_list 
{ 

    public partial class Form1 : Form 
    { 

     [DllImport("simple c dll.dll",CallingConvention= CallingConvention.Cdecl)] 
     public static extern teststruct TestDLL(); 

     public Form1() 
     { 
      InitializeComponent(); 
     } 

     private void btnTestDll_Click(object sender, EventArgs e) 
     { 
      teststruct test = TestDLL(); //Crash at the very begining! 
      textBox1.Text = test.acharacter.ToString(); 
      textBox1.Text = test.anarray.ToString(); 
      textBox1.Text = test.anumber.ToString(); 
      textBox1.Text = test.astring.ToString(); 
      textBox1.Text = test.conststring.ToString(); 


     } 

    } 
} 

下面的代碼片段給我完全相同的錯誤,我改變結構,以

struct teststruct 
{ 
    char acharacter; 
    int anumber; 
}; 

及其C#相當於

[StructLayout(LayoutKind.Sequential)] 
public struct teststruct 
{ 
    public char acharacter; 
    public int anumber; 
} 

使其儘可能簡單,但我再次得到相同的確切的錯誤!
我在這裏錯過了什麼?

+0

FWIW您的非託管代碼是C++而不是C –

+0

爲什麼是C++?它只使用c頭文件和c語句加上它的外部塊! – Breeze

+2

這是C++。我可以告訴你,因爲你使用'extern「C」'這是無效的C.更重要的是,你把這個結構稱爲'teststruct',它在C中是無效的。在C中它將是'struct teststruct',或者你必須添加一個'typedef'。 –

回答

5

問題是以空字符結尾的C字符串的編組。你不能指望p/invoke編組在函數返回值中處理這些函數。該結構需要聲明如下:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
public struct teststruct 
{ 
    public byte acharacter; // don't use C# char which is 2 bytes wide 
    public int anumber; 
    public IntPtr astring; 
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)] 
    public char[] anarray; 
    public IntPtr conststring; 
} 

你需要使用Marshal.PtrToStringAnsi解壓到C字符串的內容。

作爲更普遍的建議,將大型結構作爲函數返回值傳遞是一個壞主意。沒有一個公認的ABI可以做到這一點。不同的編譯器以不同的方式處理這個問題最好是在調用代碼中分配結構,並傳遞它的地址。就像這樣:

__declspec(dllexport) void TestDLL(teststruct *ts) 
{ 
    ts->... = ...; 
    ... 
} 

和C#的一面:

[DllImport(...)] 
public static extern void TestDLL(out teststruct ts); 

事實上,它不會讓我感到吃驚,如果你正在試圖與不能被整理爲一個返回值的工作結構。你真的應該通過它作爲out參數。

+0

非常感謝:) 事實上,我試圖使用一個指針,就像你建議的那樣。但是我只打算在c#中使用輸出結構,使用ref使得我預先初始化結構!再次,如果我做teststruct ts = new teststruct();並繼續運行示例我得到這個錯誤; http://pastebin.com/TQqKiEDL 如果我用掉,而我得到相同的確切的錯誤。 在發生的異常的詳細信息中,我看到: 「名稱'$ exception'在當前上下文中不存在」 我該怎麼辦? – Breeze

+0

我意識到我誤解了你的代碼。你沒有寫入未初始化的內存。我確定了答案。 –

+0

謝謝,那麼爲什麼我得到的錯誤?!簡單地做: teststruct測試; test.acharacter = 0; test.anumber = 0; TestDLL(ref test);只是不工作,並給出了這個錯誤http://pastebin.com/TQqKiEDL! – Breeze