FW:· COM组件中的线程模式

    技术2022-07-03  115

    提及COM的线程模式,实际上指的是两个方面,一个是客户程式的线程模式,一个是组件所支持的线程模式。客户程式的线程模式只有两种,单线程公寓(STA)和多线程公寓(MTA)。组件所支持的线程模式有四种:Single(单线程)、Apartment(STA)、Free(MTA)、Both(STA+MTA)。

    1、公寓只是个逻辑上的概念。一个STA只能包含一个线程,一个MTA能包含多个线程。一个进程能包含多个STA,但只能有一个MTA。MTA中各线程能并行的调用本公寓内实例化的组件,而不必进行调度。跨公寓调用组件实例必须要进行调度。(除非使用了自由线程调度器)

    2、客户程式的线程是在调用CoInitializeEx()时决定客户线程的类型的。如果以参数COINIT_APARTMENTTHREADED调用,则会创建一个STA公寓,客户线程包含在这个公寓里。如果以参数COINIT_MULTITHREADED调用,则创建一个MTA公寓,把线程加入到这个MTA中;如果进程内已有了一个MTA,则不创建新的MTA,只把线程加入到已有的MTA。注意每个线程都必须调用CoInitializeEx()才能使用COM组件。

    3、线程最重要的是同步问题。STA是通过窗口消息队列来解决这个问题的。当客户线程以COINIT_APARTMENTTHREADED调用CoInitializeEx()时,将为会该STA创建一个具有OleMainThreadWndClass窗口类的隐含窗口。所有对在这个公寓中建立的COM对象方法的调用都将都放到这个隐含窗口的消息队列中。所以每一个和STA相关联的线程必须用GetMessage、DispatchMessage或类似方法来分派窗口消息。MTA内各线程可并行调用同一个组件对象的实例,从而不确保安全性,所以实现同步访问的责任就落在了组件身上。注意,STA的同步是公寓级的,就是说对公寓内不同组件的访问都要放到同一个消息队列中,对一个实例的方法调用会影响对其他实例的调用,所以并发程度非常低。

    4、在不同公寓间传递接口指针必须要经过调度。这主要还是为了同步对组件的调用。通过CoMarshalInterThreadInterfaceInStream和CoGetInterfaceAndReleaseStream实现。非常简单。

    5、Single型组件非常特别,他只能在一个单一的线程中执行。首先要说明的是个进程中第一个以COINIT_APARTMENTTHREADED调用CoInitializeEx()的线程被称作是主STA。每次用CoCreateInstance()创建的Single型组件实际上都是创建在了这个主STA中,而不管是谁调用了CoCreateInstance()这个函数。所有对这个Single组件方法的调用都必须要通过这个主STA。

    6、若STA创建STA型组件,是直接创建,直接调用。若STA创建MTA型组件,系统为组件创建一个MTA,STA通过代理访问组件。若STA创建Both型组件,是直接创建,直接调用。若MTA创建STA型组件,系统为组件创建一个STA,MTA通过代理访问组件。若MTA创建MTA型组件,是直接创建,直接调用。若MTA创建Both型组件,是直接创建,直接调用。可见如果客户程式和组件都支持同样的线程模式,那么COM就允许客户程式直接调用对象,这样将产生最佳性能。

    7、Both型组件已非常好了,无论是STA还是MTA都能直接创建调用他。但跨公寓的调用仍然要经过代理。为了更进一步以获得最佳性能,能使用自由线程调度器(FTM)。注意其他类型的组件也能使用FTM,只是由Both使用FTM可获得是最佳效果。FTM实现了接口IMarshal,当调度那两个调度接口指针的函数时,这两个函数(见5)内部调用IMarshal内的相关函数,并判断如果调度发生在一个进程内的公寓之间则直接返回接口指针;如果调度发生在进程之间或远程计算机间,则调用标准的调度器,并返回指向代理对象的指针。所以可见使用FTM,即使是公寓之间也不用调度接口指针了!!

    8、FTM虽然好,但使用FTM的组件必须遵守某些限制:使用FTM的对象不能直接拥有没有实现FTM的对象的接口指针;使用FTM的对象不能拥有其他公寓对象代理的引用。

    9、全局接口表(GIT)。作用范围是进程内。能把接口指针存进表中,然后在别的公寓内把其取出,GIT自动执行公寓间的调度,所以非常方便。GIT是通过IGlobalInterfaceTable访问的。通过创建CLSID为CLSID_StdGlobalInterfaceTable的对象可调用他。

    · COM组件的使用方法

    Requirement: 1.创建myCom.dll,该COM只有一个组件,两个接口IGetRes–方法Hello(),   IGetResEx–方法HelloEx() 2.在工程中导入组件或类型库   #import "组件所在目录myCom.dll" no_namespace 或   #import "类型库所在目录myCom.tlb"   using namespace MYCOM; –Method 1——————————————————-   CoInitialize(NULL);   CLSID clsid;   CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);   CComPtr pGetRes;//智能指针   pGetRes.CoCreateInstance(clsid);   pGetRes->Hello();   pGetRes.Release();//小心哦!!请看最后的“注意”   CoUninitialize(); –Method 2———————————————————   CoInitialize(NULL);   CLSID clsid;   HRESULT hr=CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);   IGetRes *ptr;   hr=CoCreateInstance(clsid,NULL,CLSCTX_INPROC_SERVER,                 __uuidof(IGetRes),(LPVOID*)&ptr);   ptr->Hello();   CoUninitialize(); –Method 3——————————————————–   CoInitialize(NULL);   HRESULT hr;   CLSID clsid;   hr=CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);   IGetRes* ptr;   IGetResEx* ptrEx;

      //使用CoCreateClassObject创建一个组件(特别是mutilThreads)的多个对象的 时候,效率更高.   IClassFactory* p_classfactory;   hr=CoGetClassObject(clsid,CLSCTX_INPROC_SERVER,                       NULL,IID_IClassFactory,                       (LPVOID*)&p_classfactory);   p_classfactory->CreateInstance(NULL,__uuidof(IGetRes),                                          (LPVOID*)&ptr);   p_classfactory->CreateInstance(NULL,__uuidof(IGetResEx),                                          (LPVOID*)&ptrEx);   ptr->Hello();   ptrEx->HelloEx();   CoUninitialize(); –Method 4——————————————————– 直接从dll中得到DllGetClassObject,接着生成类对象及类实例(这方法可以 使组件不用在注册表里注册,这是最原始的方法,但这样做没什么意义,至少失去了COM 对用户的透明性),不推荐使用.

    typedef HRESULT (__stdcall * pfnHello)(REFCLSID,REFIID,void**);   pfnHello fnHello= NULL;   HINSTANCE hdllInst = LoadLibrary("组件所在目录myCom.dll");   fnHello=(pfnHello)GetProcAddress(hdllInst,"DllGetClassObject");   if (fnHello != 0)   {   IClassFactory* pcf = NULL;   HRESULT hr=(fnHello)(CLSID_GetRes,IID_IClassFactory,(void**)&pcf);   if (SUCCEEDED(hr) && (pcf != NULL))   {   IGetRes* pGetRes = NULL;   hr = pcf->CreateInstance(NULL, IID_IFoo, (void**)&pGetRes);   if (SUCCEEDED(hr)  && (pFoo != NULL))   {   pGetRes->Hello();   pGetRes->Release();   }   pcf->Release();   }   }   FreeLibrary(hdllInst); –Method 5——————————————————- 通过ClassWizard利用类型库生成包装类,不过前提是com组件的接口必须是派 生自IDispatch,具体方法: 调出添加类向导(.NET中),选择类型库中MFC类,打开,选择"文件",选择 "myCom.dll"或"myCom.tlb",接下来会出来该myCom中的所有接口,选择你想 生成的接口包装类后,向导会自动生成相应的.h文件.这样你就可以在你的MFC中 像使用普通类那样使用组件了.

    CoInitialize(NULL);   CGetRes getRest;   if (getRest.CreateDispatch("myCom.GetRes") != 0)   {   getRest.Hello();   getRest.ReleaseDispatch();   }   CoUninitialize();

    COM中的智能指针实际上是重载了->的类,目的是为了简化引用记数,几不需要程序 员显示的调用AddRef()和Release(),但是为什么我们在Method 1中 pGetRes.Release(),问题在与,我们的智能指针pGetRes生命周期的结束是在 CoUninitialize()之后,CoInitialize所开的套间在CoUninitialize()后已经被 关闭,而pGetRes此时发生析构,导致了程序的崩溃,解决这个问题的另一个方法是   CoInitialize(NULL);   CLSID clsid;   CLSIDFromProgID(OLESTR("myCom.GetRes"),&clsid);   {   CComPtr pGetRes;//智能指针   pGetRes.CoCreateInstance(clsid);   pGetRes->Hello();   }   CoUninitialize();

    ——————————————————————- 以上就是COM的5中方法,当然具体怎么使用还是在于程序的环境,加以琢磨….                                                         vigor                                                       2004.11.5

      CoInitialize(NULL);   CGetRes getRest;   if (getRest.CreateDispatch("myCom.GetRes") != 0)   {   getRest.Hello();   getRest.ReleaseDispatch();   }   CoUninitialize(); –注意———————————————-


    最新回复(0)