上一篇:VC中利用CRC校验码为BMP图设置水印 >>
COM接口在不同线程中的传递技巧两则
用全局接口表实现COM接口在不同线程中的传递
在多个线程中传递接口需要额外的工作,各种书籍上都介绍了所谓Marshal一个接口方法,不过我从来没有使用过。因为有更简单的方法,就是用全局接口表(GlobalInterfaceTable)。
GlobalInterfaceTable允许你在任何地方访问任何线程中创建的COM接口。GlobalInterfaceTable本身是一个COM对象,它实现了IGlobalInterfaceTable接口。这个接口有三个方法,分别用来注册接口,取得接口和注销接口。下面的例子介绍了GlobalInterfaceTable的基本使用方法。
首先当创建一个需要在其他线程使用的COM接口时,把它注册到GlobalInterfaceTable。
| CComPtr<IMyInterface> spMyInterface; spMyInterface.CoCreateInstance(); //register interface in global interface table CComPtr<IGlobalInterfaceTable> spGIT; spGIT.CoCreateInstance(CLSID_StdGlobalInterfaceTable); if (spGIT) { spGIT->RegisterInterfaceInGlobal(spMyInterface, IID_IMyInterface, &m_dwCookie); } |
注册时会返回一个Cookie,记住这个Cookie,并在任何线程需要使用前面接口时,通过这个Cookie获得接口。
| CComPtr<IMyInterface> spMyInterface; if (m_dwCookie!=0) { CComPtr<IGlobalInterfaceTable> spGIT; spGIT.CoCreateInstance(CLSID_StdGlobalInterfaceTable); if (spGIT) { spGIT->GetInterfaceFromGlobal(m_dwCookie, IID_IMyInterface, (void**)&spMyInterface.p); } } if (spMyInterface) { //Call my interface } |
最后,作为一个负责任的程序员,关闭之前一定要注销前面注册的接口。
| if (m_dwCookie!=0) { CComPtr<IGlobalInterfaceTable> spGIT; spGIT.CoCreateInstance(CLSID_StdGlobalInterfaceTable); if (spGIT) { spGIT->RevokeInterfaceFromGlobal(m_dwCookie); m_dwCookie = 0; } } |
需要注意的时,我们每次都是新创建GlobalInterfaceTable的实例,因为它本身也是COM对象,它的指针不能在不同线程间传递。
用窗口消息解决COM接口的多线程访问问题
但有时候我们不能直接访问接口指针,而是通过一个封装类间接的访问。比如:
| class SomeClass { private: IMyInterface *m_pInt; public: void Method1() { //Init m_pInt } void Method2() { //call method of m_pInt } } |
我们只能访问SomeClass的公共方法,而无法直接访问接口指针,这时就不能使用使用全局接口表的方法。如果需要在不同线程中调用SomeClass的方法,唯一的办法就是把所有的调用放在一个线程中。怎么做到这一点呢?用一个消息窗口来同步是一个简单的方法。
首先定义一个窗口类,把所有对SomeClass的操作定义成窗口消息,如下:
| #define WM_METHOD1 WM_USER + 100 #define WM_METHOD2 WM_USER + 101 class CThreadWnd : public CWindowImpl<CThreadWnd> { private: SomeClass m_someClass; public: BEGIN_MSG_MAP(CThreadWnd) MESSAGE_HANDLER(WM_METHOD1, OnMethod1) MESSAGE_HANDLER(WM_METHOD2, OnMethod2) END_MSG_MAP() LRESULT OnMethod1(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); LRESULT OnMethod2(UINT /*uMsg*/, WPARAM /*wParam*/, LPARAM /*lParam*/, BOOL& /*bHandled*/); } |
在OnMethod1和OnMethod2中完成对m_someClass的方法调用,如果方法有参数,通过wParam和lParam传递。
然后,我们需要做的是在一个线程中创建这个窗口。并在需要调用SomeClass的方法时,通过向CThreadWnd窗口发送消息间接完成。例如:
| CThreadWnd wndThread; //In thread A wndThread.SendMessage(WM_METHOD1); //In thread B wndThread.SendMessage(WM_METHOD2); |
至于wndThread,可以保存在任何地方。而创建线程窗口的线程函数代码基本上应该是这样的:
| UINT WINAPI ThreadProc(LPVOID lpParam) { ::CoInitialize(0); CThreadWnd *pWnd = (CThreadWnd*)lpParam; CMessageLoop theLoop; _Module.AddMessageLoop(&theLoop); pWnd->Create(NULL, CRect(0, 0, 0, 0), NULL, WS_POPUP); theLoop.Run(); pWnd->DestroyWindow(); _Module.RemoveMessageLoop(); ::CoUninitialize(); return 0; } |
创建线程的代码就不贴了,销毁线程则只要向线程窗口发送WM_QUIT消息即可。这个方法在实际应用中个人感觉非常有效。
有一个问题是当方法参数比较多时,很难通过wParam和lParam传递。这就需要另外定义一个结构,存放各种参数,然后通过wParam传递结构的地址。总之人是活的,灵活运用。
阅读关于 VC 的全部文章
下一篇:JFC/Swing活学活用之创建自定义图像组件 >>
相关文章:
- · 发改委与微软签署软件产业合作谅解备忘录
- · 微软与信产部签协议 2.5亿投资农村信息化
- · Sun发布Beta 2测试版Java SE 6
- · .NET2.0中一种简单的窗口控件状态控制法
- · ASP.NET开发购物推车之购物车类
- · ASP.NET中的三种表格化数据方法
- · ASP.NET入门随想之多态、接口与委托
- · ASP.NET入门随想之明明白白我的心
- · COM组件设计与应用之编译、注册、调用
- · COM 组件设计与应用之数据类型
- · COM组件设计与应用之.NET中用ATL写组件
- · MATCOM在VC++与MATLAB混合编程中的应用
- · COM组件设计与应用之实现多接口
- · 美军为防黑客入侵禁用商业电子邮箱
- · Oracle中使用PL/SQL操作COM对象
- · 驯服Tiger之访问环境变量和调用子进程
- · VC++初学者经典错误LNK2001详解
- · ODBC中的FX/Bulk RFX数据交换机制分析
- · Visual C++中DDB与DIB位图编程全攻略
- · 泛型与模板的有机结合 STL.NET简介
- · 用VC实现特定编辑框上对回车键响应
- · 用Visual C# 实现四则混合运算
- · C#中利用mediaplayer打造mp3播放器
- · C#开发的两个原则的深入讨论
- · Visual C#构建网络计算机树形图
- · C# 3.0 新特性:扩展方法初探
- · Visual Studio2005改变软件教育模式
- · Visual C#中编写多线程程序之起步
- · Visual C#实现文件分割合并器
- · 基于.NET的多线程编程入门
- · 体验Java 5.0的新增语言特性
- · 漫谈Java程序的性能优化
- · 专访Java之父:Java是否能够与时俱进
- · 基于Java的Google地图跻身手机屏幕
- · James Gosling预言Java:手机将一统桌面
- · 美杂志评25件最糟糕IT产品 Win2000上榜
- · Java初学者都必须理解的六大问题
- · 两种特殊的Java容器类List和Set分析
