Passing none static member function as callback parameter to manag

G

Guest

Hi,

I have my main application class(unmanaged) that has a none static member
function that I need to pass as a delegate to managed C# method.

In one of the methods of this class(unmamanged), I am calling a managed C#
method(I use gcnew to instantiate the managed class). One of the parameters
of this C# method is a delegate. I need to pass the none static member
function as a delegate(function pointer) as a parameter in the managed C#
function call.

e.g.

//Managed C#
namespace MySpace
{
public delegate int MyDelegate();

MyDelegate rDelegate;

public class Managed
{
void ManagedFunction(MyDelegate del)
{
//do stuff
}

}
}


//Unmanaged C++ written in VC++ 6.0

//.h
class Unmanaged
{
public:
Unmanaged();
~Unmanaged();

void UnmanagedFunc1();
void UnmanagedFunc2();

}

*.cpp
using namespace MySpace;

void Unmanaged::UnmanagedFunc1()
{
MySpace::Managed ^man = gcnew MySpace::Managed;

//I need to pass UnmanagedFunc2() here as a delegate/callback as a
//parameter to ManagedFunction.
man->ManagedFunction(??)



}

void Unmanaged::UnmanagedFunc2()
{

}

Will appreciate all the help I can get on this.

Also what if I need to pass a string also as a parameter to
ManagedFunction(). In unmanaged it is a char* or a CString. How can I do the
conversion to pass it to managed.

Thanks for the help.
 
T

Tamas Demjen

Haxan said:
Hi,

I have my main application class(unmanaged) that has a none static member
function that I need to pass as a delegate to managed C# method.

