gpt4 book ai didi

c# - CLR 托管 : Call a function with an arbitrary method signature?

转载 作者:太空狗 更新时间:2023-10-29 20:17:52 26 4
gpt4 key购买 nike

我需要获取 C++ 程序、加载 CLR 并调用 C# 库中的函数。我需要调用的函数将 COM 接口(interface)作为参数。

我的问题是,CLR 托管接口(interface)似乎只允许您调用具有此签名的方法:

int Foo(String arg)

例如,此 C++ 代码加载 CLR 并运行“test.exe”中的 P.Test 函数:

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);

我需要做的是用这个方法签名调用一个函数(注意我拥有 C# 代码,所以我可以更改它):

void SomeFunction(IFoo interface)

其中 IFoo 是一个 com 接口(interface)。如果我可以调用这样的函数,我什至可以做我需要做的事情:

IntPtr SomeFunction();

我可以让 SomeFunction 构造一个正确的委托(delegate),然后使用 Marshal.GetFunctionPointerForDelegate。但是,除了调用具有 int func(string) 签名的函数之外,我不知道如何让托管接口(interface)执行任何操作。

有谁知道如何从具有不同签名的 C++ 代码调用 C# 函数?

(注意我不能为此使用 C++/CLI。我需要使用托管 API。)

最佳答案

编辑:我 promise 会更新我的答案以包含用于传递 64 位值的代码,所以这里是..

  • 如果有人对 32 位系统的不太复杂的解决方案感兴趣,我会留下原来的答案。

注意:因为您使用的是 CorBindToRuntimeEx ,在 .net 4.0 中已过时,我假设一个使用良好的旧 Win32 API 的 .net 2.0 兼容解决方案。

因此,为了在 C# 和 C++ 之间传递数据(在我们的例子中是委托(delegate)的 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# 端设置委托(delegate)的 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 的使用为了防止委托(delegate)被垃圾回收。
  • 为了获得良好的衡量标准,我们将使用返回值作为成功/失败标志。

在 C++ 方面,我们将使用 GetSharedMem() 提取 64 位值,将其转换为函数指针并调用 C# 委托(delegate)。

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() 的解决方案为了转换委托(delegate)函数。指针。到 int这是从静态 C# EntryPoint 方法返回的。

(*) 注意 GCHandle 的使用为了防止委托(delegate)被垃圾回收。

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 函数指针开始。输入:

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();
}
  • 您可以通过使用反射来获得更通用的功能,以便根据给定的字符串输入找到正确的方法。

关于c# - CLR 托管 : Call a function with an arbitrary method signature?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9591579/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com