C#调用COM接口的三种方式

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签名转换,让错误信息可以被正常接收到:

[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;
    }

 

  • 4
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值