2013-10-07 91 views
0

嗨,我是C#和C++編程新手,嘗試在C#項目(.NET 3.5)中使用非託管C++ dll。我被困在這個錯誤上:SafeArrayTypeMismatchException將c#結構傳遞給非託管C++ DLL

System.Runtime.InteropServices.SafeArrayTypeMismatchException:指定的數組不是預期的類型。

下面是DLL

#ifdef FUNCTIONLIB_EXPORTS 
#define FUNCTIONLIB_API __declspec(dllexport) 
#else 
#define FUNCTIONLIB_API __declspec(dllimport) 
#endif 
typedef struct _FunctionOpt 
{ 
    char    UserName[32]; 
    char    Password[32]; 
    char    ServerIP[128]; 
    int     ServerPort; 
    int     Index;   
    char    TargetSubChannel; 
    long    Timeout; 
    char    Filepath[256]; 
    bool    isFirst;   
    DWORD    SyncTime;    
    char    *pOutBuf;    
    int     OutBufSize; 
    unsigned short  OutImgResW; 
    unsigned short  OutImgResH; 

}STFUNCTIONOPT, *PSTFUNCTIONOPT; 

FUNCTIONLIB_API int FunctionLib_Snapshot(PSTFUNCTIONOPT pstFunctionOpt); 

頭文件我沒有訪問C++代碼,所以我只能改變以下的C#代碼。我的相關代碼如下:

public unsafe struct PSTFUNCTIONOPT    
    { 
     public char[] UserName; 
     public char[] Password; 
     public char[] ServerIP; 
     public int ServerPort; 
     public int Index;      
     public char TargetSubChannel;    
     public long Timeout;    
     public char[] Filepath; 
     public bool isFirst;    
     public uint SyncTime;      
     public char *pOutBuf; // example C++ usage: myStruct.pOutBuf = (char*)malloc(1920 * 1080 * 3); 
     public int OutBufSize; 
     public ushort OutImgResW; 
     public ushort OutImgResH; 
    } 

    // need to use dumpbin to get entrypoint name due to c++ mangling 
    [DllImport(@"C:\Location\To\Project\bin\FunctionLibLib.dll", EntryPoint = "[email protected]@[email protected]@@Z")] 
    public static extern int FunctionLib_Snapshot(PSTFUNCTIONOPT pstFunctionOpt); 

    public unsafe int FunctionLib_Run() 
    { 
     PSTFUNCTIONOPT stFunctionOpt = new PSTFUNCTIONOPT(); 

     stFunctionOpt.UserName = ("uname").ToCharArray(); 
     stFunctionOpt.Password = ("pword").ToCharArray(); 
     stFunctionOpt.ServerIP = ("192.168.1.1").ToCharArray(); 
     stFunctionOpt.ServerPort = 80; 
     stFunctionOpt.Index = 255; 
     stFunctionOpt.Timeout = 15000; 
     stFunctionOpt.Filepath = ("c:\\temp\\test.jpg").ToCharArray(); 
     stFunctionOpt.isFirst = true; 
     stFunctionOpt.SyncTime = 0; 
    //stFunctionOpt.pOutBuf = new char*[10000];  // not sure how to do this yet 
     stFunctionOpt.OutBufSize = 10000; 

     // get result from DLL 
     return FunctionLib_Snapshot(stFunctionOpt); 
    } 

如何正確地將結構傳遞給此非託管DLL?這個錯誤看起來很簡單,但我一直無法縮小這個問題的範圍。任何幫助表示讚賞!

回答

2

幾個問題:

public char[] UserName; 

需要被編組爲嵌入字符串:

[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 
    public string UserName; 

對於像這樣聲明的其他字段重複該操作。

public char *pOutBuf; // example C++ usage: myStruct.pOutBuf = (char*)malloc(1920 * 1080 * 3); 

不清楚C++函數是分配緩衝區還是客戶端代碼應該這樣做。如果它是前者,那麼你有一個大問題,你不能再調用free()函數來釋放緩衝區。你唯一的希望就是你的C#代碼應該分配它,而不是不可能。在這種情況下,它是:

public byte[] pOutBuf; 
    ... 
    stFunctionOpt.pOutBuf = new byte[10000]; 
    stFunctionOpt.OutBufSize = stFunctionOpt.pOutBuf.Length; 

的PInvoke的聲明是錯誤的:

[DllImport(@"C:\Location\To\Project\bin\FunctionLibLib.dll", EntryPoint = "[email protected]@[email protected]@@Z")] 
public static extern int FunctionLib_Snapshot(PSTFUNCTIONOPT pstFunctionOpt); 

的說法是ref PSTFUNCTIONOPT pstFunctionOpt。從結構名稱中刪除P,它不是指針類型。避免硬編碼到DLL的路徑,這不會在用戶的機器上工作。只要確保在構建輸出目錄中有DLL的副本。

1

我認爲你缺少一兩件事情從你的結構定義

  • 在內存結構的佈局是在C不同++,所以你需要[StructLayout(LayoutKind.Sequential)]屬性添加到您的結構
  • 在C++中,你有固定長度的字符數組,所以你需要在c#中使用屬性[MarshalAs(UnmanagedType.ByValTStr, SizeConst = your_array_lenght)]指定,此外,如果你這樣做,封送拆分器將處理字符串和字符數組之間的轉換。

你的結構定義應然後看起來像

[StructLayout(LayoutKind.Sequential)] 
public struct PSTFUNCTIONOPT    
{ 
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)] 
    public string UserName; 
    ... 
    [MarshalAs(UnmanagedType.LPStr)] 
    public string pOutBuf; 
} 

欲瞭解更多信息請查看http://msdn.microsoft.com/en-us/library/s9ts558h.aspx

相關問題