问题
I am trying to pass a delegate with managed parameters to native code to be invoked. My code below runs ok, but the string output is garbage.
Native Class
Header
#pragma once
typedef void (* SegmentCreatedDelegate)(char** arg);
public class SampleClass
{
public:
SampleClass(void);
~SampleClass(void);
void DoWork(SegmentCreatedDelegate callback);
};
Code
SampleClass::SampleClass(void)
{
}
SampleClass::~SampleClass(void)
{
}
void SampleClass::DoWork(SegmentCreatedDelegate callback)
{
for(int x = 0; x< 10; x++)
{
char* myStr2 = "newsegment!";
callback(&myStr2);
}
}
Managed Class
Header
#pragma once
public ref class SampleClassNet
{
public:
delegate void SegmentCreatedDelegateNet(System::String^ arg);
SampleClassNet(void);
void DoWork(SegmentCreatedDelegateNet^ segmentCreatedCallback);
};
Code
SampleClassNet::SampleClassNet(void)
{
}
void SampleClassNet::DoWork(SegmentCreatedDelegateNet^ segmentCreatedCallback)
{
SampleClass* nativeClass = new SampleClass();
System::IntPtr pointer = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(segmentCreatedCallback);
nativeClass->DoWork((SegmentCreatedDelegate)(void*)pointer);
System::GC::KeepAlive(segmentCreatedCallback);
}
This code runs fine with the follow c#.
var sampleClass = new SampleClassNet();
sampleClass.DoWork((Console.WriteLine));
Except I get the following output, instead of the expected 10 entries of "newsegment!".
(ÇÆX
(ÇÆX☺
(ÇÆX☻
(ÇÆX♥
(ÇÆX♦
(ÇÆX♣
(ÇÆX♠
(ÇÆX
(ÇÆX
(ÇÆX
Not exactly "newsegment!", but I am not sure why the marshaling is not working. Maybe I need I need some kind of "MarshalAs" attribute so that the System::String knows that I have 8-bit chars?
回答1:
As mentioned in the comments, you should convert the char**
to a String^
. (Btw, why pass char**
, not char*
? String
has a constructer taking char*
, which might simplify things a lot.)
I haven't tried the following, but you might give it a try:
public ref class SampleClassNet {
private:
delegate void SegmentCreatedDelegateNative(char** str);
SegmentCreatedDelegateNet^ managedCallback;
SegmentCreatedDelegateNative^ nativeCallback;
void printString(char** string);
public:
delegate void SegmentCreatedDelegateNet(System::String^ arg);
SampleClassNet();
void DoWork(SegmentCreatedDelegateNet^ segmentCreatedCallback);
};
SampleClassNet::SampleClassNet() {
nativeCallback = printString;
}
void SampleClassNet::DoWork(SegmentCreatedDelegateNet^ segmentCreatedCallback) {
SampleClass* nativeClass = new SampleClass();
managedCallback = segmentCreatedCallback;
System::IntPtr pointer = System::Runtime::InteropServices::Marshal::GetFunctionPointerForDelegate(nativeCallback);
nativeClass->DoWork((SegmentCreatedDelegate)(void*)pointer);
}
void SampleClassNet::printString(char** string) {
if (this->managedCallback != nullptr) {
String^ str = gcnew String(*string);
managedCallback(str);
}
}
The basic idea is to use another delegate, SegmentCreatedDelegateNative
, handed to the native class, and to call the actual managed delegate from the function associated with the wrapper.
来源:https://stackoverflow.com/questions/21054847/c-cli-marshaling-net-delegate-to-native-delegate