Wrap delegate in std::function? - c#

I have a native, unmanaged C++ library that I want to wrap in a managed C++ class to provide clean and type safe way to access the unmanaged class from C# without having to do PInvoke.
One the methods I'm trying to wrap have the following signature:
void Unmanaged::login(
const std::wstring& email,
const std::wstring& password,
std::function<void()> on_success,
std::function<void(int, const std::wstring&)> on_error);
Trying to wrap this however turns out to be not easy at all. The obvious way:
public delegate void LoginSuccess();
public delegate void LoginFailed(int, String^);
void Wrapper::login(String ^ email, String ^ password, LoginSuccess ^ onSuccess, LoginFailed ^ onError)
{
unmanaged->login(convert_to_unmanaged_string(email), convert_to_unmanaged_string(password), [onSuccess]() {onSuccess(); }, [](int code, const std::wstring& msg) {onError(code,convert_to_managed_string(msg))});
}
Fails because managed C++ doesn't allow (local) lambdas (in members).
I know I can use Marshal::GetFunctionPointerForDelegate to get a native pointer to the delegate, but I still need to provide a "middleware" to convert between managed/unmanaged types (such as std::wstring).
Is there perhaps a better way than using managed C++ altogether?

Your code doesn't compile because you can't capture a managed object in a native lambda. But you can easily wrap a managed object in an unmanaged one, with the help of the gcroot class:
You'll need these headers:
#include <vcclr.h>
#include <msclr/marshal_cppstd.h>
And here's the wrapper code:
static void managedLogin(String^ email, String^ password, LoginSuccess^ onSuccess, LoginFailed^ onError)
{
gcroot<LoginSuccess^> onSuccessWrapper(onSuccess);
gcroot<LoginFailed^> onErrorWrapper(onError);
Unmanaged::login(
msclr::interop::marshal_as<std::wstring>(email),
msclr::interop::marshal_as<std::wstring>(password),
[onSuccessWrapper]() {
onSuccessWrapper->Invoke();
},
[onErrorWrapper](int code, const std::wstring& message) {
onErrorWrapper->Invoke(code, msclr::interop::marshal_as<String^>(message));
}
);
}
public ref class Wrapper
{
public:
static void login(String ^ email, String ^ password, LoginSuccess ^ onSuccess, LoginFailed ^ onError)
{
managedLogin(email, password, onSuccess, onError);
}
};
The gcroot object wraps a System::Runtime::InteropServices::GCHandle, which will keep the managed object alive. It's an unmanaged class you can capture in a lambda. Once you know this, the rest is straightforward.
For some reason, the compiler complains if you try to use a lambda in a member function, but it's totally fine with a lambda in a free function. Go figure.

This is the best thing I could come up with quickly. I think you're gonna be stuck with wrappers to hand marshal your args back to managed types unless there is some whole auto callback marshaling thing I'm not aware of. Which is quite possible since I've only been doing managed programming for a week.
delegate void CBDelegate(String^ v);
typedef void(*CBType)(String^);
typedef std::function<void(const wchar_t*)> cb_type;
cb_type MakeCB(CBType f)
{
gcroot<CBDelegate^> f_wrap(gcnew CBDelegate(f));
auto cb = [f_wrap](const wchar_t *s) {
f_wrap->Invoke(gcnew String(s));
};
return cb;
}
void f(cb_type);
void MyCallback(String^ s) {
Console::WriteLine("MyCallback {0}", s);
}
void main() {
f(MakeCB(MyCallback));
}
#pragma unmanaged
void f(cb_type cb)
{
cb(L"Hello");
}
EDIT 1: Improved code.
EDIT 2: Steal good ideas from #Lucas Trzesniewski. Also CBWrap was not needed
EDIT 3: If you prefer to wrap the function instead of the callback.
delegate void CBDelegate(String^ v);
typedef void(*CBType)(String^);
typedef std::function<void(const wchar_t*)> cb_type;
void f(cb_type);
void f_wrap(CBType cb) {
gcroot<CBDelegate^> cb_wrap(gcnew CBDelegate(cb));
auto cb_lambda = [cb_wrap](const wchar_t *s) {
cb_wrap->Invoke(gcnew String(s));
};
f(cb_lambda);
}
void MyCallback(String^ s) {
Console::WriteLine("MyCallback {0}", s);
}
void main() {
f_wrap(MyCallback);
}

Related

Call C++ native/unmanaged member functions from C# when member functions were not exported

