2010-04-29 62 views
3

所以我在非托管C++被调用时,一些文字恰好有一个函数“到达”一个const无符号字符*:如何通过从C++到C#

#using <MyParser.dll> 
... 
void dump_body(const unsigned char *Body, int BodyLen) 
{ 
    // Need to pass the body to DumpBody, but as what type? 
    ... 
    MyParser::Parser::DumpBody(???); 
} 

DumpBody是静态函数在一个C#DLL中定义,它应该接受一个类型的参数?

保持长度BodyLen的字符数组(文本)。

显然有一些marshalling在这里完成,但我不知道如何。

请帮忙。

回答

2
void dump_body(const unsigned char *body, int bodyLen) 
{ 
    // you might want a different encoding... 
    String ^str = gcnew String((sbyte*)body, 0, bodyLen, gcnew ASCIIEncoding); 
    MyParser::Parser::DumpBody(str); 
} 

DumpBody将采取的字符串。

+0

我认为你钉了它!然而,“sbyte”是一个C#关键字,所以我必须将其更改为“const char”并且它可以工作。但是,函数得到“无符号字符”(值0到255),我创建了一个System :: String,期望一个有符号的8位整数,这对于ASCII编码有意义,但可能不适用于这种情况(尽管文本I' m接收看起来像普通的ASCII文本)。我希望它不是一些自定义编码。 – tzup 2010-04-30 06:23:32

+0

原来编码是Latin-1,所以这里是更新的行: String^str = gcnew String((const char *)body,0,bodyLen,Encoding :: GetEncoding(“ISO-8859-1”)) – tzup 2010-04-30 09:24:32

2

以下代码描述了托管C#代码和非托管C/C++ DLL之间通过PInvoke进行通信的情况。基本上这个想法是,你可以将C代码传递给C#委托。 const unsigned char*被转换为stringMarshal.PtrToStringAnsi

C++:

typedef void FncFoo (const char*); 

FncFoo callback = 0;  

extern "C" { 
    // this function is called from C# to pass the delegate 
    void TakeFooCallback(FncFoo f) { 
    callback = f; 
    } 
} 

// C function calling C# 
void dump_body(const unsigned char *Body, int BodyLen) { 
    if(callback) { 
    callback(Body); 
    } 
} 

C#:

class StringC2CS { 

    delegate void DlgFoo(IntPtr a); 

    [DllImport("myclib.dll")] // name of the dll 
    void TakeFooCallback(DlgFoo callback); 

    // this function gets called by C 
    void FooImpl(IntPtr a) { 
    string str = Marshal.PtrToStringAnsi(a); 
    // use string 
    } 

    public StringC2CS() { 
    // passes a callback to C 
    TakeFooCallback(new DlgFoo(FooImpl)); 
    } 
} 
+0

这看起来像一个可行的解决方案,但我还没有尝试。但是,我选择了单线解决方案,因为这也适用。 +1并感谢您的输入。 – tzup 2010-04-30 06:28:11

+0

如果您使用托管C++,则更容易。与“纯”C/C++库进行通信时,我必须使用我的方法。 – Danvil 2010-04-30 08:13:38

+0

在这种情况下,它被管理的C++,但你的解决方案被注意到并存档以供将来使用:) – tzup 2010-04-30 09:30:08

0

我结束了使用Danvil的方法。我将C代码封装在C++ dll中,然后制作了另一个引用C++ dll的C#dll,并公开了我在托管代码中需要的功能。所以在这里,它是:

C++ DLL:

.h文件中

// Definition of a callback function to be called when some data arrives 
typedef void (*FncDlg) (const unsigned char*, int len); 

// Declaration of the function that will be exposed in managed code. 
// The managed code will use this function to pass in a delegate 
extern "C" __declspec(dllexport) void AttachCallback(FncDlg f); 

// Declaration of a function that receives data and passes it to managed code 
void PassData(const unsigned char *Body, int BodyLen); 

.cpp文件

// Instantiate a global function pointer to nothing 
void (*callback)(const unsigned char*, int len) = 0; 

// This is called by C# to pass in a delegate 
void AttachCallback(FncDlg f) 
{ 
    // Save delegate globally (to be triggered later when we receive data) 
    callback = f; 
} 

// This is the function called when data is read from the socket 
void PassData(const unsigned char *Body, int BodyLen) 
{ 
    if(callback) { 
    callback(Body, BodyLen); 
    } 
} 

C#DLL:

public static class Parser 
{ 
    public delegate void DlgDumpTipData(IntPtr a, int len); 

    [DllImport("C++ dll name here", EntryPoint = "AttachCallback", ExactSpelling = true)] 
    public static extern void AttachCallback(DlgDumpTipData callback); 

    public static int ConnectAndStartReceive(...) 
    { 
     // Attach a callback function (called when a message is received from data feed) 
     AttachCallback(DumpDataCalback); 
     ... 
    } 

    public delegate void MsgHandler(string msg); 
    // When data arrives from the C library, this event passes it on to C# clients 
    public static event MsgHandler OnMessageArrived = delegate { }; 

    public static void DumpDataCalback(IntPtr ptr, int len) 
    { 
     //string str = Marshal.PtrToStringAnsi(ptr); 
     string dumpData; 
     sbyte[] byteArr = new sbyte[len]; 
     for (int i = 0; i < len; i++) 
     { 
      // WARNING: if byte > 127 we have a problem when casting from Byte to SByte 
      // In this case there is no problem, tested with values over 127 and it 
      // converts fine to Latin-1 using the String overload 
      byteArr[i] = (sbyte)Marshal.ReadByte(ptr, i); 
     } 

     unsafe 
     { 
     fixed (sbyte* pbyteArr = byteArr) // Instruct the GC not to move the memory 
     { 
      dumpData = new String(pbyteArr, 0, len, Encoding.GetEncoding("ISO-8859-1")); 
     } 
     } 

     // Send data to whoever subscribes to it 
     OnMessageArrived(dumpData); 
     GC.Collect(); // This slows things down but keeps the memory usage low 
    }   
}