通过CLR API实现C++调用C#代码交互

时间:2022-09-01 17:53:55

对于某些跨平台程序,这也就意味着只能在windows下使用了

不过最近.Net开源或许以后可以跨平台

 

之前花了一些时间研究COM方式调用,太繁琐不推荐。 COM方式调用总结

后来尝试使用CLR C++,虽然可以用.Net的接口直接载入,但他只是一层C++的皮,最终没能运用在实际项目中。

最后使用MS提供的CLR API接口,完成调用 MSDN CLR接口介绍

 

=======================================================

有几个注意点:

1.官方一直强调的签名格式static int pwzMethodName (String pwzArgument),并非某个C#特性,而是函数格式必须是带一个string参数返回int型。否则在调用时会报找不到方法。

2.ExecuteInDefaultAppDomain中,dll路径支持相对路径格式,"xx.dll"是可以被识别的。

3.GetRuntime方法获得的.Net版本号,需要去c:/windows/Microsoft.NET下找,64位对应Framework64,普通版对应Framework。如果目标机器没有该文件夹,说明对应版本的.net未安装。那么程序就会报错。版本号第三位数字似乎是死的,应该都是统一的发行版。

4.在创建测试项目时,需要点掉预编译头选项,否则会出现许多莫名其妙的错误。

5.需要在管理员权限下使用

6.类可以是静态类或者非静态,但函数必须是静态函数

 

参考代码摘自github:

https://github.com/entice/client/blob/42f388d68b1954ef244c379560eeb4867ae80b7e/Loader/Loader/DllMain.cpp

 

具体上代码

 

C#

通过CLR API实现C++调用C#代码交互通过CLR API实现C++调用C#代码交互
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace ClassLibrary1
{
public class Class1
{
public static int EntryPoint(string arg)
{
File.AppendAllText(
"C:/xx.txt", "qweqwe");
return 0;
}
}
}
View Code

 

 

C++

通过CLR API实现C++调用C#代码交互通过CLR API实现C++调用C#代码交互
//Reference From GitHub entice:https://github.com/entice/client/blob/42f388d68b1954ef244c379560eeb4867ae80b7e/Loader/Loader/DllMain.cpp

#include
<Windows.h>
#include
<stdio.h>
#include
<iostream>
#include
<mscoree.h>
#include
<metahost.h>
#include
<assert.h>

#pragma comment(lib, "mscoree.lib")

#define BUFFER_SIZE 500
#include
"stdafx.h"


void StartTheDotNetRuntime(LPCWSTR runtimeVersion, LPCWSTR dllPath, LPCWSTR startClass, LPCWSTR startMethod, LPCWSTR startArgument)
{
ICLRMetaHost
*pMetaHost = NULL;
ICLRMetaHostPolicy
*pMetaHostPolicy = NULL;
ICLRDebugging
*pCLRDebugging = NULL;

CLRCreateInstance(CLSID_CLRMetaHost, IID_ICLRMetaHost, (LPVOID
*)&pMetaHost);
CLRCreateInstance(CLSID_CLRMetaHostPolicy, IID_ICLRMetaHostPolicy, (LPVOID
*)&pMetaHostPolicy);
CLRCreateInstance(CLSID_CLRDebugging, IID_ICLRDebugging, (LPVOID
*)&pCLRDebugging);

DWORD dwVersion
= 0;
DWORD dwImageVersion
= 0;
ICLRRuntimeInfo
*pRuntimeInfo;
HRESULT result;
result
= pMetaHost->GetRuntime(runtimeVersion, IID_ICLRRuntimeInfo, (LPVOID*)&pRuntimeInfo);
assert(SUCCEEDED(result));

ICLRRuntimeHost
* pRuntimeHost = NULL;
result
= pRuntimeInfo->GetInterface(CLSID_CLRRuntimeHost, IID_ICLRRuntimeHost, (LPVOID*)&pRuntimeHost);
assert(SUCCEEDED(result));

result
= pRuntimeHost->Start();
assert(SUCCEEDED(result));

DWORD dwRetCode
= 0;
result
= pRuntimeHost->ExecuteInDefaultAppDomain(dllPath, startClass, startMethod, startArgument, &dwRetCode);
assert(SUCCEEDED(result));
pRuntimeHost
->Stop();
pRuntimeHost
->Release();
pRuntimeInfo
->Release();
pCLRDebugging
->Release();
pMetaHostPolicy
->Release();
pMetaHost
->Release();
}

void Loader()
{
wchar_t
* runtimeVersionSpace = new wchar_t[BUFFER_SIZE];
wchar_t
* dllPathSpace = new wchar_t[BUFFER_SIZE];
wchar_t
* currentDir = new wchar_t[BUFFER_SIZE];

GetCurrentDirectoryW(BUFFER_SIZE, currentDir);

StartTheDotNetRuntime(L
"v4.0.30319", L"ClassLibrary1.dll", L"ClassLibrary1.Class1", L"EntryPoint", L"");

free(runtimeVersionSpace);
free(dllPathSpace);
free(currentDir);
}


int _tmain(int argc, _TCHAR* argv[])
{
HANDLE handle
= CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)Loader, NULL, 0, NULL);

for (size_t i = 0; i < 10; i++)
{
Sleep(
100);
}

TerminateThread(handle,
0);

return 0;
}
View Code

 

 

然后把c#生成的dll,放到c++的build目录下,再以管理员权限执行即可。

最后会在c盘根目录下创建一个xx.txt文件。说明调用C#成功