您当前的位置: 首页 >  c#

光怪陆离的节日

暂无认证

  • 2浏览

    0关注

    1003博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

C#程序设计用反射动态创建COM对象

光怪陆离的节日 发布时间:2022-05-19 17:10:26 ,浏览量:2

C#中有时需要和COM组件打交道,创建COM对象比较常用的是引入dll文件,但有些不能直接引入,但如果知道COM对象的GUID或ProgID信息,可以用反射动态方式创建COM对象。

  1. 操作流程 1.1. 创建JetEngine的COM对象代码实例 JetEngine组件提供对Jet Engine-specific特定功能的控制,例如压缩数据库和刷新缓存中的数据。 动态创建COM对象这种方法,包括使用Type.GetTypeFromCLSID和Type.GetFromProgID两种方法获取COM对象的Type再创建.这种方式也好理解,就是说使用这两个方法之前,必须得知道COM对象的GUID或ProgID,好在这也不是什么难事,一般我们要使一个COM对象,多多少少都了解一些这个COM对象的GUID或ProgID信息.用这种方获取到了一个Type对象后,就可以用.NET里面通用的反射创建对象的方法来做了. 这里给出一个创建JetEngine 的COM对象的代码实例: public object GetActiveXObject(Guid clsid) { Type t = Type.GetTypeFromCLSID(clsid); if (t == null) return null;

    return Activator.CreateInstance(t); }

Guid g = new Guid(“DE88C160-FF2C-11D1-BB6F-00C04FAE22DA”); // JetEngine object jet = GetActiveXObject(g); 上述代码根据GUID获取COM组件的对象类型,最后 jet 就是返回了COM组件的实例化对象。 实际调试过程如下所示: 在这里插入图片描述

可以看到返回类型是System._comObject 在这里插入图片描述 在这里插入图片描述

1.2. 创建DTE的实例对象代码 以下代码通过ProgID方式获取DTE实例对象 //获取DTE版本 ProgID = GetRegistryValue(“VisualStudio.DTE\CurVer”,“”); //若有Visual Stdio 开发环境则选择相应的DTE Type t; if (ProgID != “”) t = Type.GetTypeFromProgID(ProgID); else t = Type.GetTypeFromProgID(“TcXaeShell.DTE.15.0”); //若没有VS开发环境,只有XAE则选择这个 dte = (DTE2)Activator.CreateInstance(t); //创建类型t的实例对象

至此,COM对象的GUID或ProgID信息,可以用反射动态方式创建COM对象测试完成。

附加说明 所谓在 C# 中创建 COM 对象,实际上就是向 COM 公开用 C# 编写的组件对象接口。这个 C# 类及其成员必须遵循下列规则才能对 COM 可见:

1> 类必须是公共的。 2> 属性、方法和事件必须是公共的。 3> 属性和方法必须在类接口上声明。 4> 事件必须在事件接口中声明。

其他没有在这些接口中声明的类的公共成员对于 COM 是不可见的,但它们对于其他 .net Framework 对象将是可见的。

若要向 COM 公开属性和方法,必须在类接口上声明这些属性和方法,并用 DispId 属性予以标记,然后在类中实现它们。成员在接口中声明的顺序即是用于 COM vtable 的顺序。

若要从类中公开事件,必须在事件接口上声明这些事件,并用 DispId 属性予以标记。该类不应实现此接口。(这里所指的事件实际上应该是事件处理函数,如果声明成字段,即一个变量,则这个事件不被 COM 所见)

类实现类接口(它可以实现一个以上的接口,但第一个实现将作为默认类接口)。在此处实现向 COM 公开的方法和属性。它们必须标记为是公共的,并且必须与类接口中的声明匹配。同时,在此处声明由类引发的事件。它们必须标记为是公共的,并且必须与事件接口中的声明匹配。

以下是一个把一个 C# 类公布到 COM 的例子:

// 首先要添加以下引用,它提供了如 DispId 、Guid 等属性的支持 using System.Runtime.InteropServices;

// 接口的声明,每个接口都需要使用 Guid 属性进进标识 [Guid(“694C1820-04B6-4988-928F-FD858B95C880”)] // GUID 属性 public interface IMyComInterface { [DispId(1)] // 接口中的每个方法和属性都需要用 DispId 属性标识 void Hello(string name, int nParam); [DispId(2)] bool Hello2(string name); [DispId(3)] string Name {get;set;} }

// 为了便于在 COM 客端 (VB 6.0 程序) 中使用这个自定义的事件参数类型 // 在这里为它声明了一个接口, 这个接口声明了若干属性以访问事件参 // 数类型中的成员 [Guid(“E65B4846-BBEC-484f-85DC-088B407DB9DC”)] public interface IMyEventArgs {

int nParam { get; set; }

string strName { get; set; } }

// 自定义的事件参数型,必须从 EventArgs 从派生,这里还继承了 // 一个接口,目的是便于在 COM 客户端中提前绑定事件参数类型对象 [Guid(“9815F614-83DF-4b4f-8302-ABA23365DB84”)] [ClassInterface(ClassInterfaceType.None)] public class MyEventArgs : EventArgs, IMyEventArgs { // 通常会定义一个带参数的构造函数,用以作为事件参数传递之用 // 当然这个派生类中实现更多的成员也是可以的 public MyEventArgs(string strParam, int nParam) { this.m_strParam = strParam; this.m_nParam = nParam; } public string m_strParam; public int m_nParam;

