Triggering event in C# from C++ DLL - c#

I have an umanaged C++ DLL which is communicating with a Cisco Server (UCCX).
It sends and receives messages to and from this server via TCP/IP. Now there are some types of messages it receives which contains some parameters which it needs to send to a C# GUI which will display these parameters on screen.
Please tell me an efficient method to trigger an event in C# from this DLL.

http://blogs.msdn.com/b/davidnotario/archive/2006/01/13/512436.aspx seems to answer your question. You use a delegate on the C# side and a standard callback on the C++ side.
C++ side:
typedef void (__stdcall *PFN_MYCALLBACK)();
int __stdcall MyUnmanagedApi(PFN_ MYCALLBACK callback);
C# side
public delegate void MyCallback();
[DllImport("MYDLL.DLL")] public static extern void MyUnmanagedApi(MyCallback callback);
public static void Main()
{
MyUnmanagedApi(
delegate()
{
Console.WriteLine("Called back by unmanaged side");
}
);
}

Note the C++ side should be
typedef void (__stdcall *PFN_MYCALLBACK)();
extern "C" __declspec(dllexport) void __stdcall MyUnmanagedApi(PFN_ MYCALLBACK callback);
An alternate option is to drop the __stdcall on the C++ side
typedef void (*PFN_MYCALLBACK)();
extern "C" __declspec(dllexport) void MyUnmanagedApi(PFN_ MYCALLBACK callback);
And on the C# side:
public delegate void MyCallback();
[DllImport("MYDLL.DLL", CallingConvention = CallingConvention.Cdecl))]
public static extern void MyUnmanagedApi(MyCallback callback);
as above ...

Related

C# delegate callback with params cause AccessViolation when calling from C++ DLL

I have an unmanaged C++ DLL and a .net application which communicate using P/Invoke .
I need to update a dictionary within the .net app from a C++ DLL.
I tried to do that in the following way:
public delegate void MyDelegate(string address, string username);
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern void SaveMyDelegate(MyDelegate callback);
private static SortedDictionary<string, string> dict = new SortedDictionary<string, string>();
public void save_delegate() {
SaveMyDelegate(delegate(string address, string username) {
if (username != "") {
dict.Add(address, username);
}
});
}
On C++ side I have:
typedef void (*MANAGED_CALLBACK)(string user_address, string username);
extern "C" __declspec(dllexport) void SaveMyDelegate(MANAGED_CALLBACK callback);
MANAGED_CALLBACK my_callback;
void SaveMyDelegate(MANAGED_CALLBACK callback) {
my_callback = callback;
}
extern "C" __declspec(dllexport) VOID start_collection();
VOID start_collection() {
my_callback("test", "User");
}
On C# I do the following calls:
[DllImport("MyDLL.dll")]
public static extern void start_collection();
private void Button1_Click(object sender, EventArgs e) {
save_delegate();
start_collection();
}
When I call start_collection() I get an AccessViolation exception.
I tried to declare the delegate in the following way:
public delegate void MyDelegate(
[MarshalAs(UnmanagedType.LPStr)]string address,
[MarshalAs(UnmanagedType.LPStr)]string username);
I have also added this statement:
[UnmanagedFunctionPointer(CallingConvention.StdCall)]
public delegate void MyDelegate(...)
I've not looked at every detail of your code, but some commonly seen p/invoke issues just out immediately:
You cannot use C++ strings as interop types. You should use null terminated character arrays on the C++ side. Obtain these using the c_str() method of std::string.
The delegate likely has the wrong calling convention. The unmanaged code (probably) uses cdecl and so you need to use the [UnmanagedFunctionPointer(CallingConvention.Cdecl)] attribute when declaring the delegate type in C#.
Your C# delegate is susceptible to being collected early (while your unmanaged code still holds a reference to it) because there are no managed references to it. Typically that would be solved by holding a reference to the delegate in a static variable in your C# code.

