www.esb111.com bodog88 www.ysb88.com www.bwin88.com www.3555.com

118kj开奖现场 > www.118668.com >

COM编程入门 第二部门

来源:本站原创   更新时间:2019-08-10

 

  你每次实现组件对象类的时候,都要写一个旁类担任建立第一个组件对象类的实例。这个旁类就叫这个组件对象类的类工场(classctory),其独一目标是建立COM对象。之所以要一个类工场,是由于言语无关的来由。COM本身并不建立对象,由于它不是于言语的也不是于实现的。

  每一行都包含函数名和PRIVATE环节字。这个环节字的意义是:此函数是输出函数,但不包含正在输入库(importlib)中。也就是说客户端不克不及间接从代码中挪用这个函数,即便是链接了输入库也不可。这个环节字时必必要用的,不然链接器会犯错。

  这个工程是一个能运转的DLL办事器例子,对象由类工场建立,此DLL办事器正在CSimpleMsgBoxImpl组件对象类中实现了一个接口:ISimpleMsgBox。

  下面让我们来浏览一下这个过程。这里要涉及到几个不太熟悉的术语,但不消焦急,后面会对它们做细致会商。

  当建立新的COM对象时,构制器被挪用,它添加办事器的援用计数以连结这个办事器驻留内存。同时它还将对象的援用计数初始化为零。当这个COM对象被摧毁时,它削减办事器的援用计数。

  正在本文的第一部门中,我们见过CoCreateInstance()API,其感化是当客户端请求对象时,用它来建立对象。从客户端的立场看,它是一个黑盒子。只需用准确的参数挪用它即可获得一个COM对象。它并没有什么魔法,只是正在一个定义优良的过程中加载COM办事器,建立请求的COM对象并前往所要的指针。就这些。

  若是你编写多线程使用,可能会想到利用++&替代InterlockedIncrement()和InterlockedDecrement()的线程平安问题。++&用于单线程办事器很安全,由于即便客户端使用是多线程的并从分歧的线程中进行方式挪用,COM库城市按挨次进行办事器的方式挪用。也就是说,一旦一个方式挪用起头,所有其它试图挪用方式的线程都将堵塞,曲到第一个方式前往。COM库本身确保办事器一次不会被一个以上的线程闯入。

  我们曾经会商了一些DLL办事器的内部细节,接下来让我们回头看一看当客户端挪用CoCreateInstance()时是若何处置办事器的。

  术语“类工场”和“类对象”现实上是一回事。没有阿谁单词能切确描述类工场的感化和义,但恰是这个工场建立了COM对象,而不是COM类所为。将“类工场”理解成“对象工场”可能会更有帮于理解(现实上MFC就是如许理解的它的类工场实现就叫做COleObjectFactory)。但“类工场”是正式术语,所以本文也如许用。

  InProcServer32键包含两个串:这两个串的缺省值是办事器DLL的全径和线程模子值(ThreadingModel)。线程模子超出了本文所涉及的范畴,我们先接管这个概念,这里我们指的是单线程办事器,用的模式为Apartment(即单线程公寓)。

  本文我们将会商最简单的一种COM办事器,历程内办事器(in-process)。“历程内”意义是办事器被加载到客户端法式的历程空间。历程内办事器都是DLLs,而且取客户端法式同正在一台计较机上。

  删除关于COM办事器的注册表入口。其代码都是对注册表的处置,所以正在此不必赘言,只是列出DllRegisterServer()建立的注册表入口:

  前面会商过QI()的实现,但仍是有需要再看一看类工场的QI(),由于它是一个很现实的例子,此中COM对象实现的不但是IUnknown。起首辈行的是对ppv缓冲的无效性查抄以及初始化。

  这是历程内办事器运转的最小需求。正在注册表的HKEY_CLASSES_ROOT\CLSID键值下必需建立一个键值,用办事器的GUID做为键名字,这个键值必需包含两个键值清单,一是办事器的,而是办事器的线程模子。COM库对DllGetClassObject()函数进行挪用是正在CoCreateInstance()API中完成的。

  回顾看一看客户端的COM,它是若何以本人于言语的体例建立和COM对象。客户端挪用CoCreateInstance()建立新的COM对象。现正在我们来看看它正在办事器端是若何工做的。

  要建立新的COM对象援用,就必需挪用这个函数通知COM对象这个新援用成立。正在AddRef()挪用中的强制转换IUnknown*看起来仿佛多余,可是正在QI()中初始化的*ppv有可能不是IUnknown*类型,所以最好是养成习惯对之进行转换。。

  若是你想正在办事器代码中设置断点,有两种方式:第一种是将办事器工程(MsgBoxSvr)设置为勾当工程,然后起头调试。MSVC将问你调试会话要运转的可施行法式。输入客户端测试法式的全径,你必需事先成立好。第二种方式是将客户端工程(TestClient)设置为勾当工程,设置装备摆设工程的隶属(dependencies)属性,以便办事器工程隶属于客户端工程。如许若是你改变了办事器的代码,那么正在编译客户端工程时会从动从头编译办事器工程代码。最初还要做的是当你起头调试客户端时必需告诉MSVC加载办事器符号(symbols)。

  COM办事器必需正在Windows注册表确注册当前才能一般工做。若是你看一下注册表中的HKEY_CLASSES_ROOT\CLSID键,就会发觉大把大把子键,它们就是正在这个计较机上注册的COM办事器。当某个COM办事器注册后(凡是是用DllRegisterServer()进行注册),就会以尺度的注册表格局正在CLSID键下建立一个键,它名字为办事器的GUID。下面是一个如许的例子:

  我们能够把本人假设成一个所建立的COM对象的客户端,挪用AddRef()进行一次援用计数(COUNT=1)。然后挪用QI()。若是QI()挪用成功,它将再一次用AddRef()进行援用计数(COUNT=2)。若是QI()挪用失败。援用计数将连结为本来的值(COUNT=1)。

  COM代码中有些宏躲藏了实现细节,并答应正在C和C++客户端利用不异的声明。本文中没有利用宏,但正在例子代

  正在GUID键的子键中还能够存储其它消息。需要建立什么子键依赖于COM办事器的类型以及COM办事器的利用方式。对于本文例子中这个简单的历程内办事器,我们值需要一个子键:InProcServer32。

  这个键的默认值是人可值此外组件对象类名,利用VC所带的OLE/COM对象浏览器能够察看到它们。

  当COM库挪用DllGetClassObject()时,它传送客户端请求的CLSID。办事器担任为所请求的CLSID建立者各类工场并将它前往。类工场本身就是一个组件对象类,而且实现IClassFactory接口。若是DllGetClassObject()挪用成功,它前往一个IClassFactory指针给COM库,然后COM库用IClassFactory接口方式建立客户端所请求的COM对象实例。

  这里的两个参数:riid和ppv雷同QI()的参数。不外正在这个函数中,riid指的是COM库所请求的类工场接口的IID。凡是就是IID_IClassFactory。

  我们的新接口是ISimpleMsgBox。所有的接口多必需从IUnknown派生。这个接口只要一个方式:DoSimpleMsgBox()。留意它前往尺度类型HRESULT。所有的方式都该当前往HRESULT类型,而且所有前往到挪用者的其它数据都该当通过指针参数操做。

  别的,只输出准确的函数是不敷的还必需遵照COM规范,如许COM库和客户端法式才能利用办事器。

  现正在该当大白了为什么正在客户端使用法式确挪用AddRef()和Release()是如斯主要!若是正在这了做得不合错误,你利用的对象会被很快摧毁,如许的话正在整个办事器中内存会很快溢出导致使用法式下次存取办事器代码时解体。

  本文为方才接触COM的法式员供给编程指南,注释COM办事器黑幕以及若何用C++编写本人的接口。

  一个利用办事器的客户端举例申明一个简单的客户端使用法式,用它来测试COM办事器。

  构制函数、析构函数和IUnknown方式都和前面例子中的一样,分歧的只要IClassFactory的方式,LockServer(),看起来相当更简单:

  办事器通过简单的援用计数来确定它能否能被卸载。下面是DllCanUnloadNow()的实现:

  当某个客户端想要建立一个COM对象时,COM库就从COM办事器请求类工场。然后类工场建立COM对象并将它前往客户端。它们的通信机制由函数DllGetClassObject()来供给。

  第一个参数pUnkOuter只用于聚合的新对象,指向“外部的”COM对象,也就是说,这个“外部”对象将包含此新对象。对象的聚合超出了本文的会商范畴,本文的例子对象也不支撑聚合。

  QueryIntece()简称QI(),由客户端法式挪用这个函数从COM对象请求分歧的接口。我们正在例子代码中由于只实现一个接口,QI()会很容易利用。QI()有两个参数:一个是所请求的接口IID,一个是指针的缓冲大小,若是查询成功,QI()将接口指针地址存储正在这个缓冲指针中。

  端需要CSimpleMsgBoxImpl的GUID,不是它的定义。将GUID移到零丁的文件中,使客户规矩在存取GUID时不依赖

  有需要回忆一下IUnknown派生的每一个接口。由于IUnknown包含了两个COM对象的根基特征援用计数和接口查询。当你编写组件对象类时(coclass),还要写一个满脚本人需要的IUnknown实现。以实现IUnknown接口的组件对象类为例下面这个例子可能是你编写的最简单的一个组件对象类。我们将正在一个叫做CUnknownImpl的C++类中实现IUnknown。下面是这个类的声明:

  DLL办事器的一个异乎寻常的方面是节制它们被加载的时间。“尺度的”DLLs被动的而且是正在使用法式利用它们时被随机加载/或卸载。从手艺上讲,DLL办事器也是被动的,由于不管如何它们毕尽仍是DLL,但COM库供给了一种机制,它答应某个办事器号令COM卸载它。这是通过输出函数DllCanUnloadNow()实现的。这个函数的原型如下:

  就这么简单。代码中从头至尾都有TRACE语句,如许正在调试器中运转测试法式就能够看到办事器的每一个方式

  除了削减对象的援用计数外,若是没有别的的明白援用,Release()将摧毁对象。Release()也前往更新的援用计数。留意Release()的实现假设COM对象正在堆中建立。若是你正在全局粘上建立某个对象,当对象试图删除本人时就会出问题。

  正在QI()挪用之后,类工场对象就利用完了,因而要挪用Release()来它。若是QI()挪用失败,这个对象将删除(由于援用计数将为零),所以最终成果是一样的。

  rclsid是客户端所请求的组件对象类的CLSID。这个函数必需前往指定组件对象类的类工场。

  本文的例子代码正在一个WORKSPACE(工做间)文件中(SimpleComSvr.dsw)同时包含了办事器的源代码和测试办事器所用的客户端源代码。正在VC的IDE中能够同时加载它们进行处置。正在工做间的同级条理有两个工程都要用到的头文件,但每个工程都有本人的子目次。

  正如前面所说的,必需用.DEF文件来从办事器输出四个尺度的输出函数。下面是例子工程的.DEF文件:

  我们曾经完成了一个超等棒的COM办事器,若何利用它呢?我们的接口一个定制接口,也就是说它只能被C或C++客户端利用。(若是正在组件对象类中同时实现IDispatch接口,那我们几乎就能够正在任何客户端中VisualBasic,WindowsScriptingHost,Web页面,PerlScript等利用COM对象。相关这方面的内容我们留待别的的文章会商)。本文供给了一个利用ISimpleMsgBox的例子法式。这个法式基于用Win32使用法式领导成立的HelloWorld例子。文件菜单包含两个测试办事器的号令:

  继上一篇COM编程入门之后,本文将会商相关COM办事器的内容,注释编写本人的COM接口和COM办事器所需要的步调和学问,以及细致会商当COM库对COM办事器进行挪用时,COM办事器运转的内部机制。

  当客户使用法式挪用COMAPICoFreeUnusedLibraries()时,凡是出于其空闲处置期间,COM库遍历这个客户端使用已加载所有的DLL办事器并通过挪用它的DllCanUnloadNow()函数查询每一个办事器。另一方面,若是某个办事器确定它不再需要驻留内存,它能够前往S_OK让COM将它卸载。

  若是你读过上一篇文章。该当很熟悉COM客户端是怎样会事了。本文将会商COM的另一端COM办事器。内容包罗若何用C++编写一个简单的不涉及类库的COM办事器。深切到建立COM办事器的内部过程,毫无地研究那些库代码是充实理解COM办事器内部机制的最好方式。

  CreateInstance()是沉点。我们说过这个方式担任建立新的CSimpleMsgBoxImpl对象。让我们进一步切磋一下它的原型和参数:

栏目导航