C#开发COM组件2

时间:2022-08-30 18:09:18

1.    概述

       Microsoft在解决和以往的COMSDK开发技术之间的互操作性(Interoperability)方面做了很多的工作,其中包括COM.NET对象之间的相互调用以及.NET如何调用SDK中的Windows库函数。本文试图用一种实践的方式探索COM.NET对象之间的互操作(Interoperability)的实现以及相关的技术。

2.    发展历史

       在面向对象技术发展的初期开始暴露出来了很多的问题,其中一个就是随着系统的复杂度不断上升,系统的类开始出现爆炸,而且对象之间的调用依赖无法在一个更高的抽象层次上彼此之间的松散耦合,这也因此导致了这种基于源代码的复用方式变得非常脆弱。基于组件的开发技术很好地解决了上面的问题,它在组件(某个功能的聚合,是一个类的集合)这个层次上实现了抽象和复用。在这个时期,COM(Component Object Model)技术作为这么一种方法论的实现方式开始展现它独特的魅力,而Microsoft则带领着IT界把这一技术推向了稳定、成熟。

       而到了20世纪90年代末期,随着商业逻辑处理的复杂化以及很多个性化的需求的出现,软件复杂度也开始提高,COM技术在应用技术领域也开始显得力不从心了。在这个时候就需要一种更灵活的、基于标准的技术来支撑应用软件的开发,.NET作为一个具有跨时代意义的应用软件的开发平台悄然而至。.NET以一种基于平台方式(应用软件依赖于某个平台,可移植性依赖于平台),又在一个更高的层次上实现了软件的复用。

3.    问题描述

       为了保持.NETCOM技术的可互操作性,Microsoft提供一个专门的组件来解决这个问题,可以参考命名空间System.Runtime.InteropService。它不为.NET对原有的COM组件的调用提供了一种有效的方式,同时也为用.NET编写COM组件提供一种便利。

4.    步骤

Step1. 构建.NET组件,并设置它COM组件可见性

Step2. 定义COM组件接口

Step3. 实现COM组件接口

Step4. 部署

Step5. 测试

Step 1构建.NET组件

n         新建一个.NET组件,如下图:

n         设置这个.NET组件的COM可见性

设置工程属性:“生成”-> “为COM Interop注册

当然也可以为每个接口设置COM可见性,ComVisibleAttribute类提供了这样的控制。

Step 2定义组件接口

       每个COM组件接口都有一个唯一的GUID,在.NET接口的定义中由Guid属性来指定

       在这个例子中我定义了如下的接口:

 

    [TypeLibType(4160)]
    [Guid("F0485D81-59C1-44b1-9316-D474E26C026E")]
    public interface IXMPReader
    {
        [DispId(0)]
        string Metadata { get;set;}
        [DispId(1)]
        IXMPTemplate ReadTemplate(string xPath);
    }

 

Step 3实现组件接口

每个COM组件接口的实现类也都有一个唯一的GUID,由Guid属性来定定义,

IDL中对组件类的定义需要有一个Default(默认实现的接口声明), .NET组件中由ComDefaultInterface属性来定义。

在这个例子中我是这样实现了这个接口的,如下:

  

namespace XMPTemplate
{
    [TypeLibType(4160)]
    [Guid("AD665240-9E4D-4c30-9475-023EA44E41BD")]
    [ComDefaultInterface(typeof(IXMPReader))]
    [ComSourceInterfaces(typeof(IXMPReader))]
    public class XMPReader : IXMPReader
    {
        private string _metadata="";
        public string Metadata
        {
            get { return _metadata; }
            set { _metadata = value; ; }
        }
        public IXMPTemplate ReadTemplate(string xPath)
        {
                XmlDocument document = new XmlDocument();
                document.LoadXml(Metadata);
                XmlNode node = document.SelectSingleNode(xPath);
                XMPTemplate template = new XMPTemplate();
                template.LoadXml(node.InnerXml);
                return template;
}
    }

 

 

 

 

  

 

 

 

 

Step 4部署

       假设我们生成的.NET组件的名称是XMPTemplate.dll,访问路径为C:\ XMPTemplate.dll

n         利用Regasm工具来注册.NET组件

regasm C:\ XMPTemplate.dll

n         将这个.NET组件加入到全局程序集缓存中

Gacutil /I C:\ XMPTemplate.dll

Step 5 测试

n         VC++调用

 

const wchar_t* progid = L"XMPTemplate.XMPReader";

CLSID clsid = CLSID_NULL;

HRESULT hr = CLSIDFromProgID(progid,&clsid);

IDispatch* pDispatch = NULL;

CoCreateInstance(clsid,NULL,CLSCTX_INPROC,IID_IDispatch,(void**)&pDispatch);

ATL::CComVariant varMetadata = tenant->GetMedata().c_str();

ATL::CComVariant varResult;

DISPPARAMS params = {&varMetadata, NULL, 1, 0 };

pDispatch->Invoke(0, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYPUT, &params, &varResult, NULL, NULL);

varResult.Clear();

ATL::CComVariant varXPath = L"\\";

DISPPARAMS paramsXPath ={&varXPath, NULL, 1, 0 };

pDispatch->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_PROPERTYGET, &paramsXPath, &varResult, NULL, NULL);

pDispatch->Release();

 

5.    源代码