1.准备工作
使用OLEView.exe查看本机安装的COM组件CLSID和接口Guid
COM组件的提供者没有提供CLSID等信息或信息提供不全时,可以使用OleView.exe来查看其类和接口的GUID. OLEView.exe全称是OLE-COM Object Viewer,是Microsoft SDK中包含的一个工具,可以用来查看本机安装的所有COM组件的信息,包括CLSID、ProgID等。如果是完整的开发环境,其位置在C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin,非开发环境下也可以到网上下载该程序和它依赖的一个IVIEWERS.DLL.
在All Objets中查看COM组件的CLSID,右键可以直接复制CLSID. 右键选中项,点击ViewTypeInfo可查看详细接口列表和接口的Guid。本次使用的COM组件只有一个callJson接口,一个字符串类型的输入参数([in]),一个输出参数作为返回值([out,retval])。
2.三种不同的引用方式
方式1 使用tlbimp生成类库直接引用
常见的COM组件是dll形式,在VS中可以直接添加引用,VS会自动生成一个Interop.xxx.dll类库。项目中使用的第三方COM组件是exe形式,可以使用tlbimp.exe手动转换。在Visual Studio的工具库中找到并打开VS的命令提示符,按格式输入指令tlbimp FILENAME /out:OUTPUT
生成类库,类库生成之后就可以在VS中添加引用,然后像其他类库一样直接调用其中的接口。
方式2 使用CLSID和反射动态调用
已知COM类的CLISID时可以直接使用Type.GetTypeFromCLSID方法来获取COM组件的Type,然后动态创建对象调用接口。
private string CallJson(string input)
{
string ret = "";
Type dycomType = Type.GetTypeFromCLSID(new Guid("xxxxxx"));
if (dycomType != null)
{
//创建类实例
dynamic dycomObject = Activator.CreateInstance(dycomType);
//调用
ret = dycomObject?.callJson(input);
}
return ret;
}
也可以使用ProgId来获取:
Type t = Type.GetTypeFromProgID("TestComServer.TestComVisibleClass");
dynamic o = Activator.CreateInstance(t);
方式3 ComImport方式引用
使用ComImport标签引用COM组件,需要写明类的Guid和接口的Guid:
[ComImport, Guid("xxxxxxxxxxxx")]
class DycomObject
{
}
// Declare IMediaControl as a COM interface which
// derives from the IDispatch interface.
[Guid("xxxxxxxx"),
InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
interface IDycomObject // cannot list any base interfaces here
{
// Note that the members of IUnknown and Interface are NOT
// listed here
//
[return: MarshalAs(UnmanagedType.BStr)]
object callJson(
[In, MarshalAs(UnmanagedType.BStr)] string request);
}
调用:
private string CallJson(string input)
{
string ret = "";
Console.WriteLine("创建类实例");
DycomObject dy = new DycomObject();
IDycomObject iDy = (IDycomObject)dy;
Console.WriteLine("调用");
ret = (string)iDy.callJson(input);
return ret;
}
实际项目中的问题
上述三种方式的接口格式都是一个输入参数对应COM接口的[in]参数,一个返回值对应[out,retval]参数,按照第三方接口描述,返回值中包含接口调用结果和错误码。实际使用中发现,调用出错时,接口会返回一个非零的HRESULT,同时把错误信息传给[out,retval]参数,但.Net会把非零的HRESULT转换为一个异常抛出,导致以上三种写法的返回值都为null,无法获取错误信息。在社区发帖问到了解决方法,就是需要修改方式3的引用方式,使用[PreserveSig]取消COM互操作期间的HRESULT和retval签名转换,让错误信息可以被正常接收到:文章来源:https://www.toymoban.com/news/detail-441068.html
[ComImport, Guid("xxxxxx")]
class DycomObject
{
}
[ComVisible(true), ComImport, Guid("xxxxxx")]
interface IDycomObject
{
[PreserveSig]
int callJson(
[In,MarshalAs(UnmanagedType.BStr)]
string request,
[Out,MarshalAs(UnmanagedType.BStr)]
out string response);
}
private string CallJson(string input)
{
string ret = "";
Console.WriteLine("创建类实例");
DycomObject dy = new DycomObject();
IDycomObject iDy = (IDycomObject)dy;
Console.WriteLine("调用");
var code = iDy.callJson(input,out ret);
return ret;
}
文章来源地址https://www.toymoban.com/news/detail-441068.html
到了这里,关于C#调用COM接口的三种方式的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!