c# – CLR主机:使用任意方法签名调用函数?
我需要一个C程序,加载CLR并调用C#库中的函数.我需要调用的函数将COM接口作为参数.
我的问题是,CLR托管接口似乎只允许您使用此签名调用方法: 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#函数? (注意我不能使用C/C++LI.我需要使用托管API.) 解决方法
编辑:我答应更新我的答案,包括传递64位值的代码,所以这里是..
>如果有人对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代码 typedef void (*VOID_FUNC_PTR)(); 其余代码看起来非常像原始代码,添加了转换和执行函数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(); } >您可以通过使用反射来获得更多通用,以便根据给定的字符串输入找到正确的方法. (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |