创建C#项目
创建C#项目时选择的.net版本需要与运行环境中的.net版本相对应
1、COM组件方式
COM组件都是通过调用接口的形式实现功能调用,使用编写dll需要定义接口,继承接口实现功能。
C#代码示例
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
namespace ClassLibrary4
{
//每个接口或类需要一个唯一的uuid,作为调用时的唯一标识
[Guid("13F7B841-AFA6-4278-AF04-F77E86A5F8D3")]
public interface myInterface
{
[DispId(1)]//说是每多一个方法就要写一个这个(1)、(2)...不写好像也没事
int add(int a, int b);
}
[Guid("543E9431-FE73-43F7-84AA-10687D87603E")]
public class Class1 : myInterface
{
public int add(int a, int b)
{
return a + b;
}
}
}
调用COM组件时,查找其中方法都是通过唯一的uuid,使用dll中每个接口或类都需要唯一的uuid。uuid创建:【工具】——》【创建GUID】,选择5,复制即可
生成强名称密钥文件
这一步时为了给COM组件加密
以管理员权限运行VS的命令提示符,在开始菜单 —> developer for PowerShell for VS 2019,输入 sn -k D:\ClassLibrary3.snk,(输出目录可以更换),运行成功得到加密文件,将加密文件复制到项目的解决方案同级目录下。
配置生成COM组件
a)【项目】—>【属性】—>【应用程序】—>【程序集信息】—>勾选【使程序集COM可见】,也可以到AssemblyInfo.cs文件中,将[assembly: ComVisible(false)]属性改为true
b)【项目】—>【属性】—>【生成】—>勾选【为COM互操作注册】
c)【项目】—>【属性】—>【签名】—>勾选【为程序集签名】—>【选择强名称密钥文件】—>【浏览】,选择②中生成的强名称密钥文件,在AssemblyInfo.cs文件中添加[assembly:AssemblyKeyFile("ClassLibrary4.snk")]
配置完成后,点击运行在debug目录下就会生成dll文件和tlb文件
将COM组件注册到注册表中(把GUID导入注册表)
a)以管理员方式运行命令提示符,到目录C:\Windows\Microsoft.NET\Framework64\v4.0.30319(根据自己的.net版本调整)输入
regasm dll路径\dllName.dll
regasm dll路径\dllName.dll /tlb:dllName.tlb
显示注册成功即可,每个.net版本都有对应的regasm.exe程序,.net版本不对会导致注册不成功。
b)以管理员方式运行命令提示符,到目录C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8.1 Tools(注意区分版本),输入gacutil 指令,将dl加入到缓存中,显示Assembly successfully added to the cache,即成功。
gacutil /i dll路径\dllName.dll
另外还有regasm /regfile指令可以生成COM组件的注册表文件,可以使用该注册表文件在其他电脑上直接注册COM组件(该步骤可选)
regasm dll路径\dllName.dll /regfile: CalcClass.reg
到此完成dll文件生成。
调用
C++项目示例代码
#include <iostream>
#include<Windows.h>
#include<atlcomcli.h> //导入tlb文件关键引用
#import "ClassLibrary4.tlb"
using namespace ClassLibrary4;
int main()
{
CoInitialize(NULL);
myInterfacePtr dll(__uuidof(Class1));//根据Class1的GUID生成一个智能指针
int a = dll->add(4, 5);//使用COM中函数
std::cout << a ;
getchar();
return 0;
}
项目配置
【项目】——》【属性】——》【配置属性】——》【高级】——》【启用托管增量生成】——》【否】
配置完成后将C#项目中生成的.tlb文件复制到C++项目解决方案同级目录下,启动程序,在debug目录下会生成.tlb对应的.tlh和.tli文件
运行结果
2、clr工程方式
项目创建方式与COM组件方式一致
C#示例代码
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ClassLibrary3
{
public class Class1
{
public Class1() { }//必须声明构造函数
public int Add(int a, int b)
{
return a + b;
}
}
}
编写完成后成功运行项目,即dll生成完成,将生成的dll文件复制到调用该dll文件项目的exe文件同级目录下。
C++项目示例代码
#include <iostream>
//通过绝对路径引入dll文件
#using "D:\ProjectP\VS\dllConnectTest1\x64\Debug\ClassLibrary3.dll"
using namespace ClassLibrary3;
int main()
{
Class1^ dll = gcnew(Class1); //创建dll中的对象指针
int result = dll->Add(4,5);//调用dll对象中的方法
std::cout << result;
getchar();
return 0;
}
配置C++项目
a)【项目】-》【属性】-》【配置】-》【配置】-》【高级】-》【公共语言运行时支持】-》【公共语言运行时支持 (/clr)】
b)【项目】-》【属性】-》【配置】-》【C/C++】-》【语言】-》【符合模式】-》【否(/premissive)】
调用成功回生成dll对应的一些文件
测试结果
后续问题
- 后续项目中再次使用C#编写的COM组件时发现有些电脑无法注册成功,原因是电脑的系统版本,COM组件生成时建议生成为32位的版本,比较兼容,相对应的在注册COM组件时,需要修改regasm.exe程序的目录为“C:\Windows\Microsoft.NET\Framework\v4.0.30319”,64位版本为“Framework64”。
在查找资料的过程中,发现有些文中提到了COM组件的自动注册,比如调用COM组件的DllRegisterServer接口,实现注册,利用regsvr32注册实际上也是调用COM组件的DllRegisterServer接口,但是以上方式都只适用于C++编写的COM组件,C#的COM组件不实现DllRegisterServer接口,所以用regsvr32没办法注册。如果调用COM组件的程序也是C#编写的可以尝试使用IRegistrationServices.RegisterAssembly 方法实现自动注册,否则只能使用regasm.exe手动注册,本文中的方法就是该方法,比较繁琐。 - 开发过程中重载了一些接口,但是在调用时发现抱错,无法调用,查看后发现在COM组件中如果方法名一样,会自动为重名的方法接口重命名,如COM组件中有接口Add(int a, int b)和Add(int a, int b, int c),在生成的dll中会将其中一个Add接口重命名为 Add_2()
免注册方案
注册是为了将COM组件写入到注册表中,程序运行时通过CUID到注册表中查找对象或者方法,由此可以通过程序清单文件的方式让程序获取COM组件中的接口。
程序清单文件
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="dllName" version="1.0.0.0" processorArchitecture="msil">
</assemblyIdentity>
<clrClass
clsid="{dllName.接口对应的实现类的GUID}"
progid="dllName.接口对应的实现类" threadingModel="Both"
name="dllName.接口对应的实现类" runtimeVersion="v4.0.30319">
</clrClass>
<file name="dllName.dll" hashalg="SHA1">
</file>
</assembly>
程序清单文件需要和dll一起放到调用dll的程序的生产路径下。如果是在VC6构建的mfc项目下,需要把生成的.tlb文件一起放到debug或者release目录下,如果出现“未注册类”,可以尝试用x86环境生成dll再调用。
以上为本人开发过程中的一些总结,理论部分没有深入探索,如果有错误,请各位大佬指正