C++/CLI marshaling .NET delegate to native delegat

2019-09-05 02:49发布

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条回答
【Aperson】
2楼-- · 2019-09-05 03:49

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.

查看更多
登录 后发表回答