zoukankan      html  css  js  c++  java
  • COM接口在不同线程中的传递技巧两则

    在多线程程序中使用COM对象真是件令人头疼的事情。当你能够访问一个接口指针的时候,并不意味着你可以调用接口上的方法。我从来没真正搞懂过所谓COM的几种线程模式。问题是,当我试图在一个线程里调用一个COM接口的方法,而这个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传递结构的地址。总之人是活的,灵活运用。 

  • 相关阅读:
    C++中关于文本内容的实用操作集合(新)(添加一些关于文件流的介绍)
    C++_String_类字符串操作(转)
    C++下面关于字符串数组的一些操作
    windows下 berkerly db的安装配置(修正了关键步骤)
    代码审计基础知识
    Spring中的依赖查找和依赖注入
    JavaSE实现IoC
    LeetCode 87. Scramble String
    LeetCode 76. Minimum Window Substring
    LeetCode 169. Majority Element
  • 原文地址:https://www.cnblogs.com/fwycmengsoft/p/3069427.html
Copyright © 2011-2022 走看看