您的位置:软件 > 开发者网络 > 开发工具 > 开发专栏 > VC > 正文
实战COM编程系列之三
[文章信息]
作者:lop5712
时间:2004-05-08
出处:论坛
责任编辑:方舟
[文章导读]
由于客户和和组件实现的接口IModule、IModuleSite等都包有界面的传递,出于利用MFC的界面包装功能而都使用MFC来实现
advertisement
热点推荐
· 天极网软件频道改版调查
· 10.26精选 图画:葡萄小鸡图
· Windows操作系统网络管理技巧
· Fireworks国画:葡萄小鸡图
· 黑客张大民江南小城奇遇记
[正文]

1 2 3  下一页

天极IT资讯短信服务 电脑小技巧
资费:包月5元
手机:
介绍:细处着手,巧处用功。高手和菜鸟之间的差别就是:高手什么都知道,菜鸟知道一些。电脑小技巧收集最新奇招高招,让你轻松踏上高手之路。

  本文为此系列文章的重点,前面设计的接口都只是辅助性质,与COM线程模型没有一点关系。由于客户和和组件实现的接口IModule、IModuleSite等都包有界面的传递,出于利用MFC的界面包装功能而都使用MFC来实现,故全部运行在STA套间中,并可使得组件的窗口亦使用客户端主线程来派送消息。

  假设调用远程组件的方法以实现业务逻辑,由于远程及大数据量操作的关系,决定对于每次界面发起的数据操作(如查找),均发起一个线程,然后在这个线程中调用远程方法。决定使用一个类包装任务,其实现ITask接口,以提供任务管理的服务,而线程函数Task就理所当然的是ITask的实现类CTask的静态成员函数。由于CTask是一个内部对象,不需要拥有CLSID,故样例中直接通过静态成员函数CreateInstance获得其实例。

  出于示范的目的,考虑如何表现进度,在此提出两种方式。

  一. 假设远程组件方法是同步的,则调用线程将由于远程组件方法的迟迟不返而被挂起,因此无法表示进度。对于此,可以开启一个计时器(Timer),每隔固定时间就由前面的CTask通知ITaskManager增加了一定的进度,并给个上限,超出后就不再计时,进而进度不再增加直到方法返回或ITaskManager终止;如果是异步的,则上面说的发起一个线程这个工作由COM运行时期库(以后简称COM)干了,在此也就没有意义了,并且其必须在Win2000及以后版本发行的COM上才有效,这也就是说客户端必须是在Win2000及后续版本的操作系统上运行,这不是一个好提议。

  二. 假定ITask由远程业务组件实现,而并不是上面说的一个内部对象仅为提供包装使用,则在远程业务组件的方法中每过一段代码,其调用ITaskNotify以设置进度,并在方法结束时调用ITaskNotify以结束任务。

  上面的第一种情况中的CTask没有存在于MTA套间中的必要——其只会被客户主线程调用(不管是ITaskManager的实现者还是部门组件的操作界面发起的任务,都是通过客户端主线程操作的)。因此其应该是一个Apartment组件,使用MFC实现。但由于本样例主要是演示套间间的访问调用,故在此依旧将其设计成Free组件(即使没这个必要),使用ATL实现,以演示如何跨套间调用。但由于其是通过静态成员函数CreateInstance直接创建的,并没有CLSID及相关注册表项以说明是Free组件。由于在客户主线程创建它,即客户主线程获得其直接指针,因此它是存在于客户主线程相关的STA套间内(即使以Free组件的要求编写)。

  而对于发起线程(既CTask::Task静态成员函数)的终止,如果将发起线程和MTA线程关联(即线程开始时COINIT_MULTITHREADED作为参数调用CoInitializeEx),而业务组件是一个Free组件,将存在于MTA中,则非常不幸地发起线程是直接调用业务组件的方法,即发起线程也是业务组件方法的执行者(假设业务组件是本地组件)。不幸地原因就是如果业务组件的方法中有一个死循环或运行时间很长的代码,且其又不提供任何终止的方法(比如短时间等待一个事件),则终止调用的唯一方法就是强行终止线程,进而不能正确调用业务组件的Release,并导致COM的一些效率低下(CoUninitialize没有调用,COM的某些资源未能及时释放)。如果通过代理调用(而不是直接指针),则可以通过ICancelMethodCalls取消掉未决的调用——即发起线程中的对业务组件的调用。这是COM提供的一个接口,以使得客户可以取消同步调用。这是双方面的工作,如果组件端一直占着线程资源不放也依旧和前面一样,必须强行终止,但这样至少提供了一种途径使得可以回复发起线程的运行,进而调用CoUninitialize退出。其坏处和前面的异步调用一样,必须是Win2000及其以上版本的操作系统所发布的COM。本样例使用后者,故发起线程使用CoInitialize进入STA而不是MTA以获得代理对象的指针而非直接指针进而得以取消调用(有兴趣可以自己试下,将CTask::Task中的CoInitialize换成CoInitializeEx以进入MTA,任务将终止失败)。但由于此线程中没有生成任何COM组件对象,即此STA套间内不包含任何对象,因此无需编写消息循环。

  第二种情况中,假设在每次发起的调用线程中都通过调用CoCreateInstance以获得业务组件的一个全新实例,然后再调用其上的方法,则实际上其只会被发起的调用线程这一个线程调用,没有存在于MTA套间中的必要。这是非常符合MTS提供的编程模型的编写方式,通过将业务组件注册成MTS组件,并实现对象池功能和开启即时激活特性(Just-In-Time Activation)则上面由于每次调用CoCreateInstance而导致的损耗几乎等于没有,但程序的结构却非常简单,不需要复杂的逻辑。

  不过此样例并没有考虑编写为MTS组件,并且上面的好处正是本文的坏处,无法演示多线程对业务组件的调用处理。故提供一个全局的业务组件对象,使其为Free组件,并在每次发起的调用线程中都通过ITaskNotify通知正确的线程任务的进度(实际只有一个线程会被通知——客户端主线程。这里只是说明如何使用正确的代理对象进行通知,但由于业务组件是在MTA套间中,且只有一个线程会被通知,因此并没有保留ITaskNotify*的中立形式的必要,即各调用线程的代理对象就意义上是一样的了,也许实现上会有细微差别,视COM运行时期库的实现。但在此作为演示还是使用中立形式保存ITaskNotify*)。

  而对于第二种情况线程的终止(参看下面的代码),我只是让业务逻辑就是WaitForSingleObject一个事件来及时地响应终止事件和模拟工作的耗时,实际中当然不可能这样。实际中的业务代码如果是一个循环,则可以通过每次循环时短时间等待一个事件(或调用ICancelMethodCalls的方法),没有事件则继续循环工作(这种情况下最好是监视一个全局变量的值而不是等待一个事件)。如果不是循环却又是很耗时的操作,且操作没有提供任何中断的接口,则只能强行终止了。

  本样例就上面两种情况,分别实现业务组件接口中的两个方法Task1和Task2,此业务组件为Free类型的进程内组件。由于代码较多,在此仅列出第二种情况中的业务组件代码和部门组件中的发起调用的代码。


