CLR host: calling a function using an arbitrary method signature?

I need to take a C ++ program, load the CLR and call the function in the C # library. The function I need to call takes in the COM interface as a parameter.

My problem is that the CLR host interface only allows you to call a method with this signature:

int Foo(String arg) 

Example: this C ++ code loads the CLR and runs the P.Test function in "test.exe":

 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(L"C:\\Test.exe", L"P", L"Test", L"", &retVal); 

I need to make a function call with this method signature (note that I have C # code, so I can change it):

 void SomeFunction(IFoo interface) 

Where IFoo is the COM interface. I could even do what I need if I could call such a function:

 IntPtr SomeFunction(); 

I could have SomeFunction build the correct delegate and then use Marshal.GetFunctionPointerForDelegate. However, I cannot figure out how to make the hosting interfaces anything other than calling the function using the int func (string) function.

Does anyone know how to call a C # function from C ++ code with a different signature?

(Note. I cannot use the C ++ / CLI for this. I need to use the hosting APIs.)

+6
source share
1 answer

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; // handle to mapped file LPVOID lpvSharedMem = NULL; // pointer to shared memory const int SHARED_MEM_SIZE = sizeof(ULONGLONG); BOOL CreateSharedMem() { // Create a named file mapping object hMappedFileObject = CreateFileMapping( INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, SHARED_MEM_SIZE, TEXT("shmemfile") // Name of shared mem file ); if (hMappedFileObject == NULL) { return FALSE; } BOOL bFirstInit = (ERROR_ALREADY_EXISTS != GetLastError()); // Get a ptr to the shared memory lpvSharedMem = MapViewOfFile( hMappedFileObject, FILE_MAP_WRITE, 0, 0, 0); if (lpvSharedMem == NULL) { return FALSE; } if (bFirstInit) // First time the shared memory is accessed? { ZeroMemory(lpvSharedMem, SHARED_MEM_SIZE); } return TRUE; } BOOL SetSharedMem(ULONGLONG _64bitValue) { BOOL bOK = CreateSharedMem(); if ( bOK ) { ULONGLONG* pSharedMem = (ULONGLONG*)lpvSharedMem; *pSharedMem = _64bitValue; } return bOK; } BOOL GetSharedMem(ULONGLONG* p64bitValue) { if ( p64bitValue == NULL ) return FALSE; BOOL bOK = CreateSharedMem(); if ( bOK ) { ULONGLONG* pSharedMem = (ULONGLONG*)lpvSharedMem; *p64bitValue = *pSharedMem; } return bOK; } 
  • 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 // 1 for success, 0 is a failure ); if ( hrExecute == S_OK && retVal == 1 ) { ULONGLONG nSharedMemValue = 0; BOOL bGotValue = GetSharedMem(&nSharedMemValue); if ( bGotValue ) { VOID_FUNC_PTR CSharpFunc = (VOID_FUNC_PTR)nSharedMemValue; CSharpFunc(); } } } 

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.
+7
source

Source: https://habr.com/ru/post/910112/


All Articles