- 浏览: 2035760 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (651)
- ACE (35)
- BAT (9)
- C/C++ (116)
- fast-cgi (14)
- COM (27)
- python (59)
- CGI (4)
- C# (2)
- VC (84)
- DataBase (29)
- Linux (96)
- P2P (6)
- PHP (15)
- Web (6)
- Memcached (7)
- IME输入法 (11)
- 设计模式 (2)
- 搜索引擎 (1)
- 个人情感 (4)
- 笔试/面试 (3)
- 一亩三分地 (33)
- 历史 (2)
- 地理 (1)
- 人物 (3)
- 经济 (0)
- 不仅仅是笑哦 (43)
- 小故事大道理 (2)
- http://www.bjdsmyysjk120.com/ (0)
- http://www.bjdsmyy120.com/ (0)
- 它山之石可以攻玉 (15)
- 大学生你关注些什么 (28)
- 数据恢复 (1)
最新评论
-
luokaichuang:
这个规范里还是没有让我明白当浏览器上传文件时,STDIN的消息 ...
FastCGI规范 -
effort_fan:
好文章!学习了,谢谢分享!
com技术简介 -
vcell:
有错误os.walk(strPath)返回的已经是全部的文件和 ...
通过python获取目录的大小 -
feifeigd:
feifeigd 写道注意:文章中的CPP示例第二行 #inc ...
ATL入门:利用ATL编写简单的COM组件 -
feifeigd:
注意:文章中的CPP示例第二行 #include " ...
ATL入门:利用ATL编写简单的COM组件
STA 线程需要消息循环
上一部分中描述的应用程序还有另一个致命缺陷。看看您是否能指出来。
这个特殊的应用程序恰好是用 MFC 编写的。在一开始,它使用了 MFC 的 AfxBeginThread 函数启动一系列辅助线程。每个辅助线程要么调用 CoInitialize 要么调用 AfxOleInit(MFC 中类似 CoInitialize 的函数)来初始化 COM。某些辅助线程则调用 CoCreateInstance 来创建 COM 对象,并将所返回的接口指针封送到其他辅助线程。从创建这些对象的线程中调用对象将非常顺利,但从其他线程的调用却从不返回。您知道这是为什么吗?
如果您认为问题与消息循环(或缺少消息循环)相关,那么答案完全正确。事实确实如此。当一个线程调用 CoInitialize 或 AfxOleInit 时,它是放在单线程单元 (STA) 中。当 COM 创建一个 STA 时,它会创建一个随附的隐藏窗口。以 STA 中的对象为目标的方法调用将转换为消息,并放入与该 STA 关联的窗口的消息队列中。当运行在该 STA 中的线程检索到代表方法调用的消息时,隐藏窗口的窗口过程就会将消息转换回方法调用。COM 使用 STA 执行调用序列化。STA 中的对象一次不可能接收一个以上的调用,因为每个调用都要传递给一个而且是惟一一个运行在对象单元中的线程。
如果基于 STA 的线程无法处理消息会怎么样呢?如果它没有消息循环又会怎么样呢?针对该 STA 中对象的单元间方法调用将不再返回;它们将在消息队列中被永远搁置。MFC 辅助线程中没有消息循环,因此如果寄宿在这些 STA 中的对象要从其他单元的客户端接收方法调用,那么 MFC 辅助线程和 STA 是配合不好的。
这个故事的寓意何在呢?STA 线程需要消息循环,除非您肯定它们不会包含要从其他线程调用的对象。消息循环可以像这样简单:
MSG msg;
while (GetMessage (&msg, 0, 0, 0))
DispatchMessage (&msg);
另一种方案是将 COM 线程移到 MTA 中(或者在 Windows 2000 中,移到中立线程单元,即 NTA 中),这里没有消息队列依赖项。
单元模型对象必须保护共享数据
另一个困扰 COM 开发人员的通病是标记为 ThreadingModel=Apartment 的进程内对象。这项指定告诉 COM,对象的实例必须只能在 STA 中创建。它还可让 COM 自由地将这些对象实例放在任何主机进程的 STA 中。
假设客户端应用程序有五个 STA 线程,每个线程都使用 CoCreateInstance 来创建同一个对象的一个实例。如果线程是基于 STA 的,且对象标记为 ThreadingModel=Apartment,则这五个对象实例将在对象创建者的 STA 中创建。因为每个对象实例都在占用其 STA 的线程上运行,因此所有五个对象实例都可以并行运行。
到目前为止,一切良好。现在考虑一下,如果这些对象实例共享数据会发生什么情况。因为对象都在并发线程上执行,两个或更多的对象可能会同时尝试访问同一个数据。除非所有这些访问都是读取访问,否则就会酿成灾难。问题可能不会很快显现出来;它们会以和时间紧密相关的错误形式出现,因此很难诊断和重现。这就解释了以下事实的原因:ThreadingModel=Apartment 对象应该包括可同步对共享数据的访问的代码,除非您能够确定对象的客户端不会对执行访问的方法进行重叠调用。
问题在于,太多的 COM 开发人员相信 ThreadingModel=Apartment 能够使他们免于编写线程安全的代码。事实并非如此 ? 至少不完全如此。ThreadingModel=Apartment 并不意味着对象必须是完全线程安全的,它代表的是一个对 COM 的承诺,即访问两个或更多对象实例共享的数据(或此对象和其他对象的实例共享的数据)时是以线程安全的方式进行的。而提供该线程安全性的任务应该由您,即对象实现者来负责。共享数据的类型和大小多种多样,但大多是以全局变量、C++ 类中的静态成员变量和函数中声明的静态变量的形式出现。即使是以下这样无害的语句也会在 STA 中出问题:
static int nCallCount = 0;
nCallCount++;
因为这个对象的所有实例都将共享一个 nCallCount 实例,编写这些语句的正确方式如下:
static int nCallCount = 0;
InterlockIncrement (&nCallCount);
注意:您可以使用临界区、互锁函数或您希望的任何方式,但不要忘了访问基于 STA 的对象共享的数据时要进行同步化!
谨慎启动用户
这里还有一个问题让许多 COM 开发人员都吃过苦头。去年春天,有一家公司向我紧急呼救,他们的开发人员使用 COM 构建了一个分布式应用程序,其中客户端进程运行在与远程服务器的 Singleton 对象相连接的网络工作站上。在测试过程中,他们遇到了一些非常奇怪的行为。在一种测试场景中,客户端对 CoCreateInstanceEx 的调用可使它们与 Singleton 对象正常连接。而在另一个场景中,对 CoCreateInstanceEx 的相同调用产生了多个对象实例和多个服务器进程,使客户端无法与同一个对象 实例连接,从而实际影响了应用程序。在这两个场景中,硬件和软件是完全相同的。
此问题似乎与安全有关。当处理远程激活请求的 COM 服务控制管理器 (SCM) 在另一台机器上启动一个进程时,它会为该进程分配一个标识。除非另外指定,它选择的标识就是启动用户的标识。换句话说,分配给服务器进程的标识与启动它的客户端进程的标识相同。在这种情况下,如果 Bob 登录机器 A,并使用 CoCreateInstanceEx 连接机器 B 上的 Singleton 对象,而 Alice 也在机器 C 上如法炮制,就会启动两个不同的服务器进程(至少在两台不同的 WinStation 上),实际上使客户端无法再用 Singleton 语义与共享的对象实例连接。
两个测试场景之所以会产生大相径庭的结果,其原因就是在一个场景(那个可以工作的场景)中,所有测试人员都使用只为测试而设置的一个特殊帐户以同一个人的身份登录。而在另一个场景中,测试人员都使用他们的普通用户帐户登录。当两个或更多的客户端进程具有相同标识时,它们可以成功连接到配置为假定启动用户标识的服务器进程。但是,如果客户端有不同的标识,SCM 会使用多个服务器进程(每个唯一客户端标识一个)分隔分配给不同对象实例的标识。
图 1 DCOMCNFG 中的用户帐户
找到问题以后,解决起来就很简单了:配置 COM 服务器,让其使用特定的用户帐户而不是假定启动用户的标识。完成这一任务的一种方式是在服务器机器上运行 DCOMCNFG(Microsoft 的 DCOM 配置工具),并将“launching user ”更改为“This user”(请参见图 1)。如果您喜欢通过编程方式进行更改(可能从安装程序着手),请在主机注册表的 HKEY_CLASSES_ROOT\AppID 部分的 COM 服务器项中添加 RunAs 值(请参见图 2)。
图 2 添加 RunAs 值到注册表中
您还需要使用 LsaStorePrivateData 将 RunAs 帐户的密码存储为 LSA 密钥,并使用 LsaAddAccountRights 确保帐户拥有“Logon as batch job”的权限。(有关具体操作的示例,请参见 Platform SDK 中的 DCOMPERM 示例。请特别注意名为 SetRunAsPassword 和 SetAccountRights 的函数。)
使用线程或异步调用来避免 DCOM 超时设定太长
总是有人问我当 DCOM 无法完成远程实例化请求或方法调用时出现的超时设定太长的问题。典型的场景如下:客户端调用 CoCreateInstanceEx 来实例化远程机器上的一个对象,但是这台机器临时离线了。在 Windows NT 4.0 上,激活请求不会立即失败,DCOM 可能会花上一分钟或更长时间来返回失败的 HRESULT。DCOM 还可能花费很长时间,使指向已不再存在或其主机已离线的远程对象的方法调用失败。如果可能,开发人员应该如何避免这些较长的超时设定呢?
要回答这个问题,几句话是讲不清楚的。DCOM 高度依赖于基础网络协议和 RPC 子系统。并没有什么神奇的设置可让您限制 DCOM 超时设定的持续时间。但是,我经常使用两种技巧来避免较长超时设定的负作用。
在 Windows 2000 中,当调用在 COM 信道中挂起时,您可以使用异步方法调用来释放调用线程。(有关异步方法调用的介绍,请参 MSDN Magazine 2000 年 4 月刊的“Windows 2000: Asynchronous Method Calls Eliminate the Wait for COM Clients and Servers Alike”。如果异步调用在合理时间内没有返回,您可以通过调用用于初始化调用的调用对象上的 ICancelMethodCalls::Cancel 来取消它。
Windows NT 4.0 不支持异步方法调用,甚至在 Windows 2000 中也不支持异步激活请求。怎么解决呢?从背景线程调用远程对象(或是实例化该对象的请求)。使主线程在事件对象上阻塞,并指定超时设定值以反映您愿意等待的时间长度。当调用返回时,让背景线程来设置事件。假设主线程使用 WaitForSingleObject 阻塞,当 WaitForSingleObject 返回时,返回值可以告诉您,返回是因为方法调用或激活请求返回,还是因为您在 WaitForSingleObject 调用中指定的超时设定到期。您不能在 Windows NT 4.0 中取消挂起调用,但是至少主线程可以自由地执行自己的任务。
下面的代码演示了基于 Windows NT 4.0 的客户端如何才能从背景线程调用对象。
////////////////////////////////////////////////////// // Placing a Method Call from a Background Thread ///////////////////////////////////////////////////// HANDLE g_hEvent; IStream* g_pStream; // Thread A g_hEvent = CreateEvent (NULL, FALSE, FALSE, NULL); CoMarshalInterThreadInterfaceInStream (IID_IFoo, pFoo, &g_pStream); DWORD dwThreadID; CreateThread (NULL, 0, ThreadFunc, NULL, 0, &dwThreadID); DWORD dw = WaitForSingleObject (g_hEvent, 5000); if (dw == WAIT_TIMEOUT) { // Call timed out } else { // Call completed } ... // Thread B IFoo* pFoo; CoGetInterfaceAndReleaseStream (g_pStream, IID_IFoo, (void**) &pFoo); pFoo->Bar (); // Make the call! SetEvent (g_hEvent); CloseHandle (g_hEvent); |
在此示例中,线程 A 封送了一个 IFoo 接口指针,并启动线程 B。线程 B 取消封送了该接口指针,并调用 IFoo::Bar。无论调用返回所花费的时间有多长,线程 A 都不会阻塞超过 5 秒钟,因为它在 WaitForSingleObject 的第二个参数中传递的是 5,000 (单位为微秒)。这并不是太好的办法,但是如果“无论在线路的另一端发生什么情况,线程 A 都不会挂起”这一点很重要的话,忍受这种麻烦也算值得。
共享对象并不容易
从我收到的邮件和在会议上被问到的问题判断,困扰许多 COM 程序员的一个问题是如何将两个或更多的客户端与一个对象实例连接。要回答这个问题,写出长篇大论(或是一本小册子)都很容易,但其实只要说明与现有对象的连接既不容易也不自动化,就足够了。COM 提供了大量创建对象的方式,包括很受欢迎的 CoCreateInstance(Ex) 函数。但是 COM 缺乏一种通用的命名服务,允许使用名称或 GUID 来标识对象实例。而且它没有提供内置的方式来创建对象,然后将它标识为调用的目标以检索接口指针。
这是不是意味着将多个客户端与单一对象实例连接就不可能了呢?当然不是。实现这一点有五种方式。在这些资源链接中,您可以找到更多信息甚至是示例代码,来指导您的操作。请注意,这些技术从一般意义上讲不能互换;通常,环境因素会决定哪种方式(如果有)适用于手边的任务: Singleton 对象 Singleton 对象就是只实例化一次的对象。可能会有 10 个客户端调用 CoCreateInstance 来“创建”Singleton 对象,但实际上,它们都是接收指向同一对象的接口指针。ATL COM 类可通过在其类的声明中添加 DECLARE_CLASSFACTORY_SINGLETON 语句,来转换为 Singleton。
文件名字对象 如果一个对象实现了 IpersistFile,并在运行中对象表 (ROT) 中使用文件名字对象(它封装了传递给对象的 IPersistFile::Load 方法的文件名称)注册了自己,那么客户端就可以使用文件名字对象连接对象的现有实例了。实际上,文件名字对象允许使用文件名称来命名对象实例,对象可在这些文件名称中存储它们的持久性数据。它们甚至能够跨机器工作。
CoMarshalInterface 和 CoUnmarshalInterface 保存接口指针的 COM 客户端可以与其他客户端共享这些接口指针,只要它们愿意封送指针。COM 为愿意将接口指针封送给同一进程中其他线程的线程提供了优化(请参见教训 2),但是如果客户端线程属于其他进程,CoMarshalInterface 和 CoUnmarshalInterface 就是实现接口共享的关键途径了。
发表评论
-
如何使用BHO定制你的Internet Explorer浏览器
2009-08-20 11:26 2165如何使用BHO定制你的In ... -
定制IE浏览器的尖兵利器 - BHO
2009-08-19 18:28 2867作者:peterzb(个人 ... -
ATL入门:利用ATL编写简单的COM组件
2009-08-19 18:26 17764使用ATL编写一个简单的COM服务器文/赵湘宁 ... -
用 ATL ActiveX 绘制任意平面函数的曲线
2009-08-19 18:23 1651用 ATL ActiveX 绘制任意 ... -
COM多线程原理与应用
2009-08-19 18:14 2701COM多线程原理与应用 目录: COM多线程原 ... -
com技术简介
2009-07-28 11:32 2730一、COM是一个更好的C++1、COM 是什么Don Box ... -
COM高手总结的八个经验和教训之一
2009-07-28 11:31 1790在日常工作中,我看到 ... -
COM基础知识
2009-07-28 11:30 1919(1) COM组件实际上是一个C++类,而接口都是纯虚类。 ... -
COM原理及应用----概述
2009-07-28 11:29 18601、组件设计的原始目的 跨平台、跨网络、积木式搭建程序 2、组 ... -
COM原理及应用----COM对象和接口
2009-07-28 11:29 44231、COM对象的理解 ... -
COM原理与应用----COM的实现
2009-07-28 11:28 29691、COM的实现与操作系 ... -
COM原理及应用----COM特性
2009-07-28 11:28 21021、面向对象系统的三个最基本的特性 封装性、多态性 ... -
COM原理及应用----用Visual C++开发COM应用
2009-07-28 11:27 36511、MFC和ATL 对于COM应用的开发来说,建 ... -
COM原理及应用----可连接对象
2009-07-28 11:26 23561、COM的高级特性 COM规范中有一些高 ... -
COM原理及应用---- 结构化存储
2009-07-28 11:25 24501、结构化存储 ... -
COM原理及应用----命名和绑定技术
2009-07-28 11:25 32921、COM对象的创建方法 客户程序可以通过 ... -
COM原理及应用----统一数据传输
2009-07-28 11:24 24621、概述 COM提供了应用之间数据交换的标 ... -
COM原理及应用----分布式COM(DCOM)
2009-07-28 11:24 30921、DCOM COM的 ... -
COM原理及应用----自动化(Automation)对象
2009-07-28 11:23 23031、自动化技术 自动化技术既以前提到的OL ... -
COM原理及应用----ActiveX控件
2009-07-28 11:22 41151、ActiveX控制 ActiveX控制 ...
相关推荐
软件测试工程师个人工作总结范文中,作者从自己的经验出发,总结了软件测试工作中的一些重要知识点和经验教训。 首先,作者强调了尝试、努力和坚持的重要性,只有通过尝试、努力和坚持,才能获得成功的收获。作者...
这些特征是SEO高手们在实践过程中总结出的宝贵经验,它们构成了提升网站排名和优化用户体验的基石。 首先,**专注**是SEO高手们必须具备的首要特征。真正的SEO专家不仅对SEO行业保持高度的热情,而且愿意投入大量的...
在报告中,作者也分享了自己在实习期间的一些习惯和经验,如遇到问题先自己思索、不怕问高手帮忙、解决问题后要追根究底等。这些习惯和经验对软件工程专业的学生非常重要,可以帮助他们更好地完成实习和项目。 此外...
本文将总结ERP沙盘模拟大赛的经验和教训,帮助未来的选手更好地准备和应对比赛。 首先,企业资源规划是企业管理的核心,包括业务决策、运营管理、财务管理等方面。在ERP沙盘模拟大赛中,我们需要从战略的角度来看待...
总结来说,《编程高手启示录》不仅是一本关于提升编程技能的书,更是一本关于如何成为顶尖程序员的态度和思维方式的指导书。它教会我们如何编写高质量的代码,如何提升个人的编程效率,并且传递了编程高手在解决实际...
- 团队中通常包含数学建模专家、编程高手和论文撰写能手。 #### 角色分工: - 明确每个成员的任务和责任,比如谁负责模型构建、谁负责编程实现等。 - 保持良好的沟通机制,确保团队内部信息流通顺畅。 ### 5. 时间...
在软件测试领域,成为高手并非一蹴而就,而是需要不断的学习和实践,以及对经验教训的总结和反思。软件测试是一个知识和技术快速更新的领域,测试人员需要时刻保持学习的态度,掌握最新的测试工具和技术。此外,分享...
根据提供的信息,我们可以总结并深入探讨以下几个IT领域的知识点: ### Windows 开发 Windows 平台下的软件开发一直是程序员关注的重点之一。随着技术的发展,Windows 开发不仅限于传统的桌面应用程序,还包括了...
单片机课程设计总结 在这篇课程设计总结中,我们可以看到作者对单片机课程设计的总结和心得体会。作者首先提到,理论知识如果不与...同时,作者也强调了团队协作和学习基础的重要性,为我们提供了有价值的经验和教训。
而“传播知识”模式则鼓励开发者分享自己的知识和经验,通过教学来加深自己的理解和技能。 这本书不仅仅是一本技术指南,更是一份成长和自我提升的规划。它鼓励开发者不断寻找新的学习途径,勇于接受新的挑战,并且...
在建筑工程领域,结算是一项至关重要的工作,涉及到成本控制和经济效益的...面对新的挑战,见习期的宝贵经验和教训将成为应对未来工作的重要支撑。在这个过程中,持续学习和总结是不断提升自我,实现专业成长的关键。
案例分析是指通过对实际项目的分析和研究,总结出项目管理的经验和教训,并将其应用于未来的项目管理中。案例分析可以帮助项目经理和团队成员更好地理解项目管理的原理和方法,从而提高项目的成功率。 3. 项目管理...
- **一些心得体会**(第9.9节):总结类构造、析构过程中的经验和教训。 #### 九、类的继承与组合 **10. 类的继承与组合**(第10章) - **继承**(第10.1节):解释类的继承机制及其优点。 - **组合**(第10.2节...
2021年,我在幼儿园德育工作中总结出的经验教训,对于IT行业同样具有借鉴意义。 首先,师德建设在幼儿园中至关重要,老师的一言一行对幼儿的成长影响深远。同理,在IT行业中,领导者的道德品质和职业行为对团队同样...
#### 八、总结 数学建模是一项集思广益、团队合作的活动。通过精心准备、合理分工和不断学习,可以有效地提升团队的整体实力。希望以上经验和建议能够帮助更多的人在数学建模竞赛中取得优异的成绩。 ### 结语 ...
自1989年发布以来,DB2一直是数据库领域的领军者之一,历经25年的发展历程,它在数据管理领域积累了丰富的经验和技术创新。 - **DB2在中国的影响**: 在中国,DB2同样拥有广泛的用户基础和技术支持社区。《DB2中国》...
松掌握任何复杂主题提示:"告诉我关于[x主题]的10个初学者重要问题。创建一个包含问题示例的表格。...通过实践和应用这些提示,你将能够显著提高学习效率,从而在个人发展和专业领域中脱颖而出,成为真正的学习高手。
综上所述,大学生在营销实践中所积累的经验教训对于任何阶段的销售人员都具有不可估量的价值。掌握销售技巧、注重服务品质、建立良好的客户关系以及保持积极的心态和不断自我提升,是每一位销售人员都应该遵循的原则...
不过,在事情结束之后,收集反馈、总结经验教训则变得尤为重要。通过这种事后的评估,我们可以帮助团队成员认识到哪些做法是有效的,哪些方面还有待改进,从而为下一次任务做好更充分的准备。 在处理已经发生过的...