DCOM体系结构(一)
技术白皮书---Microsoft
摘要:
这篇文章主要介绍了DCOM的内部原理,对象的TCP/IP协议实现。这篇文章适合程序开发人员阅读,尤其适合想开发基于Internet、Intranet以及更深层次上的最新应用程序的开发人员。在一些方面我们假定读者已经熟悉COM的基本概念,尽管有些概念在讨论分布式程序设计时还会涉及到。
概述
Microsoft Distributed Component Object Model(DCOM)是Component Object Model(COM)的扩展,它支持不同的两台机器上的组件间的通信,而且不论它们是运行在局域网、广域网、还是Internet上。借助DCOM你的应用程序将能够任意进行空间分布。
由于DCOM是COM这个组件技术的无缝升级,所以你能够从你现有的有关COM得知识中获益,你的以前在COM中开发的应用程序、组件、工具都可以移入分布式的环境中。DCOM将为你屏蔽底层网络协议的细节,你只需要集中精力于你的应用。
谁适合阅读这篇文章
这篇文章适合程序开发人员阅读,尤其适合想开发基于Internet、Intranet以及更深层次上的最新应用程序的开发人员。在一些方面我们假定读者已经熟悉COM的基本概念,尽管有些概念在讨论分布式程序设计时还会涉及到。
怎样得到这篇文章的主要内容
这篇文章是有关COM和DCOM技术的系列资料的一部分。你可以从头到尾进行阅读,也可以将它作为技术参考。在这篇文章的最后一章,介绍了这一系列文章中的其他文章。
如果你还不清楚DCOM是什么,“DCOM Technical Overview”这篇文章将对你有用。如果你想知道怎样应用DCOM解决特定的问题,“Solution in Action”这篇文章将会对你有所帮助。
哪里能够得到DCOM
DCOM目前和Windows NT v4.0,Windows 2000操作系统一起发行,DCOM的Windows 9X版本可以从微软站点下载。
COM体系结构
这一章将给你一个DCOM技术内部、外部结构的一个概述,你将会看到 DCOM怎样实现了对于客户端来说的简单分布式计算,而没有再灵活性、可升级性、健壮性等方面损失性能。
DCOM处于你的应用程序组件中间,它提供了将组件透明连接起来的能力,如下图说明了DCOM的恰当的位置:
定位对象
COM技术的核心内容是怎样建立组件之间的通信以及怎样创建组件的实例。这些技术通常被归为“激活技术”(Activation mechanisms)下面讨论一下它们怎样工作。
在本地和远端创建一个新对象
分布系统中的一个基础就是创建对象。在COM中对象类以GUID进行命名。当GUID用于对象类的命名时被称作Class IDs,这些Class IDs仅仅是一些128位的整数,它提供了对象类的无重名命名空间。如果想创建一个新的对象,可以调用COM库提供的下列函数中的任意一个:
CoCreateIntance(Ex)
创建一个未初始化的对象类的接口指针
CoGetInstanceFromFile
创建一个对象类的新的实例,并用文件进行初始化
CoGetInstanceFromIStorage
创建一个对象类的新的实例,并用Storage进行初始化
CoGetClassObject
返回一个对象类的类工厂接口指针
CoGetClassObjectFromURL
返回一个对象类的类工厂指针,如果没有指定类,函数将选择对于指定MIME类型来说最恰当的类。如果目标对象已经安装在系统上,就返回它的实例,如果没有,则从指定的URL上下载并安装。
COM库函数查找系统注册表中的信息,创建对象的实例,并返回一个借口指针给调用者。
DCOM加强了COM库中创建对象的技术,它支持创建另外一台机器上的对象类。要创建其他机器上的对象类, COM库函数需要该机器的机器名。如果知道了目标机器的机器名以及CLSID,COM苦寒数就调用SCM(Service Control Manager) 与目标机器上的SCM连接。
DCOM提供了两种用于客户端在创建远端对象类时得到目标机器名的技术:
1、将服务器名作为固定的配置信息存储在有关对象类的注册信息中。
2、在CoCreateInstanceEx、CoGetInstanceFromFile、CoGetInstanceFromStorage、CoGetClassObject中指定目标机器参数。
外部远端服务器配置
第一种方法,作为固定配置指出服务器名,对于提供位置透明性特别有效:用户端不需要知道组件是本地运行还是远端运行。当服务器名作为客户计算机上的组件配置信息存在时,客户端不需要特别维护和获得服务器的位置。客户端仅仅需要知道组件的CLSID,仅仅需要简单的调用CoCreateInstance函数,COM库将透明的在服务器上创建对象。甚至现有的在DCOM出现之前设计的COM组件也可以使用这种方法。
注意服务器不能够将使用这种方法创建对象的请求再一次转给另外一个服务器,例如:客户机X适用服务器名制定在服务器Y上创建对象A,并且服务器Y上的对象A的服务器名为服务器Z,这时候客户机X上的对象创建将在服务器Y上完成。
对于大多数应用程序来说,对每一个组件由一个外部的创建服务器设置就已经足够了,这样将使得客户程序不需要维护创建该组件的服务器的信息。如果服务器更改了,只需要更改配置信息,而客户程序不需要改变。
组件的远程服务器配置存储在注册表中的HKEY_CLASS_ROOT(HKCR)下的一个新健下:[HKEY_CLASSES_ROOT\APPID\{appid-guid}]
“RemoteServerName”=<Name>
相应的,ClassID入口处也要增加一个新健AppID
[HKEY_CLASSES_ROOT\CLSID\<clsid-guid>]
“AppId”=“Appid-guid”
APPID的概念将作为COM的安全技术的一个部分在本文的后面的章节中作详细介绍。APPID实际上提供了一个供多个CLSID共享的过程。所有的共享这个APPID的组件具有相同的安全性设置。
APPID的概念的提出能够避免注册表健的重复,所有的在相同的服务器上运行的组件可以指向同一个APPID,共享同一个远程服务器名。
例如:拍卖服务
一个拍卖服务实现为一个DCOM组件,所有的拍卖活动和拍卖的管理都要通过改组件,买者的竞价将实时的通过连接点到达所有的客户。
在这个应用中,客户不需要了解他们连接了哪一台服务器,他们只知道连接拍卖服务器。服务器的DNS名称已经写入了客户计算机的注册表中,COM库函数将自动的搜索服务器。以下是客户端的注册表结构:
[HKEY_CLASSES_ROOT\CLSID\<CLSID_AuctionServer>]
“AppID”=“{<APPID_AuctionServer>}”
[HKEY_CLASSES_ROOT\APPID\{<APPID_AuctionServer>}]
“RemoteServerName”=“auctions.r.us.com”
客户端程序将用下列代码建立拍卖对象:
Iauction* pAuction=NULL;
HRESULT hr=CoCreateInstance(
CLSID_AuctionService,
NULL,
CLSCTX_SERVER,
(void**)&pAuction);
if(SUCCESSED(hr))
{
pAuction->PlaceBid(1324,1000000.0);
pAuction->Release();
}
微软提供了设置DCOM的工具DCOMCNFG.EXE,用来设置远程服务器,而不用手动修改注册表。
从Windows2000开始,COM提供了一个专门用于COM类的集中存储。所有的有关COM组件的信息将有选择的存入域控制器的活动目录中。就像登录服务在域控制器上存储用户信息。COM库将透明的从活动目录中获得组件配置信息包括远程服务器信息。改变会活动目录中的组件配置信息,将影响所有的连接到该活动目录的客户端。
编程控制远程服务器
一些应用程序需要在运行时直接控制客户连接哪一台服务器。这样的例子由:聊天应用、多人游戏、对特定机器的远程管理等。
对于这样的应用程序。COM允许将远程服务器的名字作为参数直接用于CoCreateInstanceEx、CoGetInstanceFromFile、CoGetInstanceFormStorage、CoGetClassObject中。客户端程序开发者将完全控制在远程服务器上建立对象。
例如:远程管理
一个备份服务被实现为一个COM对象,用CLSID_MyBackupService来表示。管理者是这个对象的用户,能够用对象来配置备份服务、控制备份过程和恢复过程。使用DCOM,这个备份服务实现起来将非常简单。
MULTI_QI mqi[2]=
{&IDD_IbackupAdmin,NULL,0}
{&IDD_IbackupConfig,NULL,0}
CONSERVERINFO srvinfo={0,L”MyCentralFileServer.cia.gov”,NULL,0}
HRESULT hr=CoCreateInstanceEx(
CLSID_MyBackupService,
NULL,
CLSCTX_SERVER,
&srvinfo,
sizeof(mqi)/sizeof(mqi[0]),
&mqi);
if(SUCCEESED(mqi[1].hr))
{
LPWSTR pStatus=NULL;
IbackupConfig* pBackConfig=mqi[1].pltf;
Hr=pBackupConfig->GetCurrentBackupStatus(&pStatus);
PBackupConfig->Reslease();
}
远程运行进程内组件:代理进程
要在服务器端运行一个进程内的组件(也就是一个以DLL实现的组件),需要在服务器端运行一个代理进程。要能够远端运行,则代理进程需要具备以下的特点:
l 进程内组件的运行时错误被代理进程隔离。
l 一个代理进程能够同时为多个客户程序服务。
l 客户程序在访问服务器提供的服务时,代理进程中的一些不安全代码应该能够保证客户进程的安全。
l 代理进程应该为进程内组件提供安全保证
Windows NT v4.0Service Pack 2和DCOM的Windows95版本提供了一个可供开发者参考的样本代理进程。所提供的样本代理进程是一个混合线程模式的虚拟COM服务器。当很多的DLL组件被加载进代理进程时,这个代理进程保证所有的DLL组件将以它们所注册的线程模式运行。如果DLL组件支持单线程和多线程模式,COM将选择多线程模式进行初始化。样板代理进程被实现为使COM可以控制DLL组件的退出以及该进程终止。
一个进程内组件将在以下的情况时,可以被代理进程加载:
l 在该组件的CLSID注册健下有一个APPID健,而且被设置了适当的代理进程。
l 在创建改组件时,调用中应该用到CLSCTX_LOCAL_SERVER属性,而且CLSID健中没有设置LocalServer32、LocalServer、LocalService等子健。
l CLSID包含InProcServer32子健。
l 在InProcServer32中设置的DLL文件存在。
l 在APPID健下设置DllSurrogate子健。
如果设置了LocalServer32、LocalServer、LocalService等子健被设置,就会有一个EXE文件实现的组件,而且该EXE文件总是被优先于代理进程的次序调用而去加载DLL组件。
你必须给DllSurrogate一个具体的代理进程文件名。如果想使用系统提供的代理进程,则将DllSurrogate设置为NULL。要是一是用自己的代理进程则将DllSurrogate设置为代理进程文件的全路径名。如果要在服务器端进行代理,则在客户端之需要设置RemoteServerName,不需要设置DllSurrogate,而在服务器端要设置DllSurrogate。
最好使用AppId健下的RunAs子健设置一个DLL组件单独运行在代理进程中,而服务于所有的连接客户。将RunAs设置“交互式用户”还是一个特定的用户,要根据安全性以及服务器端的一些要求决定。RunAs健保证了一个组件服务仅有一个实例运行在代理进程中,而服务于所有的客户。另一方面,如果希望每一个远程的客户都有一个组件实例,则不要设置RunAs健。
不同的组件服务如果具有相同的安全设置,并且有相同的APPID,则它们使用相同的代理进程。如果它们的安全设置不同,即使它们有着相同的APPID,它们仍然运行载不同的进程空间中。