如何在C#中调用COM组件

一、引言

  COM(Component Object Modele,组件对象模型)是微软以前推崇的一个开发技术,所以现在微软的很多产品都用到了COM组件,如Office,IE 等。然而如果.NET 平台下的程序想访问COM组件的方式来实现某个功能怎么办呢? 正是由于开发人员有这个需求,所以微软在.NET FrameWork中为COM和托管代码之间进行互操作提供了支持,这种互操作性的技术就是COM Interop。 但是COM Interop(COm互操作)这项技术,不仅支持在托管代码中使用COM对象,并且也支持在COM中使用托管对象,本专题只针对在.NET中调用COM对象来介绍,由于COM技术现在用的不多,所以如何在COM中使用托管对象将不会在本系列中做出介绍,如果有需要的朋友可以参看MSDN的相关链接: http://msdn.microsoft.com/zh-cn/library/3y76b69k(v=vs.100).aspx

下面就从一个具体的实例来看看在.NET 中是如何调用COM组件的。

二、如何在C#中调用COM组件——访问Office 互操作对象

  因为Office产品中使用了很多COM组件,下面就演示通过调用Office中的COM对象来创建Word文档并保存创建的文档到文件目录下的例子(在新建的控制台程序里添加”Microsoft.Office.Interop.Word 14.0.0.0 “ 这个引用,14.0.0.0版本是对应于Office 2010的一个互操作程序集, 12.0.0.0版本则是对应于Office 2007的互操作程序集,如果你电脑中只安装了Office 2007的话,就只能找到 12.0.0.0的版本的,如果安装了Office 2010的话,就可以同时找到这两个版本。)。具体代码如下:

using System;
// 添加额外的命名空间
using Microsoft.Office.Interop.Word;

namespace COM互操作性
{
  class Program
  {
    static void Main(string[] args)
    {
      // 调用COM对象来创建Word文档
      CreateWordDocument();
    }

    private static void CreateWordDocument()
    {
      // 启动Word并使Word可见
      Application wordApp = new Application() { Visible = true };

      // 新建Word文档
      wordApp.Documents.Add();
      Document wordDoc = wordApp.ActiveDocument;
      Paragraph para = wordDoc.Paragraphs.Add();
      para.Range.Text = "欢迎你,进入Learning Hard博客";

      // 保存文档
      object filename = @"D:\learninghard.doc";
      wordDoc.SaveAs2(filename);

      // 关闭Word
      wordDoc.Close();
      wordApp.Application.Quit();
    }
  }
}

运行结果为:

此时在所指定的文件目录中就可以看到你刚才创建的Word文档了。通过COM互操作的技术我们可以Office的自动化操作。

三、在C# 中调用COM组件的实现原理剖析

  通过上面的例子,大家可以看出在.NET 中调用COM组件是非常方便和简单的,以至于我们根本不能明白它背后的原理的,下面就介绍在托管代码中调用COM组件的实现原理和需要的步骤。