After some discussion of this topic (see http://tinyurl.com/h2kry), I
came up with two very simple examples demonstrating managed->unmanged
and unmanaged->managed event handling:

http://tweakbits.com/ManagedToUnmanagedCallback.cpp
http://tweakbits.com/UnmanagedToManagedCallback.cpp

What you're asking for is handling managed events from unmanaged code,
so my ManagedToUnmanagedCallback.cpp example should give you an idea.

On the other hand, I assume your code is written in VC++ 2005, compiled
in mixed-mode. It looks like your unmanaged class is written in VC6,
which requires yet another level of interaction. In other words, the
Thunk class (see my example) will call a VC6 DLL inside.

In addition to my example, Microsoft has an official guide about this
subject too:
http://msdn2.microsoft.com/en-us/library/367eeye0.aspx

Finally, you have to take care of your argument marshaling, such as
converting String^ to const char* / const wchar_t*. You can use
StringToHGlobalAnsi/StringToHGlobalUni to do that, as shown here:
http://tinyurl.com/kmzmh (see Jochen's StringConvA / StringConvW classes).

Tom
 
G

Guest

Thanks very Tamas. This looks like what I was looking for. Appreciate the
tips. I will work on it.
 
G

Guest

Im still having problems with passing char * parameter in my unmanaged code.
This is how I have defined

//Managed C#
namespace MySpace
{
public delegate int MyDelegate(string strMan);

MyDelegate rDelegate;

public class Managed
{
void ManagedFunction(MyDelegate del)
{
//do stuff
}

}
}


//Unmanaged C++ written in VC++ 6.0

//.h
class Unmanaged
{
public:
Unmanaged();
~Unmanaged();

void UnmanagedFunc1();
static void UnmanagedFunc2(gcroot<System::String^> pUnm);

}

*.cpp
using namespace MySpace;

void Unmanaged::UnmanagedFunc1()
{
MySpace::Managed ^man = gcnew MySpace::Managed;

//I need to pass UnmanagedFunc2() here as a delegate/callback as a
//parameter to ManagedFunction.
man->ManagedFunction(??)

}

void Unmanaged::UnmanagedFunc2(gcroot<System::String^> pUnm)
{


}

I get ane error C3352 ..The specified function does not match the delegate
type void(System::String ^)

Will appreciate if you can provide me some suggestion on how to fix this.

Thanks
 
G

Guest

Please note that my unmanaged class is a dll. It seems like we cannot pass
Managed types/objects as parameters in dlls.
 
T

Tamas Demjen

Haxan said:
Please note that my unmanaged class is a dll. It seems like we cannot pass
Managed types/objects as parameters in dlls.

You don't have to. Your managed code can call your unmanaged DLL.

Tom
 
T

Tamas Demjen

Haxan said:
*.cpp
using namespace MySpace;

void Unmanaged::UnmanagedFunc1()
{
MySpace::Managed ^man = gcnew MySpace::Managed;

//I need to pass UnmanagedFunc2() here as a delegate/callback as a
//parameter to ManagedFunction.
man->ManagedFunction(??)

}

The event must be handled by a managed class, which can forward the call
to a native class. I call this trampoline class Thunk. That's where the
translation from String^ to char* takes place:

ref class Thunk
// managed event handler that forwards to unmanaged call
{
public:
void CallbackForwarder(String^ s)
{
UnmanagedFunc1(StringConvA(s).c_str());
}
};

where StringConvA is (untested):

struct StringConvA
{
char *szAnsi;
StringConvA(String^ s)
:
szAnsi(static_cast<char*>(System::Runtime::InteropServices::Marshal::StringToHGlobalAnsi(s).ToPointer()))
{}
~StringConvA()
{
System::Runtime::InteropServices::Marshal::FreeHGlobal(IntPtr(szAnsi));
}
char* c_str() const
{
return szAnsi;
}

};

Tom
 
G

Guest

Actually the unmanaged class is part of an old dll that was written using
VC++ 6.0. Since we later on started using managed code, the changes have to
be part of the dll.
 
T

Tamas Demjen

Haxan said:
Actually the unmanaged class is part of an old dll that was written using
VC++ 6.0. Since we later on started using managed code, the changes have to
be part of the dll.

You have to write a mixed-mode DLL, a translation layer between the C#
and the VC6 code. That mixed-mode DLL has to export native functions
that your VC6 code can call. In turn, it will internally set up the
managed event forwarder and calls the managed library function. This
intermediate DLL is necessary, because I don't think you can compile
C++/CLI code from VC6.

Tom
 
G

Guest

Hi Tamas,

Sorry for the confusion. Actually the code was originally written in VC++6
and it has been ported to .Net 2005 and compiled as C++/CLI and it is talking
to a managed C# application(dll). I dont think you need to have an
intermediate dll in such a case. All I want to do is pass a function
pointer/delegate with a CString/char * parameter to managed C# function(one
of the parameters of the C# function is a delegate).

Im able to pass the delegate to managed C# function but cant pass the
CString/char * parameter(which is a parameter of this delegate).

Will appreciate your help.
THanks
 
T

Tamas Demjen

Haxan said:
Im able to pass the delegate to managed C# function but cant pass the
CString/char * parameter(which is a parameter of this delegate).

OK, I modified my original example to pass a String instead of an int.
It also includes the String^ to const char* conversion. I don't see it
happening without an intermediate forwarder, which I called Thunk. It
can go to the C++/CLI DLL. In your case ManagedSender is written in C#,
but it shouldn't make any difference. The point is that ManagedSender
doesn't know anything about the receiver, it has no idea that it's
handled by an unmanaged class.

This is the best I can do to help you. I can't suggest anything that
automatically marshals the string without the intermediate Thunk object.
There may be something available that I'm not aware of.

Tom



#include "stdafx.h"
#include <vcclr.h>
#include <iostream>

using namespace System;

////////////////////////////////////////////
// C# DLL

#pragma managed

public delegate void ManagedEvent(String^ arg);

public ref class ManagedSender
// event sender
{
public:
event ManagedEvent^ SomeEvent;
void FireEvent()
{
SomeEvent("Hello");
}
};

////////////////////////////////////////////
// Mixed native C++ / C++/CLI DLL

#pragma unmanaged

class UnmanagedReceiver
{
public:
void HandleEvent(const char* arg)
{
std::cout << arg << "\n";
}
};

#pragma managed

struct StringConvAnsi
{
char *szAnsi;
StringConvAnsi(String^ s)
: szAnsi(static_cast<char*>(
Runtime::InteropServices::Marshal::StringToHGlobalAnsi(s).
ToPointer()))
{
}
~StringConvAnsi()
{
Runtime::InteropServices::Marshal::FreeHGlobal(IntPtr(szAnsi));
}
char* c_str() const
{
return szAnsi;
}

};

ref class Thunk
// event forwarder
{
public:
Thunk(UnmanagedReceiver* A_ptr) : ptr(A_ptr) { }
void CallbackForwarder(String^ arg)
{
ptr->HandleEvent(StringConvAnsi(arg).c_str());
}
private:
UnmanagedReceiver* ptr;
};

////////////////////////////////////////////
// Test application to set up the chain of calls

#pragma managed

int main(array<String ^> ^args)
{
UnmanagedReceiver receiver;
ManagedSender^ sender = gcnew ManagedSender;
Thunk^ thunk = gcnew Thunk(&receiver);
sender->SomeEvent += gcnew ManagedEvent(thunk,
&Thunk::CallbackForwarder);
sender->FireEvent();
return 0;
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top