public int nParam { get { return m_nParam; } set { m_nParam = value; } }

public string strName { get { return m_strParam; } set { m_strParam = value; } } }

// 声明一个代理事件类型,这是一个带参数的事件类型 public delegate void MyEventHandler(object sender, EventArgs e); // 声明事件接口,事件接口除了要使用 Guid 来标识外,还需要指定为 IDispatch 类型 [Guid(“47C976E0-C208-4740-AC42-41212D3C34F0”), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] public interface IMyEventsInterface { [DispId(1)] event MyEventHandler myEvent; // 在接口中声明一个事件(字段) // 注意,以这种方式声明的事件不能被 COM 客户端(这里是一个 VB 6.0 程序) // 正确地处理, 实现了这个事件的组件不能被创建,可行的声明方法如下: [DispID(2)] void myEvent2(object sender, EventArgs e); // 也就是说声明一个方法,这个方法实际上就相应的是事件的处理函数 }

注意:由于事件接口是由订阅该事件的客户来实现,所以不应在声明组件类时将事件接口作为基类继承。

// 声明组件类,并在这里实现所有接口 [Guid(“9E5E5FB2-219D-4ee7-AB27-E4DBED8E123E”), ClassInterface(ClassInterfaceType.None), // 禁止生成类接口,而使用显式声明的接口 ComSourceInterfaces(typeof(IMyEventsInterface))] // 将事件接口与组件类联系起来 public class MyCoClass : IMyComInterface // 继承接口,以实现这个接口 { // 事件字段的声明应与事件接口中相应的事件声明保持一致,并加上 public public event MyEvent myEvent; private string m_strName;

// 这里实现所有在接口中声明的方法、属性以及激发事件 public void Hello(string name, int nParam) { MyEventArgs e = new MyEventArgs(name, nParam); FireMyEvent(e); } public bool Hello2(string name) { MyEventArgs e = new MyEventArgs(name, 0); FireMyEvent(e); }

// 通常会单独定义一个成员函数来激发事件 FireMyEvent(MyEventArgs e) { if (MyEvent != null) //首先检查一下当前是否有客户端订阅这个消息 { MyEvent(this , EventArgs.Empty); } }

public string Name { get { return m_strName; } set { // 当这个属性改变时,激发一个事件 MyEventArgs e = new MyEventArgs(value, 0); FireMyEvent(e); m_strName = value; } } }

注意:在创建 COM 对象前,首先应将这个对象注册到 COM Interop,并生成相应的 组件类型库(*.tlb): 打开项目的属性设置对话框–>配置属性–>生成–>为 COM Interop 注册 --> true 当然可以使用 RegAsm.exe 工具完成同样的注册过程。

此外,为了公开这个 COM 对象,组件库必须有一个强名称(strong name),强名称的 创建方法如下: 使用工具 SN.EXE 就可以很容易地生成一个强名称,例如 sn.exe -k Database_COM_Key.snk 然后打开项目中的 AssemblyInfo.cs 文件,并在里面指定密钥文件即可。

下面介绍一下如何在 VB 6.0 程序中使用采用上述方法上创建的 COM 对象了。

1> 首先,在工程中增加对相应的组件库(*.tlb文件) 和 Common Language Runtime Library (mscorlib.dll)的引用;

2> 然后,如果要响应组件对象所实现的事件,则需要在代码声明带事件组件对象变量, 例如:

在代码文件的开头加入以下声明语句: Option Explicit Dim WithEvents MyComObject as MyCoClass // 声明一个事件源对象

注意, VB 6.0 中不允许在 WithEvents 类型变量的同时使用 New 操作符来创建对象, 所以要在程序的其它地方,在使用这个对象之前创建它。有两种创建对象的方法: Set MyComObject = new MyComLib.MyCoClass ’ 常用于支持前期绑定的组件对象 或 Set MyComObject = CreateObject(“MyComLib.MyCoClass”)

3> 如果在工程中引用了组件库(*.tlb),借助前期绑定技术 VB 6.0 的 IDE 可以轻易为事 件源对象创建相应的事件处理函数。如果事件的参数是从 EventArgs 派生出来的自定义 类型,则由 IDE 创建的事件处理函数的参数始终是 mscorlib.EventArgs 类型,所以在 使用前需要做一些转换工作,例如:

Dim myEventArgs As MyComLib.MyEventArgs Set myEventArgs = e 'e 是事件响应函数的入口参数,类型为 mscorlib.EventArgs

注意:虽然经过上述转换以后,可以正确地访问自定义参数类型对象的成员,但是要想 这个自定义的参数类型支持前期绑定,则必须也将这个自定义类公布到 COM 。

事实上如果只是想组件类中的一些成员公开到 COM 并不需要如此繁琐。只需通过使用
interface 声明一个接口,然后在组件类中实现它即可。但是如果想将组件类中所支持
的事件公开到 COM 则必须要使用 Guid 来对事件接口件标识。
关注
打赏
1665731445
查看更多评论
立即登录/注册

微信扫码登录

0.0414s