I have a problems with passing string from unmanaged code to managed.
In my unmanaged class (unmanagedClass.cpp) I have a pointer to function from managed code:
TESTCALLBACK_FUNCTION testCbFunc;
TESTCALLBACK_FUNCTION takes one string and returns nothing:
typedef void (*TESTCALLBACK_FUNCTION )(char* msg);
Unmanaged class inherites from ITest interface which has only one method:
STDMETHOD(put_TestCallBack) (THIS_
LPVOID FnAddress
) PURE;
In managedClass.cs I write this code:
public class ManagedClass
{
ITest unmanaged = new unmanagedClass();
public delegate void TestDelegate(string info);
ManagedClass()
{
unmanaged.put_TestCallBack(new TestDelegate(this.Test));
}
void Test(string info)
{
MessageBox.Show(info);
}
}
[ComImport, Guid("<my guid here>")]
public class unmanagedClass
{
}
[ComImport, System.Security.SuppressUnmanagedCodeSecurity,
Guid("<my guid here>"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ITest
{
[PreserveSig]
int put_TestCallBack([MarshalAs(UnmanagedType.FunctionPtr), In] Capture.TestDelegate func);
}
To call Test func from unmanaged code I use this
(*testCbFunc)("Func Uragan33::Execute has been started!");
But when Test method from managedClass.cs is called I always received null string.
Why does it happen?
Thank in advance!
You have a mismatch on the calling convention. The typedef in your C++ code declares a function pointer with the default calling convention, which is __cdecl. But the default for a delegate in managed code is __stdcall.
You will need an attribute to tell the pinvoke marshaller otherwise. Make that look like this:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void TestDelegate(string info);
Drop the [MarshalAs] in the function declaration. Fixing the typedef in your C++ code might be preferable, if you can, clearly making everything consistent is the preferred solution:
typedef void (__stdcall * TESTCALLBACK_FUNCTION )(char* msg);
Unrelated, this a bug you'll need to fix:
unmanaged.put_TestCallBack(new TestDelegate(this.Test));
The delegate object you create is not visible to the garbage collector. If will be collected on the next GC, your code will crash when the native code makes the callback. You have to store the delegate object somewhere so the GC always sees a reference. Either as a field in the class, with the additional requirement that the class object needs to stay alive long enough, or in a static variable.
Note how all of these problems disappear when you declare a callback interface instead of a delegate. The COM way.
Related
I want to pass as callback a C++ member function to a C# project. I have other project in C++/CLI and I want to do it through it.
So, in unmanaged C++ of my C++/CLI project I have a function object:std::function<void(int)>callback;
This function is coming from my C++ project and it works fine, I save it there as example to avoid the previous step. Now, I would like to pass this callback function to my C# project. For this, I create a method in unmanaged C++, pass it to managed C++ and from this point pass it finally to C#. I'd like something like this:
// Unmanaged class
public class Wrapper
{
public:
std::function<void(int)>callback;
void ReturnToCallback(int data)
{
callback(data);
}
void PassCallback()
{
NETWrapper netWrapper;
netWrapper.TestCallback(ReturnToCallback);
}
};
//Managed class
public ref class NETWrapper
{
public:
void TestCallback(Action<int>^ callback)
{
StartGenerator^ startGen = gcnew StartGenerator(callback);
}
};
// C#
public class StartGenerator
{
private Communication comm;
public StartGenerator(Action<int> callback)
{
comm = Communication.Instance;
comm.callback = callback;
}
}
This solution, of course, gives me back an error when compiling:
Error 3 error C3867: 'IfaceXXX::Wrapper::ReturnToCallback': function call missing argument list; use '&IfaceXXX::Wrapper::ReturnToCallback' to create a pointer to member d:\XXX.h
I have tried other ways such as Get the delegate for the function pointer so I can work on Managed C++ and pass it to C# but I am not able to implement it right. What do you think is the best way to try this?
Make Wrapper::callback a pointer to the std::function.
Change Wrapper to a ref class.
That's it.
public ref class Wrapper
{
public:
std::function<void(int)>* callback;
void ReturnToCallback(int data)
{
(*callback)(data);
}
void PassCallback()
{
NETWrapper netWrapper;
netWrapper.TestCallback(gcnew Action<int>(this, &Wrapper::ReturnToCallback));
}
};
You do then need to manage the lifetime of the std::function now, perhaps my clr_scoped_ptr could show you how to do that.
I have a C++ function which performs a number of tasks "PerformJob()". I have a C# wrapper which calls PerformJob(). The job takes a while and I would like the C++ method to send "status updates" back up to the calling C# class. The C++ code is not exposed to the C# class. Adding the C# project as a reference would cause a circular dependency.
I've attempted to pass a delegate through as a parameter but I'm not familiar enough with C++ syntax to make this work (or if it is even possible?). Is there an appropriate way to pass a delegate into C++ as a parameter? Is there a better method to facilitate this communication? I'd like to avoid a dllimport, as I only need to receive updates from this one class.
CSharpClass.cs:
public delegate void CallbackDelegate(ref string status);
public CallbackDelegate jobStatusDelegate;
public void UpdateJobStatus(ref string status)
{
Job.JobStatus = status;
}
public void StartJob()
{
jobStatusDelegate = new CallbackDelegate(UpdateJobStatus);
CPlusClass jobHelper = new CPlusClass();
jobHelper.PerformJob(jobStatusDelegate);
}
CPlusClass.h:
public ref class CPlusClass
{
public:
void PerformJob(delegate del); // is there c++ delegate type?
};
CPlusClass.cpp:
void CPlusClass::PerformJob(delegate del)
{
// ....
}
C++ doesn't have delegates, but C++/CLI does, and based on ref class your code already is C++/CLI.
Delegates work much like any other reference type, they get stored as a tracking handle with the ^.
In order to not create a dependency on the C#, which would be in the wrong direction, I suggest you use a System::Action<System::String^>^ instead of defining your own delegate type.
public ref class CPlusClass
{
public:
void PerformJob(System::Action<System::String^>^ del)
{
del->Invoke(gcnew String("Hello World"));
}
};
I have a C++ DLL. It contains the following function:
struct Message {
int id;
unsigned short messageLength;
unsigned char* message;
};
class Listener {
public:
virtual void OnMessage(Message* message);
};
void init(Listener* listener);
Is there a way to call the function 'init' from C#? I know you can use [DllImport] in C# to call C++ functions, but the pointers are giving me a hard time.
Any help would be appreciated!
My Solution
C++ Code
typedef void (*MessageHander)(Message* message);
class MessageDelegate : public Listener {
public:
MessageHander* messageCallback; // hold the callback funtion
virtual void OnMessage(Message* message) {
if (messageCallback) {
(*messageCallback)(message);
}
}
}
static MessageDelegate messageDelegate;
void initEx(MessageHander* handler) {
messageDelegate.messageCallback = handler;
init(messageDelegate);
}
C# Code
[Serializable]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Message {
public int id;
public ushort messageShort;
public IntPtr message;
}
class Program {
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void MessageHandler(ref Message message);
[DllImport("MyCPlusPlusDLLName", CallingConvention = CallingConvention.Cdecl)]
public static extern void initEx(ref MessageHandler callback);
public void OnMessage(ref Message message) {
byte[] rawMessage = new byte[message.messageLength];
Marshal.Copy(message.message, rawMessage, 0, message.messageLength);
// some other logic
}
public void Run() {
MessageHandler handler = new MessageHandler(this.OnMessage);
initEx(ref handler);
}
}
You have a couple issues in your code.
In C++, your MessageHander is already a pointer (to a function). No need for pointers to pointers. Fixes:
MessageHander messageCallback; // hold the callback function
messageCallback( message );
void initEx( MessageHander handler )
In C#, you should tell the runtime you’re passing delegate as a C function pointer. The default is complicated, sometimes it passes these delegates as some COM objects instead:
[DllImport("MyCPlusPlusDLLName", CallingConvention = CallingConvention.Cdecl)]
public static extern void initEx(
[In, MarshalAs( UnmanagedType.FunctionPtr )] MessageHandler callback);
Finally, C# runtime doesn’t know your C++ code has kept that function pointer. C function pointers are extremely simple things, there's no way to ref.count them. Your C# delegate will be garbage collected shortly after initEx call. When that happens, your code will crash the next OnMessage call, calling into a de-allocated object. You need to store your handler somewhere, e.g. in a static variable, so the GC will leave it alone.
P.S. If you're passing that Message one way only, it's a good idea to specify const Message* in C++, and [In] ref Message in C#. This will slightly improve performance, the marshaler will stop marshaling that structure back from C# to C++.
The following sample is from Microsoft's documentation:
public delegate bool CallBack(int handle, IntPtr param);
public class LibWrap
{
// passing managed object as LPARAM
// BOOL EnumWindows(WNDENUMPROC lpEnumFunc, LPARAM lParam);
[DllImport("user32.dll")]
public static extern bool EnumWindows(CallBack cb, IntPtr param);
}
public class App
{
public static void Main()
{
Run();
}
[SecurityPermission(SecurityAction.Demand, UnmanagedCode=true)]
public static void Run()
{
TextWriter tw = System.Console.Out;
GCHandle gch = GCHandle.Alloc(tw);
CallBack cewp = new CallBack(CaptureEnumWindowsProc);
// platform invoke will prevent delegate to be garbage collected
// before call ends
LibWrap.EnumWindows(cewp, GCHandle.ToIntPtr(gch));
gch.Free();
}
private static bool CaptureEnumWindowsProc(int handle, IntPtr param)
{
GCHandle gch = GCHandle.FromIntPtr(param);
TextWriter tw = (TextWriter)gch.Target;
tw.WriteLine(handle);
return true;
}
}
There are two things that mystify me.
Firstly, the documentation of GCHandle.Alloc only talks about preventing a an object to be garbage collected. If that was all, you wouldn't need GCHandle.Alloc: Obviously in the sample, tw isn't going to be collected during the call to EnumWindows - there's a reference to it in the function scope.
The concern here is that one needs to make sure that it isn't moved. But the documentation of GCHandle.Alloc doesn't talk about that. So what's going on?
Secondly, what about the delegate? There may not be a problem in this sample, but what if the delegate is bound to an object (a lambda with a closure or a non-static method of a class)? In that case, one needs to take care of the delegate too, right? Is that another GCHandle.Alloc(myDelegate) or are there more things to consider?
Moving is not a concern. GCHandle.ToIntPtr promises to give you an integer value that you can, at a later date, pass to GCHandle.FromIntPtr to retrieve the original handle. That's all. If you need to stop the object moving in memory then you would have to pin it. But you don't actually need to pin the object, you just need to stop it being collected, and be able to retrieve it in your callback.
The delegate's lifetime is not an issue here because the p/invoke framework will make sure it is not collected during the external call to EnumWindows, as noted in the comment. If you pass a delegate to unmanaged code, and that unmanaged code holds a reference to the delegate, then you have work to do. You must ensure that the delegate outlives the unmanaged reference to it.
I have a native DLL which is implementing some API. The C++ header looks like this:
class CAPIInterface
{
public:
virtual int __stdcall Release()=0;
virtual LPCSTR __stdcall ErrorDescription(const int code)=0;
virtual int __stdcall Login(const int login,LPCSTR password)=0;
}
In C++ a pointer to the interface is acquired this way:
typedef int (*APICreate_t)(int version,CAPIInterface **api);
pfnAPICreate =reinterpret_cast<APICreate_t>(::GetProcAddress(hlib,"APICreate"));
CAPIInterface *api=NULL;
if(pfnAPICreate) (*pfnAPICreate)(version,&api);
The methods of the interface are called like this:
api->Login(123,"password");
Now I need to load this native DLL and use the API in my C# program. I managed to load the DLL and acquire the pointer to the native interface this way:
public static class GlobalMembers
{
[UnmanagedFunctionPointer(CallingConvention.StdCall, CharSet = CharSet.Ansi)]
public delegate int APICreate_t(time_t version, out IntPtr api);
}
ptr_pfnAPICreate = NativeMethods.GetProcAddress(hlib,"APICreate");
pfnAPICreate = (GlobalMembers.APICreate_t)Marshal.GetDelegateForFunctionPointer(ptr_pfnAPICreate, typeof(GlobalMembers.APICreate_t));
pfnAPICreate(version, out mptr);
But now I'm not sure how to map this pointer (mptr) to the C# implementation of the interface. Also I'm not sure how to declare the interface CAPIInterface in C# as well. I tried declaring the interface this way:
[StructLayout(LayoutKind.Sequential)]
public class CAPIInterface
{
public delegate int Release();
public delegate string ErrorDescription(int code);
public delegate int Login(int login, string password);
}
But then it doesn't compile... it returns this error:
Error 3 Non-invocable member 'CAPIInterface.Login' cannot be used like a method. I understand that the delegates must be instantiated somewhere as well... but how to do it? Is it correct approach at all to declare the CAPIInterface as above?
I was able to convert my C++ API to C# with the help of SWIG. It works great. Thank you.