I have an unmanaged DLL that exports only a C style factory method that returns a new instance of a class (simplified here to look simple).
hello.h
#if defined(HWLIBRARY_EXPORT) // inside DLL
# define HWAPI __declspec(dllexport)
#else // outside DLL
# define HWAPI __declspec(dllimport)
#endif
struct HelloWorld{
public:
virtual void sayHello() = 0;
virtual void release() = 0;
};
extern "C" HWAPI HelloWorld* GetHW();
hello.cpp
#include "hello.h"
struct HelloWorldImpl : HelloWorld
{
void sayHello(){
int triv;
std::cout<<"Hello World!";
std::cin>>triv;
};
void release(){
this->HelloWorldImpl::~HelloWorldImpl();
};
HelloWorld* GetHW(){
HelloWorld* ptr = new HelloWorldImpl();
return ptr;
};
Now, I can use dllimport to access GetHW() but is there a way to access the member functions of the returned 'struct'... ie, sayHello and release?
I was also stuck with the same problem. This question was asked a while before. I commented to it for any better solution but didn't get any reply yet. So, reposting it.
When i googled, able to find out two solutions.
Solution1: Expose all the member functions in the C-style for the existing dll. Which i cant do, as it is a 3rd party dll.
Solution2: Write a managed C++ dll exposing the functionality of native C++ dll, which later can be used in your C# dll. Here many classes/functions are present. So, creating would take most of the time.
i got the above solutions from the link below.
How To Marshall
Please let me know if there is any better solution other than the above two solutions?
i have the source code for C++ solution. But what i though was not to touch C++ dll. If there is any possibility to do it in C#, it would be great.
If there is no alternative, i need to follow any one of the specified two solutions.
The C++ code is using the way abstract classes are implemented by the Visual C++ compiler. http://blogs.msdn.com/b/oldnewthing/archive/2004/02/05/68017.aspx. This memory layout is "fixed" because it is used for implementing COM interfaces. The first member of the struct in memory will be a pointer to a vtable containing the function pointers of your methods. So for a
struct HelloWorldImpl : public HelloWorld
{
public:
int value1;
int value2;
}
the "real" layout in memory would be:
struct HelloWorldImpl
{
HelloWorldVtbl *vtbl;
int value1;
int value2;
}
where vtbl would be:
struct HelloWorldVtbl
{
void *sayHello;
void *release;
}
Just for the sake of doing a complete response, I'm writing the example for this signatures:
struct HelloWorld {
public:
virtual int sayHello(int v1, int v2, int v3) = 0;
virtual void release() = 0;
};
C# code:
[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr GetHW();
[StructLayout(LayoutKind.Sequential)]
struct HelloWorldVtbl
{
public IntPtr sayHello;
public IntPtr release;
}
Your functions are void Func(void) or int Func(int, int, int), but in truth they have a hidden parameter, this, so you can write them as:
int sayHello(HelloWorld*, int, int, int);
void release(HelloWorld*);
so in C# the delegate is
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate int Int32MethodInt32Int32Int32(IntPtr ptr, int v1, int v2, int v3);
[UnmanagedFunctionPointer(CallingConvention.ThisCall)]
public delegate void VoidMethodVoid(IntPtr ptr);
Then you can use
IntPtr ptr = GetHW();
IntPtr vtbl = Marshal.ReadIntPtr(ptr, 0);
HelloWorldVtblhw = (HelloWorldVtbl)Marshal.PtrToStructure(vtbl, typeof(HelloWorldVtbl));
Int32MethodInt32Int32Int32 sayHello = (Int32MethodInt32Int32Int32)Marshal.GetDelegateForFunctionPointer(hw.sayHello, typeof(Int32MethodInt32Int32Int32));
int res = sayHello(ptr, 1, 2, 3);
Console.WriteLine(res);
VoidMethodVoid release = (VoidMethodVoid)Marshal.GetDelegateForFunctionPointer(hw.release, typeof(VoidMethodVoid));
release(ptr);

How to call class ref object in c unmanaged dll from c#

I am trying to call c++ umnamanged dll ref. class object from c# application. DLL function call working with VC++.
My VC++ code is as follows
class METHOD_TYPE CDeskApi
{
public:
CDeskApi(void);
/*
Description : Initiates a connection to NEST system
Parameters : 1. serialkey provided to implement the api
2. object of CDeskApiCallback implemented
Return : 0 in case of success and -1 in case of error
*/
int Initialise(char *serialkey, CDeskApiCallback* callback);
/*
Description : Request data from the server
Parameters : 1. symbol of interest
2. intervals of 1 min, multiples of 1 min, DAILY_PERIOD in case of daily.
3. data to be retrieved from. in no of seconds since epoch
4. identifier, which is returned in the callback
Return : 0 in case of success and -1 in case of error
*/
~CDeskApi(void);
};
class METHOD_TYPE CDeskApiCallback
{
public:
};
class Myhandler : public CDeskApiCallback
{
public:
virtual int quote_notify( const char* symbol, int size, Quotation *pQuotes, unsigned long echo)
{
return 0;
};
};
Myhandler handler;
void Cnest_desk_appDlg::OnBnClickedOk()
{
if(odesk.Initialise("VWAP", &handler))
{
AfxMessageBox("Error!");
return;//error
}
}
and my C# code is as follows
[DllImport("DeskApi.dll", EntryPoint = "?Initialise#CDeskApi##QAEHPADPAVCDeskApiCallback###Z")]
static extern void DeskApiInitialize(IntPtr symbol, callback fn);
private delegate int callback(IntPtr symbol, int nMaxSize, ref Quotation pQuotes, ulong echo);
private callback mInstance;
private void btnFetch_Click(object sender, EventArgs e)
{
IntPtr ptrCString = (IntPtr)Marshal.StringToHGlobalAnsi(txtFetch.Text);
CallTest.DeskApiGetQuote(ptrCString,quote_notify);
Marshal.FreeHGlobal(ptrCString);
}
private int quote_notify(IntPtr symbol, int nMaxSize, ref Quotation pQuotes, ulong echo)
{
return 0;
}
In C# everything works fine but it doesn't call the quote_notify function?
Is that the full code? I doubt it because you don't even have any methods in CDeskApiCallback (which presumably is some interface-like class meant for callbacks). Either way, a C++ "callback" object is very different from a .NET delegate, which is how you're trying to use it.
Another major flaw is that you're trying to dll-import a C++ method (CDeskApi::Initialise). That part will work fine, but how are you going to instantiate the C++ class so that you can call Initialise on it?
I'm sure you could somehow get to work with P/Invoke, but for this purposes COM or C++/CLI would be better. Any COM or C++/CLI classes and interfaces will be directly visible to C# without having to write weird marshalling instructions in the C# code.

Passing a C# class object in and out of a C++ DLL class

I've been working on a prototype code application that runs in C# and uses classes and functions from older C++ code (in the form of an imported DLL). The code requirement is to pass in a class object to the unmanaged C++ DLL (from C#) and have it be stored/modified for retrieval later by the C# application. Here's the code I have so far...
Simple C++ DLL Class:
class CClass : public CObject
{
public:
int intTest1
};
C++ DLL Functions:
CClass *Holder = new CClass;
extern "C"
{
// obj always comes in with a 0 value.
__declspec(dllexport) void SetDLLObj(CClass* obj)
{
Holder = obj;
}
// obj should leave with value of Holder (from SetDLLObj).
__declspec(dllexport) void GetDLLObj(__out CClass* &obj)
{
obj = Holder;
}
}
C# Class and Wrapper:
[StructureLayout(LayoutKind.Sequential)]
public class CSObject
{
public int intTest2;
}
class LibWrapper
{
[DLLImport("CPPDLL.dll")]
public static extern void SetDLLObj([MarshalAs(UnmanagedType.LPStruct)]
CSObject csObj);
public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)]
ref CSObject csObj);
}
C# Function Call to DLL:
class TestCall
{
public static void CallDLL()
{
...
CSObject objIn = new CSObject();
objIn.intTest2 = 1234; // Just so it contains something.
LibWrapper.SetDLLObj(objIn);
CSObject objOut = new CSObject();
LibWrapper.GetDLLObj(ref objOut);
MessageBox.Show(objOut.intTest2.ToString()); // This only outputs "0".
...
}
}
Nothing but junk values appear to be available within the DLL (coming from the passed in C# object). I believe I am missing something with the class marshalling or a memory/pointer issue. What am I missing?
Edit:
I changed the above code to reflect changes to the method/function definitions, in C#/C++, suggested by Bond. The value (1234) being passed in is retrieved by the C# code correctly now. This has exposed another issue in the C++ DLL. The 1234 value is not available to the C++ code. Instead the object has a value of 0 inside the DLL. I would like to use predefined C++ functions to edit the object from within the DLL. Any more help is greatly appreciated. Thanks!
Bond was correct, I can't pass an object between managed and unmanaged code and still have it retain its stored information.
I ended up just calling C++ functions to create an object and pass the pointer back into C#'s IntPtr type. I can then pass the pointer around to any C++ function I need (provided it's extern) from C#. This wasn't excatly what we wanted to do, but it will serve its purpose to the extent we need it.
Here's C# the wrapper I'm using for example/reference. (Note: I'm using StringBuilder instead of the 'int intTest' from my example above. This is what we wanted for our prototype. I just used an integer in the class object for simplicity.):
class LibWrapper
{
[DllImport("CPPDLL.dll")]
public static extern IntPtr CreateObject();
[DllImport("CPPDLL.dll")]
public static extern void SetObjectData(IntPtr ptrObj, StringBuilder strInput);
[DllImport("CPPDLL.dll")]
public static extern StringBuilder GetObjectData(IntPtr ptrObj);
[DllImport("CPPDLL.dll")]
public static extern void DisposeObject(IntPtr ptrObj);
}
public static void CallDLL()
{
try
{
IntPtr ptrObj = Marshal.AllocHGlobal(4);
ptrObj = LibWrapper.CreateObject();
StringBuilder strInput = new StringBuilder();
strInput.Append("DLL Test");
MessageBox.Show("Before DLL Call: " + strInput.ToString());
LibWrapper.SetObjectData(ptrObj, strInput);
StringBuilder strOutput = new StringBuilder();
strOutput = LibWrapper.GetObjectData(ptrObj);
MessageBox.Show("After DLL Call: " + strOutput.ToString());
LibWrapper.DisposeObject(ptrObj);
}
...
}
Of course the C++ performs all the needed modifications and the only way for C# to access the contents is, more or less, by requesting the desired contents through C++. The C# code does not have access to the unmanged class contents in this way, making it a little longer to code on both ends. But, it works for me.
This is the references I used to come up with the basis of my solution:
http://www.codeproject.com/Articles/18032/How-to-Marshal-a-C-Class
Hopefully this can help some others save more time than I did trying to figure it out!
I believe you should declare your returning method like this
__declspec(dllexport) void getDLLObj(__out CClass* &obj)
and respectively the C# prototype
public static extern void GetDLLObj([MarshalAs(UnmanagedType.LPStruct)]
ref CSObject csObj);
the inbound method should take a pointer to CClass too, the C# prototype is ok.
If you using the class just from C# ,you should use GCHandle for that http://msdn.microsoft.com/en-us/library/system.runtime.interopservices.gchandle%28v=vs.110%29.aspx
edit:
CClass *Holder;
extern "C"
{
// obj always comes in with a 0 value.
__declspec(dllexport) void SetDLLObj(CClass* obj)
{
(*Holder) = (*obj);
}
// obj should leave with value of Holder (from SetDLLObj).
__declspec(dllexport) void GetDLLObj(__out CClass** &obj)
{
(**obj) = (*Holder);
}
}

Interop question about StringBuilder

I am calling a C# method from C code.
The C# method:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void p_func(StringBuilder arg);
public static void callback(StringBuilder arg)
{
Console.WriteLine(arg.ToString());
}
The C method:
extern "C" void c_method(p_func f)
{
char msg[4];
::strcpy(msg,"123");
char* p="123";
f(msg); // this is ok
f(p); //Error: Attempted to read or write protected memory!
}
But if I use String instead of StringBuilder as below in my C# method declaration, f(p) and f(msg) both work. Why?
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void p_func(String arg);
public static void callback(String arg)
{
Console.WriteLine(arg.ToString());
}
Note
the calling logic is like this:
c_method()---->delegate p_func--->callback()
not the reverse.
I checked the arg in the callback(StringBuilder arg), the Length, MaxCapacity, Capacity are all the same for char *p or msg[]. Only that *p leads to the exception. Why?
When you use String as the parameter type, the CLR will not attempt to write any changes back to the native memory buffer. When you use a StringBuilder (which is the right choice for in/out string parameters), it will. But the memory p points to will be read-only because of the way you declared it, that's why yout get an error.

Call c++ function pointer from c#

Is it possible to call a c(++) static function pointer (not a delegate) like this
typedef int (*MyCppFunc)(void* SomeObject);
from c#?
void CallFromCSharp(MyCppFunc funcptr, IntPtr param)
{
funcptr(param);
}
I need to be able to callback from c# into some old c++ classes. C++ is managed, but the classes are not ref classes (yet).
So far I got no idea how to call a c++ function pointer from c#, is it possible?
dtb is right. Here a more detailed example for Marshal.GetDelegateForFunctionPointer. It should work for you.
In C++:
static int __stdcall SomeFunction(void* someObject, void* someParam)
{
CSomeClass* o = (CSomeClass*)someObject;
return o->MemberFunction(someParam);
}
int main()
{
CSomeClass o;
void* p = 0;
CSharp::Function(System::IntPtr(SomeFunction), System::IntPtr(&o), System::IntPtr(p));
}
In C#:
public class CSharp
{
delegate int CFuncDelegate(IntPtr Obj, IntPtr Arg);
public static void Function(IntPtr CFunc, IntPtr Obj, IntPtr Arg)
{
CFuncDelegate func = (CFuncDelegate)Marshal.GetDelegateForFunctionPointer(CFunc, typeof(CFuncDelegate));
int rc = func(Obj, Arg);
}
}
Have a look at the Marshal.GetDelegateForFunctionPointer method.
delegate void MyCppFunc(IntPtr someObject);
MyCppFunc csharpfuncptr =
(MyCppFunc)Marshal.GetDelegateForFunctionPointer(funcptr, typeof(MyCppFunc));
csharpfuncptr(param);
I don't know if this really works with your C++ method, though, as the MSDN documentation states:
You cannot use this method with function pointers obtained through C++

Categories