It\'s a little hard to resume it in a single title, so here my situation.
I\'m building a C# application that loads a C++ library.
I call functions from that C++ DLL
The Unmanaged Exports project might help you with this:
https://sites.google.com/site/robertgiesecke/Home/uploads/unmanagedexports
Basically it allows you to export Functions from your assembly using the DllExport Attribute. Those functions can then be used like normal native Dll exports. It's something like the missing counterpart of the DllImport attribute.
You would then declare your method like this:
[DllExport("myCSharpFunction")]
static public void myCSharpFunction()
{
MessageBox.Show("Called from C++ code !!");
}
But also such a two-way dependency would usually look suspicious to me. Maybe it is also possible in your case to use callbacks, like ken suggested.
So ok, after a few test on starting back to zero, I finally managed to get this callback running !
So here is the test project I created to use the callback.
LIBRARY TestCallBack
EXPORTS
callCSharpFunction
__declspec(dllexport) void callCSharpFunction ( void *fctPointer(int) )
{
fctPointer(123456);
}
This C++ project is being built as a "DLL" file and put within a "lib" project in the C#'s project folder.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace ConsoleApplication1
{
class Program
{
// Set the library path
const string dllFilePath =
"C:\\PathToProject\\TestCallBack\\ConsoleApp\\lib\\TestCallBack.dll";
// This is the delegate type that we use to marshal
// a function pointer for C to use. You usually need
// to specify the calling convention so it matches the
// convention of your C library. The signature of this
// delegate must match the signature of the C function
// pointer we want to use.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void FunctionPointer( int nb);
// This is the function we import from the C library.
//[DllImport(dllFilePath)]
[DllImport(dllFilePath, CallingConvention = CallingConvention.Cdecl)]
public static extern void callCSharpFunction(IntPtr fctPointer);
// This is the placeholder for the function pointer
// we create using Marshal.GetFunctionPointerForDelegate
static IntPtr FctPtr;
// This is the instance of the delegate to which our function
// pointer will point.
FunctionPointer MyFunctionPointer;
// This calls the specified delegate using the C library.
void CallFunctionPointer(FunctionPointer cb)
{
// make sure the delegate isn't null
if (null == cb) throw new ArgumentNullException("cb");
// set our delegate place holder equal to the specified delegate
MyFunctionPointer = cb;
// Get a pointer to the delegate that can be passed to the C lib
FctPtr = Marshal.GetFunctionPointerForDelegate(MyFunctionPointer);
// call the imported function with that function pointer.
callCSharpFunction(FctPtr);
}
static void Main(string[] args)
{
// This is the instance of the delegate to which our function
// pointer will point.
FunctionPointer printInConsoleDelegate;
// Create the delegate object "MyFunctionPointer" that references
printInConsoleDelegate = new FunctionPointer(printInConsole);
// Get a pointer to the delegate that can be passed to the C lib
IntPtr printInConsolePtr =
Marshal.GetFunctionPointerForDelegate(printInConsoleDelegate);
Console.WriteLine(
"Call C++ which's calling back C# func \"printInConsole\"");
// Call C++ which calls C#
callCSharpFunction(printInConsolePtr);
// Stop the console until user's pressing Enter
Console.ReadLine();
}
public static void printInConsole(int nb)
{
// Write the parameter in the console
Console.WriteLine("value = " + nb + "; ");
}
}
}
This can be done, and overall not that big of a deal. However there are several points of consideration.
Since you said C++ and not C, note that besides static class, instance methods, friend functions, etc, there are still some functions that are not loadable via DllImport due to name mangling. Avoiding COM, wrapping C++ in C is occasionally used as a more portable strategy to allows libraries to be wrapped by other languages .Net or otherwise. Similarly some of these same considerations apply to callbacks from the wrapped library as in your case. Although the prototypes from your DLL don't seem to likely be a problem, if it really was built with a C++ compiler it might be worth looking at the exported symbol names to make sure.
Tell help with your searching, you need to know the vocabulary. Generally when a function takes as a parameter another function to be called either during the function invocation or later on, that is refereed to as a callback. In the world of C and C++ this is based around a language feature known as function pointers (which also serve purposes other than callbacks). Often in MS documentation the overall process of letting different functions be dynamically bound to different callers at runtime is called delegation. The C# equivalent of a function pointer are objects known as delegates created with the C# keyword delegate. I would recommend first creating some experiment programs in just C# using this feature to first understand how that works.
Also DllImport is part of the implementation, the actual .Net facility you are using is pinvoke. That should also when looking for more info. What you then want to do is to export your delegate with pinvoke by marshaling it as a function pointer. That is, .Net will create a shim function for the native code to run. Two big problem areas that often take several tries are 1) making sure this shim function / marshaled delegate itself has the correct calling convention, and 2) that the object lifetime of this behind-the-scenes shim function is such that it still exists when the native DLL is going to use it. This curve ball can sometimes require manually overriding the garbage collection behavior, but usually this means just keeping a reference to it.
I actually think that the Mono Documentation is way better than MSDN in this area also.
Here's my answer to this question, which was similar. My example doesn't use arguments for the callback. Since your arguments are integers, though, you shouldn't have any problems.
Basically, the Marshal.GetFunctionPointerForDelegate
method creates an IntPtr
from a delegate. That delegate should have the same signature as the function pointer used in your C++ library.
// This is the delegate type that we use to marshal
// a function pointer for C to use. You usually need
// to specify the calling convention so it matches the
// convention of your C library. The signature of this
// delegate must match the signature of the C function
// pointer we want to use.
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
delegate void FunctionPointer();
// This is the function we import from the C library.
[DllImport(dllFilePath)]
static extern void callCSharpFunction(IntPtr fctPointer);
// This is the placeholder for the function pointer
// we create using Marshal.GetFunctionPointerForDelegate
IntPtr FctPtr;
// This is the instance of the delegate to which our function
// pointer will point.
FunctionPointer MyFunctionPointer;
// This calls the specified delegate using the C library.
void CallFunctionPointer(FunctionPointer cb)
{
// make sure the delegate isn't null
if (null == cb) throw new ArgumentNullException("cb");
// set our delegate place holder equal to the specified delegate
MyFunctionPointer = cb;
// Get a pointer to the delegate that can be passed to the C library
FctPtr = Marshal.GetFunctionPointerForDelegate(MyFunctionPointer );
// call the imported function with that function pointer.
callCSharpFunction(FctPtr);
}