要运行上面的程序必须添加一个互操作程序集——“Microsoft.Office.Interop.Word 14.0.0.0”,大家可以通过下面的步骤来添加这个程序集:

  • 请在“解决方案资源管理器”中,右击“引用”文件夹,然后单击“添加引用”。
  • 在“.NET”选项卡上,选中最新版本的 Microsoft.Office.Interop.Word。  例如,“Microsoft.Office.Interop.Excel 14.0.0.0”。  单击“确定”。

  通过上面添加引用的步骤可以看出,Microsoft.Office.Interop.Word.dll 是一个.NET程序集,而不是COM组件,这时候朋友们肯定有这样的疑问——不是调用COM组件的吗?怎么在托管代码中调用.NET 程序集的?这样怎么能算是在.NET下调用COM组件的演示了? 然而事实是—— Microsoft.Office.Interop.Word.dll 确实是一个.NET程序集,并且它也叫做COM组件的互操作程序集,这个程序集中包含了COM组件中定义的类型的元数据, 托管代码通过调用互操作程序集中公开的接口或对象来间接地调用COM对象和接口的。由于托管代码中不能直接使用COM对象和接口,所以托管代码对COM对象的调用时是通过CLR的 COM Interop 层作为代理完成的,这个代理就是RCW(即Runtime Callable Wrapper,运行时可调用包装),所以对COM对象的调用,都是通过RCW来完成的,RCW做的工作主要有激活COM对象和在托管代码和非托管代码之间进行数据封送处理(从这里可以看出,RCW就是 .NET平台和COM组件之间的一个代理,微软的很多技术都使用了代理的,例如WCF技术——我们在代码中创建的对象其实只是服务的一个代理,通过代理对象来访问真真的对象的服务,即方法。讲到代理的技术,C#中的委托也是代理的一种实现,此时又想到了23中设计模式中的——代理模式,然而生活中也不乏代理的例子,租房中介,代理服务器等)。下面通过一个图来演示下 在.NET中调用COM组件的原理:

  关于通过Tlblmp.exe工具来生成互操作程序集步骤,这里我就不多详细诉说了,大家可以参考MSDN中这个工具详细使用说明 :http://msdn.microsoft.com/zh-cn/library/tt0cf3sx(v=VS.80).aspx

  然而我们也可以使用Visual Studio中内置的支持来完成为COM类型库创建互操作程序集的工作,我们只需要在VS中为.NET 项目添加对应的COM组件的引用,此时VS就会自动将COM类型库中的COM类型库转化为程序集中的元数据,并在项目的Bin目录下生成对于的互操作程序集,所以在VS中添加COM引用,其实最后程序中引用的是互操作程序集,然后通过RCW来对COM组件进行调用。 然而对于Office中的Microsoft.Office.Interop.Wordd.dll,这个程序集也是互操作程序集,但是它又是主互操作程序集,即PIA(Primary Interop Assemblies)。主互操作程序集是一个由供应商提供的唯一的程序集,为了生成主互操作程序集,可以在使用TlbImp命令是打开 /primary 选项。看到这里,朋友们肯定有这样的疑问:PIA与普通程序集到底有什么区别呢?——区别就是PIA除了包含了COM组件定义的数据类型外,还包含了一些特殊的信息,如公钥,COM类型库的提供者等信息。然而 为什么需要主互操作程序集的呢 ? 对于这个问题的答案就是——主互操作程序集可以帮助我们解决部署程序时,引用互操作程序集版本不一致的问题。(如果开发人员会为一个COM组件类型库生成多个互操作程序集,项目中引用的互操作程序集版本与部署时的互操作程序集版本不一致的问题,有了互操作程序集时,我们可以直接引用官方提供主互操作程序集。)

四、错误处理

知道了如何调用COM组件之后,大家或许会问:如果调用COM对象的方法失败时怎么去获取失败的信息呢?对于这个疑问,错误的处理的方法和我们平常托管代码中的处理方式是一样的,下面就具体看看是如何获取错误信息的,下面这段代码的功能是——打开一个现有的Word文档并插入相应的文本,当指定的Word文档不存在时,此时就会出现调用COM对象的Open方法失败的情况,具体代码如下:

using System;
using Microsoft.Office.Interop.Word;
using System.IO;
using System.Runtime.InteropServices;

namespace COM互操作中的错误处理
{
  class Program
  {
    static void Main(string[] args)
    {
      // 打开存在的文档插入文本
      string wordPath = @"D:\test.docx";
      OpenWordDocument(wordPath);
      Console.Read();
    }

    // 向现有文档插入文本
    private static void OpenWordDocument(string wordPath)
    {
      // 启动Word 应用程序
      Application wordApp = new Application() { Visible = true };
      Document wordDoc=null;
      try
      {
        // 如果文档不存在时,就会出现调用COM对象失败的情况
        // 打开Word文档
        wordDoc = wordApp.Documents.Open(wordPath);
        // 向Word中插入文本
        Range wordRange = wordDoc.Range(0, 0);
        wordRange.Text = "这是插入的文本";

        // 保存文档
        wordDoc.Save();
      }
      catch(Exception ex)
      {
        // 获得异常相对应的HRESULT值
        // 因为COM中根据方法返回的HRESULT来判断调用是否成功的
        int HResult = Marshal.GetHRForException(ex);
        // 设置控制台的前景色,即输出文本的颜色
        Console.ForegroundColor = ConsoleColor.Red;
        // 下面把HRESULT值以16进制输出
        Console.WriteLine("调用抛出异常,异常类型为:{0}, HRESULT= 0x{1:x}", ex.GetType().Name, HResult);
        Console.WriteLine("异常信息为:" + ex.Message.Replace('\r', ' '));
      }
      finally
      {
        // 关闭文档并
        if (wordDoc != null)
        {
          wordDoc.Close();
        }
        // 退出Word程序
        wordApp.Quit();
      }
    }
  }
}

如果我们D盘中不存在一个 test.docx文档时,此时代码就会进入catch块,输出异常信息,运行结果为:

  从上面的结果我们看到了一个 HRESULT值,这个值真是COM代码中返回返回的。在COM中,COM方法通过返回 HRESULT 来报告错误;.NET 方法则通过引发异常来报告错误,为了方便地在托管代码中获得COM代码中出现的错误和异常信息,CLR提供了两者之间的转换,每一个代表错误发生的HRESULT都会被映射到.NET Framework中的一个异常类,对于具体的映射关系可以参考MSDN中 的文章: http://msdn.microsoft.com/zh-cn/library/9ztbc5s1(VS.80).aspx ,我这里就不具体用表格列出来的。 如果某个HRESULR不能被映射到等效的.NET Framework 异常类时,那么就会被映射到COMException异常类, 我们可以通过 Marshal类的GetHRForException方法来获得异常类对应的HRESULT值(该方法的使用在上面代码中已经贴出)

五、小结

  关于在.NET中调用COM组件的介绍就到这里的,即使我们在.NET中调用COM对象的方法是非常的简单和方便,但是理解CLR 为我们背后完成的工作到底有哪些和理解托管代码中调用COM组件原理也是相当有必要的。因为理解了调用的原理之后,当我们出现问题的时候就可以很快找到解决方案并解决它,不会觉得无从下手,这样就可以帮助我们提供解决问题的能力。

以上就是如何在C#中调用COM组件的详细内容,更多关于C# 调用COM组件的资料请关注我们其它相关文章!

(0)

相关推荐

  • c#对XML文档的创建与增删改查的示例代码

    一.创建的第一种方式 //1.创建一个XML文档 XmlDocument doc = new XmlDocument(); //2.创建第一行描述信息 XmlDeclaration dec = doc.CreateXmlDeclaration("1.0", "utf-8", null); //3.将创建的第一行描述信息添加到文档中 doc.AppendChild(dec); //4.给文档添加根节点 XmlElement Books = doc.CreateElem

  • 详解c# 委托链

    引言: 上一专题介绍了下编译器是如何来翻译委托的,从中间语言的角度去看委托,希望可以帮助大家进一步的理解委托,然而之前的介绍都是委托只是封装一个方法,那委托能不能封装多个方法呢?因为生活中经常会听到,我代表大家的意见等这样的说话,既然委托也是一个代表,那他如果只能代表一个人,那他的魅力就不是很大了吧,所以我们就会委托能不能代表多个方法的? 答案是可以的,这就是本专题要讲的内容--委托链,委托链也是一个委托,只是因为它是把多个委托链在一起,所以我们就以委托链来这么称呼它的. 一.到底什么是委托链

  • Python调用C# Com dll组件实战教程

    之前公司有套C# AES加解密方案,但是方案加密用的是Rijndael类,而非AES的四种模式(ECB.CBC.CFB.OFB,这四种用的是RijndaelManaged类),Python下Crypto库AES也只有这四种模式,进而Python下无法实现C# AES Rijndael类加密效果了. 类似于这种C# 能实现的功能而在Python下实现不了的,搜集资料有两种解决方案,第一种方式,使用IronPython 直接调用C# dll文件,教程网上很多,不在赘述了,这种方式有个缺点,用的是ir

  • c#创建vc可调用的com组件方法分享

    开发工具:VS2008 VS2008命令提示符(呵呵,这个你应该可以找到在什么地方吧) 附:本文适用任何VS系列工具. 在用C#创建COM时,一定要记住以下几点: 1:所要导出的类必须为公有: 2:所有属性.方法也必须为公有: 3:要导出的属性.方法必须用接口方式:如果没有在接口中声明,即使该方法(属性)为公有,也不能正常导出到COM.但他们可以被别的.NET程序所使用: 4:所有的事件也必须用接口方式: 现在我们开始正题: 一.新建一个Visual C#工程(习惯这种叫法了,现在应该叫解决方案

  • 简单聊聊c# 事件

    引言: 前面几个专题对委托进行了详细的介绍的,然后我们在编写代码过程中经常会听到"事件"这个概念的,尤其是写UI的时候,当我们点击一个按钮后VS就会自动帮我们生成一些后台的代码,然后我们就只需要在Click方法里面写代码就可以,所以可能有些刚接触C#的朋友就觉得这样很理所当然的,也没有去思考这是为什么的,为什么点击下事件就会触发我们在Click方法里面写的代码呢?事件到底扮演个什么样的角色呢?为了解除大家的这些疑惑,下面就详细介绍了事件,让一些初学者深入理解C#中的事件的概念. 一.为

  • c# 委托的本质是什么

    引言 上一个专题已经和大家分享了我理解的--C#中为什么需要委托,专题中简单介绍了下委托是什么以及委托简单的应用的,在这个专题中将对委托做进一步的介绍的,本专题主要对委本质和委托链进行讨论. 一.委托的本质 平时我们很容易使用委托--用C# delegate关键字定义委托,再用new操作符构造委托实例,然后通过调用委托实例来调用回调方法(就是用一个了委托对象的变量来代替方法名,这句话如果刚接触的人不好理解的话,这里给个例子:MyDelegate mydelegate =new Mydelegat

  • C#中互操作性简介

    一.引言 这个系列是在C#基础知识中遗留下来的一个系列的,因为在C# 4.0中的一个新特性就是对COM互操作改进,然而COM互操作性却是.NET平台下其中一种互操作技术,为了帮助大家更好的了解.NET平台下的互操作技术,所以才有了这个系列.然而有些朋友们可能会有这样的疑问--"为什么我们需要掌握互操作技术的呢?" 对于这个问题的解释就是--掌握了.NET平台下的互操作性技术可以帮助我们在.NET中调用非托管的dll和COM组件..NET是建立在操作系统的之上的一个开发框架,其中.NET

  • 详解c# 数组(Array)

    数组是一个存储相同类型元素的固定大小的顺序集合.数组是用来存储数据的集合,通常认为数组是一个同一类型变量的集合. 声明数组变量并不是声明 number0.number1.....number99 一个个单独的变量,而是声明一个就像 numbers 这样的变量,然后使用 numbers[0].numbers[1].....numbers[99] 来表示一个个单独的变量.数组中某个指定的元素是通过索引来访问的. 所有的数组都是由连续的内存位置组成的.最低的地址对应第一个元素,最高的地址对应最后一个元

  • C# 引入委托的目的是什么

    引言: 对于一些刚接触C# 不久的朋友可能会对C#中一些基本特性理解的不是很深,然而这些知识也是面试时面试官经常会问到的问题,所以我觉得有必要和一些接触C#不久的朋友分享下关于C#基础知识的文章,所以有了这个系列,希望通过这个系列让朋友对C#的基础知识理解能够更进一步.然而委托又是C#基础知识中比较重要的一点,基本上后面的特性都和委托有点关系,所以这里就和大家先说说委托,为什么我们需要委托. 一.C#委托是什么的? 在正式介绍委托之前,我想下看看生活中委托的例子--生活中,如果如果我们需要打官司

  • 深入分析c# 继承

    继承是面向对象程序设计中最重要的概念之一.继承允许我们根据一个类来定义另一个类,这使得创建和维护应用程序变得更容易.同时也有利于重用代码和节省开发时间. 当创建一个类时,程序员不需要完全重新编写新的数据成员和成员函数,只需要设计一个新的类,继承了已有的类的成员即可.这个已有的类被称为的基类,这个新的类被称为派生类. 继承的思想实现了 属于(IS-A) 关系.例如,哺乳动物 属于(IS-A) 动物,狗 属于(IS-A) 哺乳动物,因此狗 属于(IS-A) 动物. 基类和派生类 一个类可以派生自多个

随机推荐