2009-08-04 134 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很棒!我已经通过搜索发现了很多其他问题的答案。这是我的第一篇文章,我对这篇文章的快速回复感到惊讶。恭喜所有员工团队和成员! ;)