非托管的C++去调用C#代码比较麻烦。需要将C#部分的接口提取出来,导出到COM,然后C++通过COM来调用它。
如果没有COM相关基础知识的话,做起来会很累。
首先是这样的C#代码:
using System;
using System.Runtime.InteropServices;
namespace ClassLibrary1
{
[ComVisible(true)]
[Guid("F3528A0F-D34F-4A5B-9849-0DCAD6212D5A")]
public interface MyInterface
{
int MyMethod(int param);
}
[Guid("32B922E0-FED2-40CC-A9D6-57FE3EE341E3")]
public class Class1 : MyInterface
{
public int MyMethod(int param)
{
Console.WriteLine("Class1.MyMethod is called!");
return param + 1;
}
}
}
定义一个接口(待会儿C++调用对象上的方法要用),然后定义了一个类(待会儿C++创建对象要用),两个GUID一个标识接口的(在C++里QueryInterface的时候用)一个标识类的(在C++里CreateInstance的时候用)。GUID可以用VisualStudio自带的工具生成,也可以自己找在线生成的网站。
ComVisible就是决定是否这个接口在COM可见,要可见待会儿C++才能进行下一步。
然后是工程,工程类型是“类库”,然后工程属性找到“生成”那里,下面的“输出”有一个“为COM互操作注册”,要勾起来。这样生成DLL的时候才会在注册表里面写入相关信息(需要你VisualStudio用管理员身份运行)。
然后这样生成的时候除了dll,还有一个tlb文件。这个tlb在C++里import,C++代码如下:
#ifdef _DEBUG
#import "..\ClassLibrary1\bin\Debug\ClassLibrary1.tlb"
#else
#import "..\ClassLibrary1\bin\Release\ClassLibrary1.tlb"
#endif
#include <objbase.h>
#include <stdio.h>
int main()
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
ClassLibrary1::MyInterface* myInterface;
IID iid;
CLSID clsid;
IIDFromString(L"{F3528A0F-D34F-4A5B-9849-0DCAD6212D5A}", &iid);
CLSIDFromString(L"{32B922E0-FED2-40CC-A9D6-57FE3EE341E3", &clsid);
HRESULT hr = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, iid, (LPVOID*) &myInterface);
if (hr == S_OK)
{
int x = myInterface->MyMethod(20);
printf("%d\n", x);
myInterface->Release();
}
else
printf("%s", "Fail to create object.\n");
CoUninitialize();
return 0;
}
我也没办法,调用com就是比较麻烦……
生成的DLL和EXE如果要拿到其他电脑上运行,那么要先在注册表里注册一下这个DLL才行。
.net Framework里面有工具,在C:\Windows\Microsoft.NET\Framework\v4.0.30319文件夹能找到一个regasm工具,就是用来注册的。比如这样:
regasm /codebase ClassLibrary1.dll
如果要注销在注册表里添加的信息,可以这样:
regasm /unregister ClassLibrary1.dll
这个regasm工具在这里的作用就相当于regsvr32,只不过它是给.net程序集用的
附件是VS2013写的工程的例子,要以管理员身份运行VisualStudio才能编译链接通过然后执行(主要是注册DLL程序集的时候要管理员权限