2009-08-04 136 views
0

我想從我的C#應用​​程序中用C編寫的DLL調用方法。下面是我實現的呼叫:從C#調用C DLL函數 - 將字符參數轉換爲字符串

- > C法簽名:(從第三部分公司)

Int16 SSEXPORT SS_Initialize(Uint16 ServerIds[], Uint16 ServerQty, const char PTR *Binding, const char PTR *LogPath, Uint16 LogDays, Int16 LogLevel, 
Uint16 MaxThreads, Uint16 ThreadTTL, Int16 (CALLBACK *ProcessMessage)(Uint16, const char PTR *, Uint32, char PTR **, Uint32 PTR *, Int16 PTR *));

- > C型定義:

 
#ifndef _CSISERVERTYPES_ 
#define _CSISERVERTYPES_ 

#if defined(OS_NT) 

#define CALLBACK __stdcall 
#define SSEXPORT __stdcall 
#define PTR  

typedef char    Int8; 
typedef unsigned char  Uint8; 
typedef short    Int16; 
typedef unsigned short  Uint16; 
typedef long    Int32; 
typedef unsigned long  Uint32; 
typedef unsigned char  Byte; 
typedef unsigned short  Word; 
typedef unsigned long  Dword; 
typedef short    Bool; 

#endif 

typedef Int16 (CALLBACK *PM_Function) 
     (Uint16,const char PTR *,Uint32,char PTR **,Uint32 PTR *,Int16 PTR *); 

#define SS_OK    0 
#define SS_NOT_INIT   -1 
#define SS_PROC_ERR   -2 
#define SS_ERR_TCP_SRV  -3 
#define SS_ERR_OUT_MEM  -4 

#ifndef NULL 
#define NULL    0 
#endif 

#endif 

- > C#DLL方法聲明:

[DllImport("CSITCPServer.dll", SetLastError = true)] 
    static extern Int16 SS_Initialize(
     UInt16[] ServerIds, 
     UInt16 ServerQty, 
     ref string Binding, 
     ref string LogPath, 
     UInt16 LogDays, 
     Int16 LogLevel, 
     UInt16 MaxThreads, 
     UInt16 ThreadTTL, 
     ProcessMessageCallback callback); 

方法調用:

public static void Call_SS_Initialize() 
    { 
     Int16 ret; 
     UInt16[] serverIds = new UInt16[] { 2020, 2021 }; 
     string binding = ""; 
     string logPath = ""; 

     try 
     { 
      ret = SS_Initialize(
       serverIds, 
       Convert.ToUInt16(serverIds.ToList().Count), 
       ref binding, 
       ref logPath, 
       10, 
       0, 
       256, 
       300, 
       ProcessMessage); 

      Console.WriteLine("Call_SS_Initialize() -> Result of SS_Initialize: {0}", ret.ToString()); 
     } 
     catch (Exception ex) 
     { 
      Int32 err = Marshal.GetLastWin32Error(); 
      throw new Win32Exception(err); 
      //throw; 
     } 
    } 

然後我得到Win32Exception:1008 - 試圖引用一個令牌不存在

我知道,這個問題必須在非託管(C)之間的CHAR到字符串轉換和託管(C#)代碼。如果我修改綁定LogPath參數到類型SByte,它不會給出任何錯誤。但由於該方法需要一個文本(字符串),我不知道如何將文本傳遞給該方法,因爲它期望一個SByte變量...

我知道我可能不得不使用某些東西像MarshalAs,但我已經嘗試了幾個選項,並沒有取得任何成功。

有人可以告訴我我做錯了什麼?

非常感謝!


這裏是回調的定義:

public delegate Int16 ProcessMessageCallback(
     UInt16 ServerId, 
     [MarshalAs(UnmanagedType.LPStr)] ref string RequestMsg, 
     UInt32 RequestSize, 
     [MarshalAs(UnmanagedType.LPStr)] ref string ResponseMsg, 
     ref UInt32 ResponseSize, 
     ref Int16 AppErrCode); 

的事情是,在C DLL方法需要一個 「REF」 參數。撥打SS_Initialize將執行附加到ProcessMessage回撥函數。在這個函數中,我需要能夠得到SS_Initialize修改的參數(REF)...

可否請你建議,你認爲怎樣的代碼應該是結構化?

謝謝!

回答

0

你不需要使用ref來綁定和LogPath;它們是const char *並且不會改變。

這ResponseMessage回調將返回一個字符串數組,(字符**)不只是一個字符串。響應大小可能會指示數組的深度。嘗試[MarshalAs(UnamangedType.LPArray,ArraySubType = UnmanagedType.LPStr)] string []來代替。

0

編組字符串你不通過ref string,只是string。另外,你應該用[MarshalAs(UnmanagedType.LPStr)]來裝飾每個字符串,以表明你正在傳遞包含ascii字符的字符串,並且它是空的。
編輯:你也可以給你這個回調過程的定義,因爲你可能犯了類似的錯誤。

0

謝謝RavadreR Ubben,你的幫助組合做的伎倆!最後,所有關於正確控制參數中的REF子句和使用MarshalAs標記的內容都是如此。 C部分中的參數「const char *」實際上不需要REF標籤。另外我使用[MarshalAs(UnmanagedType.LPStr)]作爲字符串參數。而已!

ps:Stackoverflow.com很棒!我已經通過搜索發現了很多其他問題的答案。這是我的第一篇文章,我對這篇文章的快速回復感到驚訝。恭喜所有員工團隊和成員! ;)