int Foo(String arg)
例如,此C代码加载CLR并在“test.exe”中运行P.Test函数:
ICLRRuntimeHost *pClrHost = NULL; HRESULT hrCorBind = CorBindToRuntimeEx(NULL,L"wks",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);
我需要做的是用这个方法签名调用一个函数(注意我拥有C#代码,所以我可以改变它):
void SomeFunction(IFoo interface)
其中IFoo是一个com接口.如果我可以调用这样的函数,我甚至可以做我需要的东西:
IntPtr SomeFunction();
我可以让SomeFunction构造一个正确的委托,然后使用Marshal.GetFunctionPointerForDelegate.但是,我无法弄清楚除了使用int func(string)签名调用函数之外,如何使托管接口执行任何操作.
(注意我不能使用C/C++LI.我需要使用托管API.)
解决方法
>如果有人对32位系统的不太复杂的解决方案感兴趣,我就离开了原来的答案.
注意:由于你使用的是在.net 4.0中已经过时的CorBindToRuntimeEx,我将假设使用旧版Win32 API的.net 2.0兼容解决方案.
因此,为了在C#和C之间传递数据(在我们的例子中是委托的IntPtr),我们将使用两种直接的方法创建一个名为SharedMem的小型Win32 DLL项目.
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); }
现在为实现文件:
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,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); 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; }
>请注意,为简单起见,我只是共享一个64位值,但这是在C#和C之间共享内存的一般方法.您可以随意放大SHARED_MEM_SIZE和/或添加功能,以便根据需要共享其他数据类型.
这就是我们将如何使用上述方法:我们将在C#端使用SetSharedMem(),以便将委托的IntPtr设置为64位值(无论代码是在32位还是64位系统上运行) .
C#代码
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(); } } }
>注意使用GCHandle以防止委托被垃圾收集.
>对于好的措施,我们将使用返回值作为成功/失败标志.
在C端,我们将使用GetSharedMem()提取64位值,将其转换为函数指针并调用C#委托.
C代码
#include "SharedMem.h" typedef void (*VOID_FUNC_PTR)(); void ExecCSharpCode() { ICLRRuntimeHost *pClrHost = NULL; HRESULT hrCorBind = CorBindToRuntimeEx( NULL,(PVOID*)&pClrHost ); HRESULT hrStart = pClrHost->Start(); DWORD retVal; HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain( szPathToAssembly,L"CSharpCode.COMInterfaceClass",L"EntryPoint",&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(); } } }
原始答案 – 适用于32位系统
这是一个基于使用IntPtr.ToInt32()的解决方案,以转换委托func. PTR.到从静态C#EntryPoint方法返回的int.
(*)注意使用GCHandle以防止代理被垃圾收集.
C#代码
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代码
我们需要将返回的int值转换为函数指针,因此我们首先定义一个void函数ptr.类型:
typedef void (*VOID_FUNC_PTR)();
ICLRRuntimeHost *pClrHost = NULL; HRESULT hrCorBind = CorBindToRuntimeEx( NULL,(PVOID*)&pClrHost ); HRESULT hrStart = pClrHost->Start(); DWORD retVal; HRESULT hrExecute = pClrHost->ExecuteInDefaultAppDomain( szPathToAssembly,&retVal ); if ( hrExecute == S_OK ) { VOID_FUNC_PTR func = (VOID_FUNC_PTR)retVal; func(); }
一点额外的
您还可以使用字符串输入来选择要执行的方法:
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(); }
>您可以通过使用反射来获得更多通用,以便根据给定的字符串输入找到正确的方法.