Can I call functions across programming language boundaries using function pointer?

A C# application has a static dependency on a C++ DLL. Lets say there is a function in C# application as below:
void foo(int a)
{
Console.WriteLine(a);
}
The C++ DLL has an exported function that looks like as below.
typedef void (*Func_t)(int);
extern "C" __declspec(dllexport) void bar(Func_t f)
{
f(5);
}
Can I call bar() function from the C# application and pass foo() as a parameter and expect '5' to be printed?
bar(foo);
Define a delegate
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void Func_t(int a);
Import the function
[DllImport(...)]
static extern void bar(Func_t f);
Usage
bar(foo);

Register C# delegate to C++ callback, what does Marshal.GetFunctionPointerForDelegate do?

I used to register a delegate from C# to a callback function pointer like this (without using Marshal.GetFunctionPointerForDelegate):
C++ (Test.dll)
typedef void (__stdcall* Callback)(int num);
Callback pCallback = 0;
void __stdcall SetCallback(Callback callback)
{
pCallback = callback;
}
void __stdcall ActionCpp()
{
if(pCallback)
{
pCallback();
}
}
C#
class Program
{
[DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void SetCallBack(Callback callback);
[DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void ActionCpp();
public delegate void Callback();
static void Action()
{
Console.WriteLine("Callback succeeded.");
}
static void Main(string[] args)
{
Callback myAction = new Callback(Action);
SetCallback(myAction);
ActionCpp(); //Suppose to do the same thing as Action();
}
}
It seems that this way works fine. However, I found that we can do the same thing by using Marshal.GetFunctionPointerForDelegate and register the IntPtr of the delegate to the function pointer in C++.
I would like to know what the difference is and which practice is better? (And also Why? Thanks in advance.)
Here is the C# code when using Marshal.GetFunctionPointerForDelegate. (Nothing changes in C++ code.)
C# (With Marshal.GetFunctionPointerForDelegate)
class Program
{
[DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void SetCallBack(IntPtr pCallBack); //change to type "IntPtr" this time.
[DllImport("Test.dll", CallingConvention = CallingConvention.StdCall)]
public static extern void ActionCpp();
public delegate void Callback();
static void Action()
{
Console.WriteLine("Callback succeeded.");
}
static void Main(string[] args)
{
Callback myAction = new Callback(Action);
//GCHandle gcHandle = GCHandle.Alloc(myAction); <-- Is this necessary?
IntPtr pMyAction = Marshal.GetFunctionPointerForDelegate(myAction);
SetCallback(pMyAction);
ActionCpp();
//gcHandle.Free();
}
}
I have another further but related question about whether it is necessary to use "GCHandle.Alloc" (as in my comments in the above code) to avoid any GC action as long as my Callback myAction is still alive?
I am a newbie on C# and C++ callbacks, please let me know if I have naive mistakes made.
A callback requires the start address of the method that handles the event. So your two methods are both getting the same start address. The first method is clear since you are using the name of the method. 2nd method you have more statements that accomplishes the same as first method.
c# is managed language while c++ is un-managed. Which basically means that extra protection was added to c# to prevent PC from going blue screen. So when calling un-managed c++ from c# rules must be followed to prevent blue screen. So any pointer variables passed to a c++ method must be allocated in c# as un-managed using GCHandle.Alloc or by using other Marshal methods. Marshal.StrToPtr will also allocate the un-managed memory.

Correct way to call a C DLL method from C#

I'm trying to execute some methods (in this particular case, rdOnAllDone) from a third party DLL, written in C, and looking trough the headers files, I found this:
#ifndef TDECLSDONE
#ifdef STDCALL
#define CCON __stdcall
#else
#define CCON __cdecl
#endif
#define TDECLSDONE
#endif
#define DLLIMP __declspec (dllimport)
DLLIMP int CCON rdOnAllDone (void(CCON *)(int));
After goggling for a way to call this method, I made this:
[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(Delegate d);
public delegate void rdOnAllDoneCallbackDelegate();
private static void rdOnAllDoneCallback()
{
Console.WriteLine("rdOnAllDoneCallback invoked");
}
The method was called correctly except that I couldn't get the int parameter. So I tried adding the input parameter int like this
[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(Delegate d);
public delegate void rdOnAllDoneCallbackDelegate(int number);
private static void rdOnAllDoneCallback(int number)
{
Console.WriteLine("rdOnAllDoneCallback invoked " + number);
}
But now delegate is called twice and and it crashes the program with the following error " vshosts32.exe has stopped working"
What's the correct way to call this DLL method?
EDIT: Forgot to add the Main method:
public static void Main()
{
rdOnAllDoneCallbackDelegate del3 = new rdOnAllDoneCallbackDelegate(rdOnAllDoneCallback);
rdOnAllDone(del3);
while (true)
{
Thread.Sleep(1000);
}
}
Three things you need to do to make this work right:
you need to tell the pinvoke marshaller about the actual delegate type, using Delegate isn't good enough. That will create the wrong thunk that won't properly marshal the argument. Which is what you saw happening.
you need to tell the marshaller about the calling convention if it isn't __stdcall with the [UnmanagedFunctionPointer] attribute. Getting this wrong imbalances the stack with good odds for a hard crash.
you need to store a reference to the delegate object so that the garbage collector won't collect it. It cannot see references held by native code. Getting this wrong makes the native code fail with a hard crash after the next garbage collection.
So this ought to work better, tweak as necessary:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void rdOnAllDoneCallbackDelegate(int parameter);
[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(rdOnAllDoneCallbackDelegate d);
class Foo {
private static rdOnAllDoneCallbackDelegate callback; // Keeps it referenced
public static void SetupCallback() {
callback = new rdOnAllDoneCallbackDelegate(rdOnAllDoneCallback);
rdOnAllDone(callback);
}
private static void rdOnAllDoneCallback(int parameter) {
Console.WriteLine("rdOnAllDoneCallback invoked, parameter={0}", parameter);
}
}
Your delegates signature has to match that of the native callback, also it has to have the UnmanagedFunctionPointerAttribute set appropriately.
In your case like so:
[UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)]
public delegate void rdOnAllDoneCallbackDelegate(int parameter);
[DllImport("sb6lib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int rdOnAllDone(rdOnAllDoneCallbackDelegate callback);
Usage:
{
rdOnAllDone(rdOnAllDoneCallback);
}
private static void rdOnAllDoneCallback(int parameter)
{
Console.WriteLine("rdOnAllDoneCallback invoked, parameter={0}", parameter);
}

calling a C++ function from C# and sending data back to the C#

I have a C# application that calls functions in a DLL written in C++. The calls work fine and the code is executed using this method in the C#:
[DllImport("myDLL.dll")]
public static extern void Function();
static void Main()
{
Function();
}
Now this works, I need the executing C++ code to send back the text to the C# where it can be displayed in a panel.
The text is "stage one..." executes code...then "stage two" etc, showing the processes running. Just basic stuff. I'm not sure how to approach this as I'm not a C++ guy. My main skills are .NET.
Cheers
Try using callbacks in C++ and sign them in C#
C++ part
typedef void (CALLBACK *pfNotifyMessage)(LPTSTR);
extern "C" AFX_API_EXPORT void SetNotifyMessage(pfNotifyMessageType pfNotify);
extern "C" AFX_API_EXPORT void Function();
In C++ Function call make call of pfNotifyMessage
C# part
public delegate void NotifyMessage(string message);
[DllImport("myDLL.dll")]
public static extern void SetNotifyMessage(NotifyMessage notify);
[DllImport("myDLL.dll")]
public static extern void Function();
in ะก# assign delegate with function

Categories

Resources