2017-02-13 96 views
1

我嘗試在我的C#代碼以使用C++ DLL。
所讀取的所有數據必須被傳遞到DLL作爲一個指針結構。我的第一個想法是隻保留一些非託管內存,傳遞函數指針和事後提取數據。問題是這些函數只會返回一個錯誤代碼,該代碼會轉換爲「參數無效」。C#通指向struct(含有非Blittable型)非託管C++ DLL

從DLL文件頭(結果和BusPortId縮短)

typedef enum { 
    BUS_COM1 = 0x01, // 2 to 15 ommited 
    BUS_COM16 = 0x10, 
    BUS_USB1 = 0x101, // 2 to 15 ommited 
    BUS_USB16 = 0x110 
} BusPortId; 

typedef enum { 
    BUS_SUCCESS = 0,  //!< The operation was completed successfully 
    BUS_ERROR  = 0x100, //!< An error occured 
    BUS_INVALIDARG = 0x1000, //!< An argument is not valid 
} Result 


struct BusPortInfo 
{ 
    ULONG  portInfoSize; 
    CHAR  portText[64]; 
    BOOL  portLocked; 
    BusPortId portId; 
}; 

Result BUSDRV_API busGetAvailablePortCount(ULONG *retCount); 

Result BUSDRV_API busGetAvailablePort(ULONG index, BusPortInfo *portInfo); 

我的C#程序的相關部分,到目前爲止

enum BusPortId 
{ 
    BUS_COM1 = 0x01, // 2 to 15 ommited 
    BUS_COM16 = 0x10, 
    BUS_USB1 = 0x101, // 2 to 15 ommited 
    BUS_USB16 = 0x110 
}; 

public enum Result 
{ 
    BUS_SUCCESS = 0,  //!< The operation was completed successfully 
    BUS_ERROR = 0x100, //!< An error occured 
    BUS_INVALIDARG = 0x1000, //!< An argument is not valid 
}; 

struct BusPortInfo 
{ 
    public ULONG portInfoSize; 
    unsafe public fixed char portText[64]; 
    public BOOL portLocked; 
    public BusPortId portId; 
} 

[DllImport(DLL_Path)] 
unsafe static extern Result busGetAvailablePortCount(ULONG* retCount); 
[DllImport(DLL_Path)] 
unsafe static extern Result busGetAvailablePort(ULONG index, BusPortInfo* portInfo); 

ulong count= 0; 
Result res = busGetAvailablePortCount(&count); 

ulong index = 0; 
BusPortInfo info = new BusPortInfo(); 
Result res = busGetAvailablePort(0, &info); 

到busGetAvailablePortCount(或其他類似功能)呼叫工作沒有任何問題。但是,當我打電話busGetAvailablePort我得到了我的控制檯輸出以下

Cannot marshal 'parameter #2': Pointers cannot reference marshaled structures. Use ByRef instead.

的問題是,我可以改變我的結構在C#中,這樣我可以通過指針,但隨後從DLL中的函數也返回「一種說法是無效」

我有什麼做我的結構,所以我可以將指針傳遞給它的功能同時還獲得由DLL接受?

P.S.對不起英語不好,我不是母語的人。

回答

3

這些聲明有很多問題,往往發生在程序員不斷嘗試使代碼調用工作的黑客代碼時。對結構最可能是正確的聲明是:

[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)] 
    struct BusPortInfo { 
     public int portInfoSize; 
     [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 64)] 
     public string portText; 
     public bool portLocked; 
     public BusPortId portId; 
    } 

強調的ULONG在本機代碼是一個32位的類型和fixed恰恰是沒有必要的,是非常尷尬的。由於bool和string成員,這個結構不是blittable,沒什麼值得擔心的。

[DllImport]聲明需要正確聲明第二個參數。該CallingConvention屬性始終關係重大,我們不能看到什麼BUSDRV_API手段。 Punting:

[DllImport(DLL_Path, CallingConvention = CallingConvention.StdCall)] 
    static extern Result busGetAvailablePortCount(out int retCount); 

    [DllImport(DLL_Path, CallingConvention = CallingConvention.StdCall)] 
    static extern Result busGetAvailablePort(int index, 
          [In, Out] ref BusPortInfo portInfo); 

而且通話看起來不正確。當一個struct有一個「size」成員時,那麼api協議通常會要求它在調用之前被設置。這是一個安全措施,確保API時,呼叫方使用了錯誤的結構聲明不能破壞內存。當設置錯誤時,「無效參數」錯誤是預期的結果。所以寫它類似於:

int count; 
Result res = busGetAvailablePortCount(out count); 
if (res != Result.BUS_SUCCESS) throw new Exception("Api failure " + res.ToString()); 

for (int ix = 0; ix < count; ++ix) { 
    BusPortInfo info; 
    info.portInfoSize = Marshal.SizeOf(typeof(BusPortInfo)); // Important! 
    res = busGetAvailablePort(ix, ref info); 
    if (res != Result.BUS_SUCCESS) throw new Exception("Api failure " + res.ToString()); 
    // etc... 
} 

未經測試,當然應該在球場。如果仍然有問題,那麼驗證本地代碼中的sizeof(BusPortInfo)與C#中的Marshal.SizeOf(typeof(BusPortInfo))的值是否匹配。如果全部失敗,則改爲使用C++/CLI,以便直接使用本機聲明。並與DLL的所有者進行正確的使用說明,最好他會爲你寫一個pinvoke示例。