编辑:我承诺更新我的答案以包含传递 64 位值的代码,所以这里是..
- 如果有人对 32 位系统的不太复杂的解决方案感兴趣,我会留下原始答案。
注意:由于您使用CorBindToRuntimeEx
的是 .net 4.0 中已过时的 .net 2.0 解决方案,因此我将假设使用良好的旧 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,
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;
}
- 请注意,为简单起见,我只是共享一个 64 位值,但这是在 C# 和 C++ 之间共享内存的一般方式。随意放大 SHARED_MEM_SIZE 和/或添加函数,以便共享您认为合适的其他数据类型。
这就是我们使用上述方法的方式:我们将SetSharedMem()
在 C# 端使用,以便将委托设置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++ 端,我们将使用 提取 64 位值GetSharedMem()
,将其转换为函数指针并调用 C# 委托。
C++ 代码
#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();
}
}
}
原始答案 - 适用于 32 位系统
这是一个基于使用IntPtr.ToInt32()
以转换委托函数的解决方案。点。int
从静态 C# EntryPoint 方法返回的。
(*) 注意使用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)();
其余的代码看起来很像你的原始代码,除了转换和执行函数 ptr。
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();
}
一点额外的
您还可以使用string
输入来选择要执行的方法:
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();
}
- 您可以通过使用反射来获得更通用的方法,以便根据给定的字符串输入找到正确的方法。