新书推介:《语义网技术体系》
作者:瞿裕忠,胡伟,程龚
   XML论坛     W3CHINA.ORG讨论区     计算机科学论坛     SOAChina论坛     Blog     开放翻译计划     新浪微博  
 
  • 首页
  • 登录
  • 注册
  • 软件下载
  • 资料下载
  • 核心成员
  • 帮助
  •   Add to Google

    >> 本版讨论高级C/C++编程、代码重构(Refactoring)、极限编程(XP)、泛型编程等话题
    [返回] 计算机科学论坛计算机技术与应用『 C/C++编程思想 』 → COM 组件设计与应用(五、六)——用 ATL 写第一个组件 查看新帖用户列表

      发表一个新主题  发表一个新投票  回复主题  (订阅本版) 您是本帖的第 7409 个阅读者浏览上一篇主题  刷新本主题   树形显示贴子 浏览下一篇主题
     * 贴子主题: COM 组件设计与应用(五、六)——用 ATL 写第一个组件 举报  打印  推荐  IE收藏夹 
       本主题类别:     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客楼主
    发贴心情 COM 组件设计与应用(五、六)——用 ATL 写第一个组件

    一、前言

      1、如果你在使用 vc5.0 及以前的版本,请你升级为 vc6.0 或 vc.net 2003;

      2、如果你在使用 vc6.0 (ATL 3.0)请阅读本回内容;

      3、如果你在使用 vc.net(ATL 7.0)请阅读下回内容;(当然读读本文内容也不错)

      4、这第一个组件,除了所有 COM 组件必须的 IUnknown 接口外,我们再实现一个自己定义的接口 IFun,它有两个函数: Add()完成两个数值的加法,Cat()完成两个字符串的连接。

      5、下面......好好听讲! 开始了:-)

      二、建立 ATL 工程

      步骤2.1:建立一个工作区(WorkSpace)。

      步骤2.2:在工作区中,建立一个 ATL 工程(Project)。示例程序叫 Simple1,并选择DLL方式,见图一。

    按此在新窗口浏览图片
      图一、建立 ATL DLL 工程

      Dynamic Link Library(DLL) 表示建立一个 DLL 的组件程序。

      Executable(EXE) 表示建立一个 EXE 的组件程序。

      Service(EXE) 表示建立一个服务程序,系统启动后就会加载并执行的程序。

      Allow merging of proxy/stub code 选择该项表示把“代理/存根”代码合并到组件程序中,否则需要单独编译,单独注册代理存根程序。代理/存根,这个是什么概念?还记得我们在上回书中介绍的吗?当调用者调用进程外或远程组件功能的时候,其实是代理/存根负责数据交换的。关于代理/存根的具体变成和操作,以后再说啦......

      Support MFC 除非有特殊的原因,我们写 ATL 程序,最好不要选择该项。你可能会说,如果没有MFC的支持,那CString怎么办呀?告诉你个秘密吧,一般人我都不告诉他,我后半辈子就靠着这个秘密活着了:

      1、你会STL吗?可以用 STL 中的 string 代替;

      2、自己写个 MyString 类,嘿嘿;

      3、悄悄地、秘密地、不要告诉别人(特别是别告诉微软),把 MFC 中的 CString 源码拿过来用;

      4、使用 CComBSTR 类,至少也能简化我们字符串操作;

      5、直接用 API 操作字符串,反正我们大家学习 C 语言的时候,都是从这里干起的。(等于没说,呵呵)

      Support MTS 支持事务处理,也就是是否支持 COM+ 功能。COM+ 也许在第 99 回介绍吧。

      三、增加 ATL 对象类

      步骤3.1:菜单 Insert\New ATL Object...(或者用鼠标右键在 ClassView 卡片中弹出菜单)并选择Object 分类,选中 Simple Object 项目。见图二。

    按此在新窗口浏览图片
      图二、选择建立简单COM对象

      Category Object 普通组件。其中可以选择的组件对象类型很多,但本质上,就是让向导帮我们默认加上一些接口。比如我们选 "Simple Object",则向导给我们的组件加上 IUnknown 接口;我们选 "Internet Explorer Object",则向导除了加上 IUnknown 接口外,再增加一个给 IE 所使用的 IObjectWithSite 接口。当然了,我们完全可以手工增加任何接口。

      Category Controls ActiveX 控件。其中可以选择的 ActiveX 类型也很多。我们在后续的专门介绍 ActiveX 编程中再讨论。

      Category Miscellaneous 辅助杂类组件。

      Categroy Data Access 数据库类组件(我最讨厌数据库编程了,所以我也不会)。

      步骤3.2:增加自定义类 CFun(接口 IFun) ,见图三。

    按此在新窗口浏览图片
      图三、输入类中的各项名称

      其实,我们只需要输入短名(Short Name),其它的项目会自动填写。没什么多说的,只请大家注意一下 ProgID 项,默认的 ProgID 构造方式为“工程名.短名”。

      步骤3.3:填写接口属性,见图四。

    按此在新窗口浏览图片
      图四、接口属性

      Threading Model 选择组件支持的线程模型。COM 中的线程,我认为是最讨厌,最复杂的部分。COM 线程和公寓的概念,留待后续介绍。现在吗......大家都选 Apartment,它代表什么那?简单地说:当在线程中调用组件函数的时候,这些调用会排队进行。因此,这种模式下,我们可以暂时不用考虑同步的问题。(注1)

      Interface 接口基本类型。Dual 表示支持双接口(注2),这个非常 非常重要,非常非常常用,但我们今天不讲。Custom 表示自定义借口。切记!切记!我们的这第一个 COM 程序中,一定要选择它!!!!(如果你选错了,请删除全部内容,重新来过。)

      Aggregation 我们写的组件,将来是否允许被别人聚合(注3)使用。Only 表示必须被聚合才能使用,有点类似 C++ 中的纯虚类,你要是总工程师,只负责设计但不亲自写代码的话,才选择它。

      Support ISupportErrorInfo 是否支持丰富信息的错误处理接口。以后就讲。

      Support Connection Points 是否支持连接点接口(事件、回调)。以后就讲。

      Free Threaded Marshaler 以后也不讲,就算打死你,我也不说!(注4)

      四、添加接口函数

    按此在新窗口浏览图片
      图五、调出增加接口方法的菜单

    按此在新窗口浏览图片
      图六、增加接口函数 Add

    按此在新窗口浏览图片
      图七、增加接口函数 Cat

      请严格按照图六的方式,增加Add()函数;由于图七中增加Cat()函数的参数比较长,我没有适当的输入空格,请大家自己输入的时候注意一下。[in]表示参数方向是输入;[out]表示参数方向是输出;[out,retval]表示参数方向是输出,同时可以作为函数运算结果的返回值。一个函数中,可以有多个[in]、[out],但[retval]只能有一个,并且要和[out]组合后在最后一个位置。(注5)

    按此在新窗口浏览图片
      图八、接口函数定义完成后的图示

      我们都知道,要想改变 C++ 中的类函数,需要修改两个地方:一是头文件(.h)中类的函数声明,二是函数体(.cpp)文件的实现处。而我们现在用 ATL 写组件程序,则还要修改一个地方,就是接口定义(IDL)文件。别着急 IDL 下次就要讨论啦。

      由于 vc6.0 的BUG,导致大家在增加完接口和接口函数后,可能不会向上图(图八)所表现的样式。解决方法:

       1 关闭工程,然后重新打开 该方法常常有效
    2 关闭 IDE,然后重新运行  
    3 打开 IDL 文件,检查接口函数是否正确,如不正确请修改  
    4 打开 IDL 文件,随便修改一下(加一个空格,再删除这个空格),然后保存 该方法常常有效
    5 打开 h/cpp 文件,检查函数是否存在或是否正确,有则改之 无则嘉勉,不说完这个成语心理别扭
    6 删除 IDL/H/CPP 中的接口函数,然后 再来一遍
    7 重新建立工程、重新安装vc、重新安装windows、砸计算机 砸!


      五、实现接口函数

      鼠标双点图八中CFun\IFun\Add(...)就可以开始输入函数实现了:

    STDMETHODIMP CFun::Add(long n1, long n2, long *pVal)
    {
      *pVal = n1 + n2;
      return S_OK;
    }
      这个太简单了,不再浪费“口条”。下面我们实现字符串连接的Cat()函数:

    STDMETHODIMP CFun::Cat(BSTR s1, BSTR s2, BSTR *pVal)
    {
      int nLen1 = ::SysStringLen( s1 );  // s1 的字符长度
      int nLen2 = ::SysStringLen( s2 );  // s2 的字符长度
      *pVal = ::SysAllocStringLen( s1, nLen1 + nLen2 );  // 构造新的 BSTR 同时把 s1 先保存进去
      if( nLen2 )
      {
        ::memcpy( *pVal + nLen1, s2, nLen2 * sizeof(WCHAR) );  // 然后把 s2 再连接进去
    //    wcscat( *pVal, s2 );
      }
      return S_OK;
    }
      学生:上面的函数实现,完全是调用基本的 API 方式完成的。

      老师:是的,说实话,的确比较烦琐。

      学生:我们是用memcpy()完成连接第二个字符串功能的,那么为什么不用函数 wcscat()那?

      老师:多数情况下可以,但你需要知道:由于BSTR包含有字符串长度,因此实际的BSTR字符串内容中是可以存储L''\0''的,而函数 wcscat() 是以L''\0''作为复制结束标志,因此可能会丢失数据。明白了吗?

      学生:明白,明白。我看过《COM 组件设计与应用(三)之数据类型》后就明白了。那么老师,有没有简单一些的方法那?

      老师:有呀,你看......

    STDMETHODIMP CFun::Cat(BSTR s1, BSTR s2, BSTR *pVal)
    {
      CComBSTR sResult( s1 );
      sResult.AppendBSTR( s2 );
      *pVal = sResult.Copy();
    //  *pVal = sResult.Detach();
      return S_OK;
    }
      学生:哈哈,好!使用了 CComBSTR,这个就简单多了。CComBSTR::Copy()和CComBSTR::Detach()有什么区别?
    老师:CComBSTR::Copy() 会制造一个 BSTR 的副本,另外CComBSTR::CopyTo()也有类似功能。而CComBSTR::Detach()是使对象与内部的 BSTR 指针剥离,这个函数由于没有复制过程,因此速度稍微快一点点。但要注意,一但剥离后,就不能再使用该对象啦。
    学生:老师,您讲的太牛啦,我对您的敬仰如巍巍泰山,直入云霄......
    老师:STOP,STOP!留作业啦......
       1、自己先按照今天讲的内容写出这个组件;
       2、不管你懂不懂,一定要去观察 IDL 文件,CPP 文件;
       3、编译后,看都产生了些什么文件?如果是文本的文件,就打开看看;
       4、下载本文的示例程序(vc6.0版本)编译运行,看看效果。然后预习一下示例程序中的调用方法;
    学生:知道啦,快下课吧,我要上厕所,我都憋的不行了......
    老师:下课!别忘了顶我的帖子呀......


    六、小结

      本回介绍第一个ATL组件程序的建立步骤,而如何使用该组件,敬请关注《COM 组件设计与应用(七)》。
     
    --------------------------------------------------------------------------------
    注1:Apartment,系统通过隐藏的窗口消息来排队组件调用,因此我们可以暂时不考虑同步问题。注意,是暂时哈。
    注2:双接口表示在一个接口中,同时支持自定义接口和 IDispatch 接口。以后,以后,以后就讲。因为双接口非常重要,我们以后会天天讲、夜夜讲、常常讲------简称“三讲”:)
    注3:组件的重用方法有2个,聚合和包容。
    注4:名称的功能很好听,但微软根本就没有实现。
    注5:这些都是 IDL 文件中的概念,以后用到什么,就介绍什么。

    [此贴子已经被作者于2007-10-22 9:38:38编辑过]

       收藏   分享  
    顶(0)
      




    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/20 11:40:00
     
     卷积内核 帅哥哟,离线,有人找我吗?
      
      
      威望:8
      头衔:总统
      等级:博士二年级(版主)
      文章:3942
      积分:27590
      门派:XML.ORG.CN
      注册:2004/7/21

    姓名:(无权查看)
    城市:(无权查看)
    院校:(无权查看)
    给卷积内核发送一个短消息 把卷积内核加入好友 查看卷积内核的个人资料 搜索卷积内核在『 C/C++编程思想 』的所有贴子 访问卷积内核的主页 引用回复这个贴子 回复这个贴子 查看卷积内核的博客2
    发贴心情 
    COM 组件设计与应用(六)——用 ATL 写第一个组件(vc.net)

    一、前言
      1、与 《COM 组件设计与应用(五)》的内容基本一致。但本回讲解的是在 vc.net 2003 下的使用方法,即使你不再使用vc6.0,也请和上一回的内容,参照比对。

      2、这第一个组件,除了所有 COM 组件必须的 IUnknown 接口外,我们再实现一个自己定义的接口 IFun,它有两个函数: Add()完成两个数值的加法,Cat()完成两个字符串的连接。

      3、下面......好好听讲! 开始了:-)

      

      二、建立 ATL 工程

      步骤2.1:建立一个解决方案。

      步骤2.2:在 该解决方案中,新建一个 vc++ 的 ATL 项目。示例程序叫 Simple2,并选择DLL方式,见图一、图二。

    按此在新窗口浏览图片
      图一、新建 ATL 项目

    按此在新窗口浏览图片
      图二、选择非属性化的DLL组件类型

      属性化 属性化编程,是未来的方向,但我们现在先不要选它。

      动态链接库(DLL) 选择它。

      可执行文件(EXE) 以后再讲。

      服务(EXE) 表示建立一个系统服务组件程序,系统启动后就会加载并执行的程序。

      允许合并代理/存根(stub)代码 选择该项表示把“代理/存根”代码合并到组件程序中,否则需要单独编译,单独注册代理存根程序。代理/存根,这个是什么概念?还记得我们在上回书中介绍的吗?当调用者调用进程外或远程组件功能的时候,其实是代理/存根负责数据交换的。关于代理/存根的具体变成和操作,以后再说啦......

      支持 MFC 除非有特殊的原因,我们写 ATL 程序,最好不要选择该项。你可能会说,如果没有MFC的支持,那CString怎么办呀?告诉你个秘密吧,一般人我都不告诉他,我后半辈子就靠着这个秘密活着了:

      1、你会STL吗?可以用 STL 中的 string 代替;

      2、自己写个 MyString 类,嘿嘿;

      3、悄悄地、秘密地、不要告诉别人(特别是别告诉微软),把 MFC 中的 CString 源码拿过来用;

      4、使用 CComBSTR 类,至少也能简化我们字符串操作;

      5、直接用 API 操作字符串,反正我们大家学习 C 语言的时候,都是从这里干起的。(等于没说,呵呵)

      支持 COM+ 1.0 支持事务处理的 COM+ 功能。COM+ 也许在第 99 回介绍吧。

      三、添加 ATL 对象类

      步骤3.1:菜单"项目\添加类..."(或者用鼠标右键在 项目中弹出菜单"添加\添加类...")并选择 ATL 简单对象。见图三。

    按此在新窗口浏览图片
      图三、选择建立ATL简单对象

      除了简单对象(只实现了 IUnknown 接口),还可以选择“ATL控件”(ActiveX,实现了10多个接口)......可以选择的组件对象类型很多,但本质上,就是让向导帮我们默认加上一些接口。在以后的文章中,陆续介绍吧。

      步骤3.2:增加自定义类 CFun(接口 IFun) ,见图四。

      按此在新窗口浏览图片

      图四、填写名称

      其实,我们只需要输入简称,其它的项目会自动填写。没什么多说的,只请大家注意一下 ProgID 项,默认的 ProgID 构造方式为“项目名.简称名”。

      步骤3.3:填写接口属性选项,见图 五。

    按此在新窗口浏览图片
      图五、接口选项

      线程模型 COM 中的线程,我认为是最讨厌,最复杂的部分。COM 线程和公寓的概念,留待后续介绍。现在吗......大家都选"单元"(Apartment),它代表什么那?简单地说:当在线程中调用组件函数的时候,这些调用会排队进行。因此,这种模式下,我们可以暂时不用考虑同步的问题。(注1)

      接口。双重(Dual),这个非常 非常重要,非常非常常用,但我们今天不讲(注2)。切记!切记!我们的这第一个 COM 程序中,一定要选择“自定义”!!!!(如果你选错了,请删除全部内容,重新来过。)

      聚合 我们写的组件,将来是否允许被别人聚合(注3)使用。“只能创建为聚合”,有点类似 C++ 中的纯虚类,你要是总工程师,只负责设计但不亲自写代码的话,才选择它。

      ISupportErrorInfo 是否支持丰富信息的错误处理接口。以后就讲。

      连接点 是否支持连接点接口(事件、回调)。以后就讲。

      IObjectWithSite 是否支持IE的调用

      四、添加接口函数

    按此在新窗口浏览图片
      图六、调出增加接口方法的菜单

    按此在新窗口浏览图片
      图七、增加接口函数 Add

      请按照图示的方法,增加Add()函数,增加Cat()函数 。[in]表示参数方向是输入;[out]表示参数方向是输出;[out,retval]表示参数方向是输出,同时可以作为函数运算结果的返回值。一个函数中,可以有多个[in]、[out],但[retval]只能有一个,并且要和[out]组合后在最后一个位置。(注4)

    按此在新窗口浏览图片
      图八、接口函数定义完成后的图示

      我们都知道,要想改变 C++ 中的类函数,需要修改两个地方:一是头文件(.h)中类的函数声明,二是函数体(.cpp)文件的实现处。而我们现在用 ATL 写组件程序,则还要修改一个地方,就是接口定义(IDL)文件。别着急 IDL 下次就要讨论啦。

      五、实现接口函数

      鼠标双点图八中CFun\基项和接口\Add(...)就可以开始输入函数实现了:

    STDMETHODIMP CFun::Add(long n1, long n2, long *pVal)
    {
      *pVal = n1 + n2;
      return S_OK;
    }这个太简单了,不再浪费“口条”。下面我们实现字符串连接的Cat()函数:STDMETHODIMP CFun::Cat(BSTR s1, BSTR s2, BSTR *pVal)
    {
      int nLen1 = ::SysStringLen( s1 );  // s1 的字符长度
      int nLen2 = ::SysStringLen( s2 );  // s2 的字符长度
      *pVal = ::SysAllocStringLen( s1, nLen1 + nLen2 );// 构造新的 BSTR 同时把 s1 先保存进去
      if( nLen2 )
      {
        ::memcpy( *pVal + nLen1, s2, nLen2 * sizeof(WCHAR) );  // 然后把 s2 再连接进去
    //    wcscat( *pVal, s2 );
      }
      return S_OK;
    }学生:上面的函数实现,完全是调用基本的 API 方式完成的。

      老师:是的,说实话,的确比较烦琐。

      学生:我们是用memcpy()完成连接第二个字符串功能的,那么为什么不用函数 wcscat()那?

      老师:多数情况下可以,但你需要知道:由于BSTR包含有字符串长度,因此实际的BSTR字符串内容中是可以存储L''\0''的,而函数 wcscat() 是以L''\0''作为复制结束标志,因此可能会丢失数据。明白了吗?

      学生:明白,明白。我看过《COM 组件设计与应用(三)之数据类型》后就明白了。那么老师,有没有简单一些的方法那?

      老师:有呀,你看......STDMETHODIMP CFun::Cat(BSTR s1, BSTR s2, BSTR *pVal)
    {
      CComBSTR sResult( s1 );
      sResult.AppendBSTR( s2 );
      *pVal = sResult.Copy();
    //  *pVal = sResult.Detach();
      return S_OK;
    }学生:哈哈,好!使用了 CComBSTR,这个就简单多了。CComBSTR::Copy()和CComBSTR::Detach()有什么区别?

      老师:CComBSTR::Copy() 会制造一个 BSTR 的副本,另外CComBSTR::CopyTo()也有类似功能。而CComBSTR::Detach()是使对象与内部的 BSTR 指针剥离,这个函数由于没有复制过程,因此速度稍微快一点点。但要注意,一但剥离后,就不能再使用该对象啦。

      学生:老师,您讲的太牛啦,我对您的敬仰如巍巍泰山,直入云霄......

      老师:STOP,STOP!留作业啦......

      1、自己先按照今天讲的内容写出这个组件;

      2、不管你懂不懂,一定要去观察 IDL 文件,CPP 文件;

      3、编译后,看都产生了些什么文件?如果是文本的文件,就打开看看;

      4、下载本文的示例程序(vc.net 2003版本)编译运行,看看效果。然后预习一下示例程序中的调用方法;

      学生:知道啦,快下课吧,我要上厕所,我都憋的不行了......

      老师:下课!别忘了顶我的帖子呀......

      六、小结

      本回介绍第一个ATL组件程序的建立步骤,而如何使用该组件,敬请关注《COM 组件设计与应用(七)》。
    注1:Apartment,系统通过隐藏的窗口消息来排队组件调用,因此我们可以暂时不考虑同步问题。注意,是暂时哈。
    注2:双接口表示在一个接口中,同时支持自定义接口和 IDispatch 接口。以后,以后,以后就讲。因为双接口非常重要,我们以后会天天讲、夜夜讲、常常讲------简称“三讲”:)
    注3:组件的重用方法有2个,聚合和包容。
    注4:这些都是 IDL 文件中的概念,以后用到什么,就介绍什么。

    [此贴子已经被作者于2007-10-22 9:39:40编辑过]

    ----------------------------------------------
    事业是国家的,荣誉是单位的,成绩是领导的,工资是老婆的,财产是孩子的,错误是自己的。

    点击查看用户来源及管理<br>发贴IP:*.*.*.* 2007/10/20 11:42:00
     
     GoogleAdSense
      
      
      等级:大一新生
      文章:1
      积分:50
      门派:无门无派
      院校:未填写
      注册:2007-01-01
    给Google AdSense发送一个短消息 把Google AdSense加入好友 查看Google AdSense的个人资料 搜索Google AdSense在『 C/C++编程思想 』的所有贴子 访问Google AdSense的主页 引用回复这个贴子 回复这个贴子 查看Google AdSense的博客广告
    2024/11/24 20:36:47

    本主题贴数2,分页: [1]

    管理选项修改tag | 锁定 | 解锁 | 提升 | 删除 | 移动 | 固顶 | 总固顶 | 奖励 | 惩罚 | 发布公告
    W3C Contributing Supporter! W 3 C h i n a ( since 2003 ) 旗 下 站 点
    苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》
    70.313ms