1 2 3  下一页

·"WAP天极之IT新闻资讯,50万元等你拿"    ·天极WAP之游戏狂图,50万元等你下载


发表评论推荐给朋友我想参加相关培训打印我对此感兴趣订阅电子杂志
相关内容阅读排行榜
  • 实战COM编程系列之二
  • 实战COM编程系列之一
  • COM线程模型详解
  • 用VC++实现ODBC数据源设置
  • Visual C++中的ODBC编程实例
  • 改善电视卡接收效果三个诀窍
  • 液晶该不该使用屏保等问答6则
  • 自力更生维修4.1音箱实例
  • 发烧大餐 六千元游戏配置
  • 10.26精选 图画:葡萄小鸡图
  • 网络打印服务器选购攻略
  • 信不信集成显卡BIOS我也照刷
  • 李鬼哪里逃 处理器假货曝光
  • Advertisement

    天极无线
    待机彩图    >>更多
    多彩动画    >>更多
    美妙和弦    >>更多
    天使在唱歌
    壁虎漫步
    PrettyBoy
    LoveLoveLove
    我是你的小小狗
    单身情歌
    十面埋伏
    祝酒歌
    回心转意
    波斯猫
    太委屈
    S.H.E
    潘玮柏
    M2M
    蔡依林
    阿牛
    林志炫
    陈弈迅
    刀郎
    黑龙
    S.H.E
    陶晶莹
    情人玫瑰坊
    音乐风云
    新片速递
    神秘测试
    都市约会
    ·天极彩信天天精彩
    ·图铃梦工厂下载无限!
    ·找寻童真,卡通专题
    ·夏日激情交友社区!
    ·星座运程,预测人生

    CSEEK搜索