COM线程模型
要注意单线程与多线程的问题
https://blog.csdn.net/zj510/article/category/2510453
通常我们提交COM的线程模型,其实指的是两方面:一个是客户程序的线程模式,一个是组件所支持的线程模式。客户程序的线程模式只有两种,单线程公寓(STA)和多线程公寓(MTA)。组件所支持的线程模式有四种:Single(单线程)、Apartment(STA)、Free(MTA)、Both(STA+MTA)。
注意,公寓和套间是同一个概念,这只是翻译而已,都是指apartment
STA客户调用STA COM组件
STA对象在客户创建的STA套间线程里面运行;
STA客户直接调用STA COM对象指针;
MTA客户调用STA COM组件
- STA对象在default STA里面运行,如果有多个STA对象,它们统统在同一个default sta线程里面运行。
- MTA客户调用STA COM对象的代理。
谈谈COM的线程 模型 。然后 讨论 进程内/外组件的差别
中文翻译为“ 公寓 ” ,有时候为 “ 套间 ” ,这里就翻译为 “ 公寓 ” 吧,都一个意思,指的就是COM的线程模式,这个概念很抽象,理解起来比较困难。因为公寓不像 Windows内核对象那样有个 “ 句柄 ” ,并且跟公寓相关的Windows API很少,只有5个:CoInitialize,CoUninitialize,CoInitializeEx,OleInitialize和 OleUninitialize,大家都很熟悉了,5个关于COM初始化和反初始化的函数。如何来理解公寓呢?可以这样:1、线程住在公寓中;2、对象住在公寓中。有时候,对象和创建它的线程住在同一个公寓中,有时候不是。这样还是很难理解对吧,但没事,这个先记下来,后面会明白的。
COM只有两种公寓,一种叫单线程公寓(Single-Thread Apartment),简称STA,一种叫多线程公寓(Multi-Thread Apartment),简称MTA,顾名思义,一种只能容纳一个线程,另一种能容纳多个线程。在一个进程中,MTA只有一个,而STA可以有很多。
我们在使用COM之前,都应该先初始化COM,怎么初始化?当然是前文提到的那几个函数了,CoInitialize,CoInitializeEx和OleInitialize,那我们是每个程序(进程)初始化一次还是每个线程初始化一次?答案是线程,每个线程初始化一次,这么个初始化,就相当于把这个线程安置在某个公寓中。具体这样的:CoInitialize或OleInitialize把线程放置入STA;CoInitializeEx允许你把线程放置入MTA。从公寓中移除线程的方法是CoUninitialize和OleUninitialize。
我们都知道,对象是线程创建的,那对象什么时候跟创建它的线程同一个公寓,什么时候不是同一个公寓呢?前面说了线程所在的公寓类型是由那几个初始化函数所决定,那对象所在什么公寓是由什么决定的呢?这个稍微复杂一点,答案是:由创建它的线程的类型及对象本身的线程属性所决定。线程类型大家都知道啦,就前面提到的由那几个初始化函数决定,那么对象本身线程属性怎么来定呢?答案:注册表里的信息来定。
打开注册表编辑器,按照这路径: “ \ HKEY_CLASSES_ROOT\CLSID{00000010-0000-0010-8000-00AA006D2EA4}\InprocServer32 ” ,(这个 GUID很奇怪吧,明显不是用工具生成的,微软可有手动填写GUID的特权哦)看看 “ ThreadingModel ” 的值,嗯,没错,是 “ Apartment ” ,这个 “ Apartment ” 就是刚才我所提到的 “ 对象本身线程属性 ” ,对象本身线程属性一共有四种:Apartment、Both、Free和Single。下面我列个表,一目了然。
组件线程属性 | STA线程 | MTA线程
Apartment | 同一公寓中,直接访问 | 创建一个STA,用代理访问
Free | MTA中,用代理访问 | MTA,直接访问
Both | 同一公寓中,直接访问 | MTA,直接访问
Single | 主STA中,通过主STA访问
备注:第一个以 COINIT_APARTMENTTHREADED调用CoInitializeEx()的线程被称作是主STA。
例如一个STA线程创建了一个本身线程属性为 Free的对象,那该对象存在于MTA中,这个STA线程访问它就得通过代理,当然了,这对程序员来说是透明的,因为这个功能是靠COM的 remoting层来实现的。要说和直接访问有什么能体现出来的不同,可能就是通过代理访问会慢一些,毕竟消息需要Marshalling,但这几毫秒的时间差你们地球人是很难感觉出来的( J )。
那么很明显了,只要我们把组件类型设置为 Apartment,就不会有任何线程访问冲突的问题。《Win32 多线程》侯捷
进程内/外组件的差别是Com组建的一种表现形式:.exe文件,它是进程外组建,dll是进程内组建.
关于COM组件线程模型的实验
单线程模型(Single)
在注册表中删除上述ThreadingModel键值,则COM组件被配置为使用单线程模型。使用单线程模型的组件只能存在于主STA,也就是进程中的第一个STA中。对于具有图形界面的Windows程序,第一个STA通常由主线程,也就是界面线程创建。具有图形界面的COM组件,比如说,ActiveX控件,常常使用单线程模型。
套间线程模型(Apartment)
设置上述ThreadingModel键值为Apartment,则COM组件被配置为使用套间线程模型。使用套间线程模型的组件只能存在于STA中。套间线程模型是在Visual Studio中使用ATL开发COM组件时默认的线程模型。
单线程模型和套间线程模型的共同点是在任何时刻只有一个线程可以直接访问组件,这个线程就是创建组件所在的STA的线程(不一定是调用CoCreateInstance创建组件的线程)。其他线程对组件的调用都是通过这个线程间接进行的:COM基础设施为STA创建一个隐藏的窗口,将其他线程对STA中组件的调用请求转化为发送给这个窗口的消息,然后由套间中唯一的线程处理消息,返回调用结果。所以,使用单线程模型和套间线程模型的组件要求消息队列,其他线程对组件的调用都是间接地通过消息队列进行的。这一点很重要。本文后面将通过代码验证这一点。
自由线程模型(Free)
设置上述ThreadingModel键值为Free,则COM组件被配置为使用自由线程模型。使用自由线程模型的组件只能存在于MTA中,可以被处于MTA中的多个线程“自由”地调用。不在MTA中的线程调用MTA组件时,COM基础设施随机选择RPC线程池中的某个RPC线程代为间接处理(RPC线程池是COM基础设施的组成部分)。由于COM基础设施没有提供任何同步方面的帮助,多个线程可以并发地调用组件的方法,所以需要编写代码对组件实施必要的保护,就像多线程编程中需要对共享资源实施保护一样。
双线程模型(Both)
设置上述ThreadingModel键值为Both,则COM组件被配置为使用双线程模型。此时组件与创建组件的线程存在于相同的套间中:既可能是STA,也可能是MTA。因为组件可能存在于MTA中,被多个线程并发访问,所以需要编写代码对组件实施必要的保护。
自由线程模型和双线程模型有一个重要的差别:采用自由线程模型的组件可以创建能够直接调用组件的工作线程;而采用双线程模型的组件不能。因为采用双线程模型的组件可能位于STA中,如果组件创建的工作线程可以直接访问组件,则工作线程也必须位于STA中(套间之外的线程对组件的调用不能直接进行),这就违反了STA中只能有一个线程的规则,破坏了COM线程模型的同步机制。
线程中立模型(Neutral)
设置上述ThreadingModel键值为Neutral,则COM组件被配置为使用线程中立模型。使用线程中立模型的组件位于TNA中,可以被任何线程自由地、直接地访问。调用线程访问这种类型的组件时将暂时离开所属的STA或者MTA,进入TNA,直接对组件进行方法调用,调用完成后返回STA或者MTA。与采用自由线程模型和双线程模型的组件一样,必须编写代码对组件实施必要的保护,以防止多线程并发访问可能出现的问题。线程中立模型是运行在组件服务中的,不需要用户界面的组件的最优选择。
什么是COM
《Visual C++从入门到精通(视频实战版)》
COM是Component Object Model(组件对象模型)的缩写。不管用户需要什么样的产品,以下两个条件是必须要保证的:
- 高效:使用软件产品可以做的工作,一般而言,使用手工劳动一样能达到同样效果,只是软件可以快速处理相同工作。在众多软件的选择中,高效的软件具备无可比拟的优势。
- 健壮:如果一个软件系统出现很小的问题就会异常终止,导致整个系统工作无法进行,并且需要系统管理员不厌其烦地重启服务器,这样的软件还会有人购买吗?因此,健壮性也是软件产品的一个必要条件。 面向对象的程序设计可以说是一次革命性的变革,因为面向对象的程序设计可以将要解决的问题和对象抽象成为数据对象,将功能和动作封装起来,并提供一些接口。但当今的计算机时代是“后OO时代”(后面向对象时代),即面向组件的时代。 COM是一种跨应用和语言共享二进制代码的方法。
- 跨应用:指不同的应用程序可以使用或共享COM组件。
- 跨语言:指不同的语言可以调用同一个COM组件。
COM提倡源代码重用,而ATL则在代码重用方面不如COM,因为ATL只是在代码级别上的重用好,但只能用于C++语言,如果不使用名称控件,还可能引起名字冲突的问题。同时,这样的重用很可能会导致工程膨胀和源代码臃肿。 Windows使用DLLs在二进制级共享代码。所以Windows程序运行经常会调用到诸如kernel32.dll、user32.dll等。但DLLs是针对C接口(Win32 SDK)而写的,它们只能被C或理解C调用规范的语言(如C++)使用。由编程语言来负责实现共享代码,这样会导致DLLs的使用受到限制。 MFC引入了另外一种MFC扩展DLLs二进制共享机制。但它的使用仍受限制,因为其只能在MFC程序中使用。 COM通过定义二进制标准解决了这些问题,即COM明确指出二进制模块(DLLs和EXEs)必须被编译成与指定的结构匹配。实际上,COM在这里充当了中间层的作用。这个标准也确切规定了在内存中如何组织COM对象。COM定义的二进制标准还必须独立于任何编程语言,这样才能对任何语言独立平等地进行对待。满足这些条件之后,就可以很方便地从其他语言中使用这些模块中的功能。由编译器负责所产生的二进制代码与标准兼容。这样使后来的人就能更容易地使用这些二进制代码。 大多数COM的代码都是用C++编写的,但并非就表明编写代码一定要用C++,编写COM组件是与开发语言没有关系的,而且二进制代码也可以为所有语言所用。 注意 在操作系统层次中,COM也不是Win32特有的。从理论上讲,它可以被移植到UNIX或其他操作系统中。
COM接口
COM的接口都必须从IUnknown继承,犹如在MFC中,CObject类的地位。IUnknown有三个重要的函数: 1)QueryInterface()函数。该函数的原型如下: HRESULT__stdcall QueryInterface(const IID&iid,void**ppv); 该函数的参数的含义是:
iid:标志客户所需的接口,是一个接口标志符“结构(IID)”。
ppv:QueryInterface用来存放所请求接口的地址。
该函数的返回值可以是S_OK或E_NOINTERFACE,并且应该用SUCEEDED或者FAILED宏验证是否调用成功。
该函数的使用方法是:假如知道一个指向IUnknown接口的指针pI,传给它一个接口标志符即可。 使用例子可以如以下代码所示:
void Function( IUnknown*pI )
{
IX*pIX = NULL; HRESULT hr = pI - > QueryInterface( IID_IX, (void * *) pIX ); if ( SUCCEEDED( hr ) )
{
pIX - > FX();
}
}
- Addref()函数:用于增加引用计数。
- Release()函数:用于释放引用计数。
这三个函数的顺序是不能变化的。QueryInterface用于查询组件实现的其他接口,也就是看看这个组件的父类中还有哪些接口类,AddRef用于增加引用计数,Release用于减少引用计数。一般在以下两种情况下必须调用Release:
调用QueryInterface后。 调用了任何得到一个接口指针的函数后。 IDispatch接口把每一个函数每一个属性编上号,Client要调用这些函数、属性的时候就把这个编号传给IDispatch接口。开发组件时一般有两种方式:
- ATL:使用ATL对于用C++创建COM组件和节省空间都是一种快速简便的方法。如果不需要MFC自动提供的所有内置功能,使用ATL创建控件。
- MFC:MFC允许创建具有完整功能的应用程序、ActiveX控件和活动文档。 如果已经使用MFC创建了控件,可能需要继续使用MFC进行开发。当创建新控件时,如果不需要MFC的所有内置功能,可考虑使用ATL。ATL与传统的C++模板库的区别:ATL通常只作为源代码提供,并且在本质上没有固有的分层结构或没有必要有分层结构;不是从派生类得到所需的功能,而是从模板中实例化类。
COM组件、ActiveX、OCX区别
COM 组件就是一组接口的集合,实现了 IDispath 接口的 COM 组件叫做 自动化组件。在自动化组件的基础上,在实现规定的接口就称为 ActiveX 组件,Ocx 是 Activex 的文件载体
一般来讲 ,一个 ActiveX 对应一个 Ocx 文件, 如果愿意, 一个Ocx 文件也可以包含多个 ActiveX 控件。
COM是microsoft制定的一个组件软件标准,跟unix上的CORBA一样。凡是遵循COM标准开发出来的组件称为COM组件。简单的说就是要实现在二进制方式的重用 。
在windows平台上,COM的实现形式有DLL(进程内组件)和EXE(进程外组件)2种。
ActiveX是Microsoft提出的一组使用COM(Component Object Model,部件对象模型)使得软件部件在网络环境中进行交互的技术集。它与具体的编程语言无关。作为针对Internet应用开发的技术,ActiveX被广泛应用于WEB服务器以及客户端的各个方面。同时,ActiveX技术也被用于方便地创建普通的桌面应用程序,此外ActiveX一般具有界面。