Edit: I promised to update my answer to include code for transmitting 64-bit values, so here it goes.
- I left the original answer if someone is interested in a less complicated solution for a 32-bit system.
Note. . Since you are using CorBindToRuntimeEx
, which is deprecated in .net 4.0, I will CorBindToRuntimeEx
.NET solution with support for the old old Win32 API.
So, to transfer data between C # and C ++ (in our case, the IntPtr
delegate), we will create a small Win32 DLL project called SharedMem with two direct transfer methods.
SharedMem.h
#pragma once #ifdef SHAREDMEM_EXPORTS #define SHAREDMEM_API __declspec(dllexport) #else #define SHAREDMEM_API __declspec(dllimport) #endif #define SHAREDMEM_CALLING_CONV __cdecl extern "C" { SHAREDMEM_API BOOL SHAREDMEM_CALLING_CONV SetSharedMem(ULONGLONG _64bitValue); SHAREDMEM_API BOOL SHAREDMEM_CALLING_CONV GetSharedMem(ULONGLONG* p64bitValue); }
Now for the implementation file:
SharedMem.cpp
#include "stdafx.h" #include "SharedMem.h" HANDLE hMappedFileObject = NULL;
- Please note that for simplicity, I just use a 64-bit value, but this is a general way of exchanging memory between C # and C ++. Feel free to extend SHARED_MEM_SIZE and / or add functions to share other data types as you see fit.
Here's how we will use the above methods: we will use SetSharedMem()
on the C # side to set the IntPtr
delegate as a 64-bit value (regardless of whether the code is running on a 32 or 64-bit system).
C # code
namespace CSharpCode { delegate void VoidDelegate(); static public class COMInterfaceClass { [DllImport( "SharedMem.dll" )] static extern bool SetSharedMem( Int64 value ); static GCHandle gcDelegateHandle; public static int EntryPoint(string ignored) { IntPtr pFunc = IntPtr.Zero; Delegate myFuncDelegate = new VoidDelegate( SomeMethod ); gcDelegateHandle = GCHandle.Alloc( myFuncDelegate ); pFunc = Marshal.GetFunctionPointerForDelegate( myFuncDelegate ); bool bSetOK = SetSharedMem( pFunc.ToInt64() ); return bSetOK ? 1 : 0; } public static void SomeMethod() { MessageBox.Show( "Hello from C# SomeMethod!" ); gcDelegateHandle.Free(); } } }
- Pay attention to using
GCHandle
to prevent delegates from gathering. - For good measures, we will use the return value as a success / failure flag.
On the C ++ side, we will extract the 64-bit value using GetSharedMem()
, convert it to a function pointer, and call the C # delegate.
C ++ code
#include "SharedMem.h" typedef void (*VOID_FUNC_PTR)(); void ExecCSharpCode() { ICLRRuntimeHost *pClrHost = NULL; HRESULT hrCorBind = CorBindToRuntimeEx( NULL, L"wks", 0, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (PVOID*)&pClrHost ); HRESULT hrStart = pClrHost->Start(); DWORD retVal; HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain( szPathToAssembly, L"CSharpCode.COMInterfaceClass", L"EntryPoint", L"", &retVal
Original answer is good for 32-bit systems
Here's a solution based on using IntPtr.ToInt32()
to convert the delegate function. PTR to an int
that returns from a static C # EntryPoint method.
(*) Note the use of GCHandle
to prevent delegates from gathering.
C # code
namespace CSharpCode { delegate void VoidDelegate(); public class COMInterfaceClass { static GCHandle gcDelegateHandle; public static int EntryPoint(string ignored) { IntPtr pFunc = IntPtr.Zero; Delegate myFuncDelegate = new VoidDelegate( SomeMethod ); gcDelegateHandle = GCHandle.Alloc( myFuncDelegate ); pFunc = Marshal.GetFunctionPointerForDelegate( myFuncDelegate ); return (int)pFunc.ToInt32(); } public static void SomeMethod() { MessageBox.Show( "Hello from C# SomeMethod!" ); gcDelegateHandle.Free(); } } }
C ++ code We need to convert the return value of int
to a function pointer, so we will start by defining the void vtr function. A type:
typedef void (*VOID_FUNC_PTR)();
And the rest of the code looks just like your source code, with the addition of conversion and the execution of the ptr function.
ICLRRuntimeHost *pClrHost = NULL; HRESULT hrCorBind = CorBindToRuntimeEx( NULL, L"wks", 0, CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (PVOID*)&pClrHost ); HRESULT hrStart = pClrHost->Start(); DWORD retVal; HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain( szPathToAssembly, L"CSharpCode.COMInterfaceClass", L"EntryPoint", L"", &retVal ); if ( hrExecute == S_OK ) { VOID_FUNC_PTR func = (VOID_FUNC_PTR)retVal; func(); }
A little extra
You can also use the string
input to select which method to execute:
public static int EntryPoint( string interfaceName ) { IntPtr pFunc = IntPtr.Zero; if ( interfaceName == "SomeMethod" ) { Delegate myFuncDelegate = new VoidDelegate( SomeMethod ); gcDelegateHandle = GCHandle.Alloc( myFuncDelegate ); pFunc = Marshal.GetFunctionPointerForDelegate( myFuncDelegate ); } return (int)pFunc.ToInt32(); }
- You can get a more general character by using reflection to find the correct method according to the given string input.