DCOM揭秘之五

2008-04-09 04:02:23来源:互联网 阅读 ()

新老客户大回馈,云服务器低至5折

  理解ATL产生的代码

  我们服务器端DLL的源代码是由ATL产生的。对于许多人来说,可以完全不用了解ATL创建的代码。不过,对于一些喜欢寻根究底的人来说,这是不可以接受的。这里就介绍一下由ATL产生的代码。

  服务端的DLL代码由三种不同类型的文件组成

   首先,是传统的C 源文件和头文件。在开始时,所有这些代码是由ATL向导产生的

   Beep方法是通过使用“AddMethod”对话框加入的,它修改了MIDL接口的定义。MIDL的源代码是一个IDL文件--在这个例子中它是BeepServer.IDL。MIDL编译器将使用该文件来创建几个输出文件。这些文件负责了大部分实现服务器的工作。当我们为COM对象加入方法时,我们也将在IDL加入一些东西。

   第三组的源文件是自动产生的MIDL输出文件,是由MIDL编译器产生的。这些文件是源代码文件,不过由于它们是由MIDL编译器通过IDL源代码自动产生的,因此不能被向导或者开发者直接修改。你可以将它们称为“第二产生文件”--向导创建了一个IDL文件,而MIDL编译器由该IDL文件创建了源代码文件。由MIDL创建的文件包括有:

    BeepServer.RGS - 服务器的注册脚本


    BeepServer.h - 该文件包括了COM组件的定义

    BeepServer_i.c - COM组件的GUID结构

    Proxy/Stub files - 包括了"C"源代码、DLL定义,以及Proxy和Stub的makefile (.mk)

  ATL向导还创建了一个应用的“资源”。如果你查看项目的资源,你将会发现它处在“REGISTRY”下。该资源包含了BeepServer.RGS中定义的注册脚本。资源的名字是IDR_BEEPOBJ。

  我们将在以下的部分看一下这些不同的组件

  主C 模块

  我们在运行ATL COM Appwizard时,我们选择创建一个基于DLL的服务器,并且选择不使用MFC。向导的第一个选择屏幕决定了服务器的整体配置。

  AppWizard创建了一个标准的DLL模块。该类的标准DLL并没有一个WinMain应用循环,不过它有一个DllMain函数用作在载入时初始化该DLL:


CComModule _Module;

BEGIN_OBJECT_MAP(ObjectMap)
OBJECT_ENTRY(CLSID_BeepObj, CBeepObj)
END_OBJECT_MAP()

////////////////////////////////////////
// DLL Entry Point

extern "C"
BOOL WINAPI DllMain(HINSTANCE hInstance,
DWORD dwReason, LPVOID /*lpReserved*/)
{
if (dwReason == DLL_PROCESS_ATTACH)
{
_Module.Init(ObjectMap, hInstance);
DisableThreadLibraryCalls(hInstance);
}
else if (dwReason == DLL_PROCESS_DETACH)
_Module.Term();
return TRUE; // ok
}


  DllMain函数的真正工作是检查有没有一个客户连上该DLL,然后会做一些初始化的工作。一眼看去,并没有一个明显的指示这是一个COM应用。

  我们新服务器的COM部分被封装到ATL类CComModule中。CComModule是ATL服务器的基类。它包含了所有用作登记和运行服务器、开始和维护COM对象的COM逻辑。CComModule被定义在头文件“altbase.h”中。该代码用以下的行声明一个全局的CComMoudule 对象:

   CComModule _Module;

  这个单一的对象包含了许多用作我们应用的COM服务器功能。它在程序执行开始时的创建和初始化设置了一连串的事件动作。

  ATL需要你的服务器命名它的全局CComModule对象“_Module”。使用你自己的类来覆盖CComModule是可以的,不过你不能改变它的名字。

  如果我们选择创建一个可执行的服务器,或者一个带MFC的DLL,代码将会是完全不同。这时还会有一个基于CComModule的全局对象,不过程序的入口将会是WinMain()。选择一个基于MFC的DLL将会创建一个基于CWinApp的主对象。

  对象映射

  CComModule通过在前面部分看到的对象映射连接到我们的COM对象(CBeepObj)。一个对象映射定义了该服务器控制的所有COM对象的一个数组。对象映射在代码中使用OBJECT_MAP宏定义。以下就是我们DLL的对象映射:


BEGIN_OBJECT_MAP(ObjectMap)
 OBJECT_ENTRY(CLSID_BeepObj, CBeepObj)
END_OBJECT_MAP()


  OBJECT_ENTRY宏通过一个C 类与对象的CLSID关联。一个服务器中包含有多于一个的COM对象是很常见的。这时,对于每个对象,都将会有一个OBJECT_ENTRY。

  输出文件

  我们这个进程内的DLL和大部分的DLL一样,都拥有一个输出文件。输出文件将被客户端用来连接到我们DLL中的外部函数。这些定义都放在BeepServer.def文件中:


; BeepServer.def : Declares the module parameters.

LIBRARY "BeepServer.DLL"

EXPORTS
DllCanUnloadNow @1 PRIVATE
DllGetClassObject @2 PRIVATE
DllRegisterServer @3 PRIVATE
DllUnregisterServer @4 PRIVATE


  重要的是要注意到那些没有被输出的。没有用户的方法,也没有“Beep”方法的输出。以上你只看到一个COM DLL应有的输出。

  查看BeepServer.CPP文件,我们可看到这4个函数的定义是由COM应用的类来处理的。以下就是DllRegisterServer的代码:


// DllRegisterServer - Adds entries to the system registry
STDAPI DllRegisterServer(void)
{
// registers object, typelib and all interfaces in typelib
return _Module.RegisterServer(TRUE);
}


  在这里,DLL仅调用了ATL的CComModule::RegisterServer()方法。CComModule实现服务器注册的方法是兼容进程内、本地和远程COM服务器的。其余4个被输出的DLL函数是完全相同的。真正的实现被隐藏在ATL的模板中。

  以上介绍的大部分代码是DLL的特定代码。只有在你选择创建一个基于DLL的服务器时,才会得到这个配置。主模块的代码都不是COM特定的。该主模块的体系完全是为了在一个DLL中传送COM对象,这部分的代码根据服务器类型的不同,有着明显的区别。服务器内的真正代码则是更为类似的。coclass和接口的实现是一样的,与你创建的服务器类型无关(DLL、EXE、服务器)。如果要将一个DLL服务器的coclass转换为使用一个基于EXE的服务器实现,你不需做多少的改变。

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:DCOM 揭秘之四

下一篇:对象组件技术COM 慨述