DCOM揭秘

原创 2005年04月29日 16:06:00

http://www.pcdog.com/p/html/2004927/27920042820_1.htm

Distributed COM --跨网络工作

  DCOM代表的是“Distributed(分布式)”COM。在前面的部分中,我们已经讲解了运行在同一部计算机的COM客户和服务器。在这一部分,我们将讨论如何将它扩展到DCOM的领域和分布式计算。

  大多数的COM编程者仅使用本地的“进程内”服务器,作为DLL运行。DLL载入到客户程序的处理空间,因此很可靠和有效。我们打算使用一个基于 EXE的服务器。这意味着服务器和客户端作为分开的程序运行。这个想法很有意义,特别是考虑到两个程序运行在不同的计算机上。当然,它带来了新的难点。

  好消息是将COM转换为DCOM是简单的。坏消息是连接客户和服务器时可出现很多的错误。在这里,重点是帮助你避免这些问题。

  COM和DCOM的区别

  COM和DCOM应用之间的大部分区别都被开发者隐藏起来。客户和服务器端的程序可以一样地编写,而不管程序在哪里运行。这个概念被称为本地/远程透明。

  当然,分布和本地COM的内部工作是有着一些区别的。本地的通信可通过许多的方式来完成,包括简单的Windows信息,而连接到一个远程的计算机需要一个全新的对象层和网络传输。除了这些大的区别外,你的程序需要做的改动并不大。

  与所有的COM通信一样,只有在客户请求一个服务器的接口时才会开始。在DCOM中,客户端调用CoCreateInstanceEx(),传送服务器计算机的一个描述和请求一个类标识器(CLSID)和接口。该请求由服务控制管理器处理(Service Control Manager,SCM),它是Windows的一部分。SCM负责在服务器计算机上创建和激活COM对象。在DCOM中,SCM将尝试启动远程计算机上的服务器。


图1



  一旦创建了远程的COM服务器,所有的调用将通过proxy和stub对象配置。proxy和stub使用RPC(Remote Procedure Calls,远程过程调用)进行通信,RPC处理所有网络交互。在服务器端,stub对象负责配置,而客户端则由proxy负责。

  跨网络的数据传送由RPC负责。实际上,DCOM使用一个扩展类型的RPC,称为对象RPC(Object RPC)或者ORPC。RPC可以运行在多种不同的协议上,包括有TCP/IP,UDP,NetBEUI,NETBIOS和命名管道。标准的RPC协议是 UDP(用户数据报协议)。UDP是一个无连接的协议,看来与DCOM这种面向连接的系统配合并不是一个好主意。不过这并不是一个问题,DCOM自动负责管理连接。

  你也知道,分布式的COM是通过不同硬件、操作系统和软件组件这样一个复杂的交互完成的。你应该认识到:

   a)COM在后面做了很多工作;

   b)有许多地方可能出错

  在编写时,如果使用Windows95/98系统,仅可使用TCP/IP协议进行DCOM数据传输。这是一个有点讨厌的限制,即使有其它的网络协议,你仍然需要在所有的Windows系统上安装TCP/IP协议。

  服务器的改动不大

  作为一个程序(EXE)运行的服务器将能够在网络上工作。实际上,将一个服务器转为DCOM方式工作,要做的改动并不大。不过,你要为该服务器加入一些安全性,这需要作一些努力。为了简单,在这里我忽略了安全性。

  如果你以前使用的是进程内的服务器,你将需要作一些改变。进程内的服务器是一个DLL,它不可以跨网络载入。一个DLL载入到客户程序的地址空间中,它不可以通过远程连接工作。有一个称为surrogate的工作区,可以将DLL封装为一个可执行的程序,不过,更好的是将服务器转变为一个EXE,要将一个DLL转换为一个EXE,最简单的方法是重新使用ATL向导创建服务器,并且将代码由DLL传送到EXE中。

  在我们的例子中,我提供了RemoteServer.exe的源代码,它实现了一个基于EXE的简单DCOM服务器。如果你查看其中的代码,你将发现它是由向导产生的。不过,我在其中加入了两个方法--一个是得到服务器的名字,另一个是得到服务器的系统时间。(若需要源代码,请与我联系)

  在编译以上的例子后,我们需要将客户的EXE拷贝到客户计算机。要注意的是,由于它是一个用户自己的接口,因此你还需要一个proxy/stub DLL,并且在客户端和服务端计算机中注册该proxy/stub DLL。如果你使用一个带类库的自动服务器,你还需要将类库拷贝到客户计算机,并且登记它。

  你还可以使用OLEVIEW和DCOMCNFG来设置一个远程服务器

  实际上,你只需改变寄存器的设置就可以令一个服务器程序远程地运行。有两款微软的工具可做到这一点:OLEVIEW和DCOMCNFG。两款工具都可设置寄存器以让DCOM尝试找到远程计算机上的服务器。你可以在OLEVIEW的Activation标签下输入远程的计算机名字,这样它将在该计算机上被启动。

  虽然它可以在旧的非DCOM应用上可行,不过这是一个不太便利的解决方法。我打算还是集中在使用编程的方法来设置远程激活,只要你掌握了它,这种方法将是更灵活的。


客户端程序

  以下我们将讨论客户端程序的重要部分。

RemoteClient.cpp
// RemoteClient.cpp : Defines the entry point for the console application.
file://

#include "stdafx.h" // added _WIN32_DCOM
#include // get "cout"
#include // get _com_error
#include // get time_t

// extract definitions from server project
#include "../RemoteServer/RemoteServer.h"
#include "../RemoteServer/RemoteServer_i.c"

// forward reference for status display method
void ShowStatus( HRESULT hr );

int main(int argc, char* argv[])
{
 HRESULT hr; // COM error code
 IGetInfo *pI; // pointer to interface

 // Get the server name from user
 char name[32];
 cout << "Enter Server Name:" << endl;
 gets( name );
 _bstr_t Server = name;

 // remote server info
 COSERVERINFO cs;
 // Init structures to zero
 memset(&cs, 0, sizeof(cs));
 // Allocate the server name in the COSERVERINFO struct
 cs.pwszName = Server;

 // structure for CoCreateInstanceEx
 MULTI_QI qi[1];
 memset(qi, 0, sizeof(qi));

 // initialize COM
 hr = CoInitialize(0);
 ShowStatus( hr );

 // macro to check for success
 if (SUCCEEDED(hr))
  {
   // set a low level of security
   hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
   RPC_C_AUTHN_LEVEL_NONE,
   RPC_C_IMP_LEVEL_IMPERSONATE,NULL, EOAC_NONE, NULL);

   // init security
   ShowStatus( hr );
  }

 if (SUCCEEDED(hr))
  {
   // Fill the qi with a valid interface
   qi[0].pIID = &IID_IGetInfo;

   // get the interface pointer
   hr = CoCreateInstanceEx( CLSID_GetInfo, // clsid NULL,
      // outer unknown CLSCTX_SERVER,
      // server context &cs, // server info 1,
      // size of qi qi ); // MULTI_QI array
      ShowStatus( hr );
  }

 if (SUCCEEDED(hr))
  {
   BSTR bsName; // Basic style string

   // Extract the interface from the MULTI_QI strucure
   pI = (IGetInfo*)qi[0].pItf;

   // Call a method on the remote server
   hr = pI->GetComputerName( &bsName );
   ShowStatus( hr );

   // Convert name to a printable string
   _bstr_t bst( bsName );
   cout << "Server Name :" << bst << endl;

   // get time from remote computer
   time_t tt;
   hr = pI->GetTimeT(&tt );
   ShowStatus( hr );

   // display time_t as a string
   cout << "Server Time :" << ctime( &tt ) << endl;

   // Release the interface
   pI->Release();
  }

  // Close COM
  CoUninitialize();

  // Prompt user to continue
  cout << "Press ENTER to continue" << endl;
  getchar();

  return 0;
 }

 // Display detailed status information
 void ShowStatus( HRESULT hr )
  {
   if (SUCCEEDED(hr))
    {
     cout << "OK" << endl;
    }
   else
    {
     // construct a _com_error using the HRESULT
     _com_error e(hr);
     char temp[32];

     // convert to hexidecimal string and display
     sprintf( temp, "0x%x", hr );
     cout << "Error : " << temp << endl;

     // The hr as a decimal number
     cout << "Decimal : " << hr << endl;

     // show the 1st 16 bits (SCODE)
     cout << "SCODE : " << HRESULT_CODE( hr ) << endl;
     // Show facility code as a decimal number
     cout << "Facility: " << HRESULT_FACILITY( hr ) << endl;
     // Show the severity bit
     cout << "Severity: " << HRESULT_SEVERITY( hr ) << endl;
     // Use the _com_error object to format a message string. This is
     // Much easier then using ::FormatMessage
     cout << "Message : " << e.ErrorMessage() << endl;
    }
  }


             ************源代码 ***********

为简单的客户加入DCOM

  为了激活我们的DCOM服务器,我们使用了基本的COM客户外壳并且加入了一些额外的方法,以便作DCOM测试。一个明显的改动是我们指定了服务器计算机的名字。以下就是我们必须加入到客户端的东西:

  。指定服务器计算机名字的方法。它将载入到COSERVERINFO结构体

  。调用CoCreateInstanceEx()代替CoCreateInstance()。这将包括有一些不同的参数和一个称为MULTI_QI的结构体

  。在任何的COM编程中,你首先要做的第一件事情是调用CoInitialize。我们将使用默认的线程模式,也就是独立线程

  // initialize COM

  hr = CoInitialize(0);

  通过COSERVERINFO指定服务器

  进行远程DCOM连接时,你必须指定服务器计算机的名字。计算机的名字可以是一个标准的UNC计算机名字或者是一个TCP/IP地址。我们将要求用户输入计算机的名字。我们的例子是一个控制台应用,因此我们将使用一个简单的输入流(include )来得到用户的输入并显示信息。

   // Get the server name from user
  char name[32];
  cout << "Enter Server Name:" << endl;
  gets( name );


  该服务器的名字将会被载入到一个COSERVERINFO结构体中。这个结构体需要一个指向宽字符(wide-character)的指针以得到服务器的名字。我们将使用_bstr_t copy构造器来转换该字符,在与BSTRs和宽字符配合时,_bstr_t是一个很有用的类。要注意到COSERVERINFO结构体会通过 memset()函数初始化为0。

// remote server info
COSERVERINFO cs;
// Init structures to zero
memset(&cs, 0, sizeof(cs));
// Allocate the server name in the COSERVERINFO struct
// use _bstr_t copy constructor
cs.pwszName = _bstr_t(name);


  通过MULTI_QI指定接口

  我们一般通过调用CoCreateInstance得到一个接口指针。对于DCOM来说,我们需要使用扩展的版本 CoCreateInstanceEx。这个扩展的函数对于本地的COM服务器调用也是适用的。CoCreateInstanceEx有几个重要的区别。首先,它可让你指定服务器的名字;第二,通过一次调用,它可让你得到超过一个的接口。

  我们已经设置好COSERVERINFO结构体。我们将把它传送到CoCreateInstanceEx以指定服务器(如果你将该参数设置为NULL,你将使用本地的计算机)。

  以一般的版本不同,CoCreateInstanceEx可一次返回超过一个接口。它通过传送MULTI_QI结构体的一个数组来做到这一点。数组的每个元素指定了一个单一的接口。CoCreateInstanceEx将会填入到数据请求中。

// structure for CoCreateInstanceEx
MULTI_QI qi[2];
// Array of structures
// set to zero
memset(qi, 0, sizeof(qi));

// Fill the qi with a valid interface
qi[0].pIID = &IID_IGetInfo;
qi[1].pIID = &IID_ISomeOtherInterface;

// get the interface pointer
hr = CoCreateInstanceEx(
CLSID_GetInfo, // clsid
NULL, // outer unknown
CLSCTX_SERVER, // server context
&cs, // server info
2, // size of qi
qi ); // MULTI_QI array


  MULTI_QI结构体包含有三部分的信息:

   a)一个到IID的指针;

   b)返回接口的指针;

   c)一个HRESULT。

  以下就是该结构,定义在OBJIDL.IDL:

typedef struct tagMULTI_QI
{
 // pass this one in
 const IID *pIID;
 // get these out (must set NULL before calling)
 IUnknown *pItf;
 HRESULT hr;
} MULTI_QI;


  qi变量是MULTI_QI结构体的一个数组。开始时我们通过调用memset(),将整个数组设置为0。接着我们就会将一个指针填入到pIID元素中,该指针指向我们选用接口的接口GUID(IID)。在我们的例子中,我们请求一个到IGetInfo和 ISomeOtherInterface (ISomeOtherInterface是一个虚构的接口)的指针。如果你仅需要一个单一的接口,我们可将数组的大小设置为1并且忽略第二个元素。 CoCreateInstanceEx的第5个参数定义了数组的大小。

  调用CoCreateInstanceEx将会计算MULTI_QI数组的大小。与大多数的COM API函数一样,CoCreateInstanceEx返回一个HRESULT。该函数可返回以下HRESULT中的任意一个。

  S_OK:所有接口均被返回

  CO_S_NOTALLINTERFACES:至少有一个接口被返回,但有一些失败了

  E_NOINTERFACE:没有接口被返回

  为了知道哪个接口失败了,你可以检测qi数组中的HRESULT。

  if (SUCCEEDED(hr))
  if (SUCCEEDED(qi[0].hr)) {
  // pItf pointer is OK.

  如果网络存在问题,CoCreateInstanceEx将会失败

  一切准备好后,你就可以调用CoCreateInstanceEx,它可能成功,也可能失败,例如得到一个 RPC_S_SERVER_UNAVAILABLE的错误。在我的代码中,我通常仅检查CoCreateInstanceEx返回的状态,并且在返回不是 S_OK时终止程序。这是由于如果得不到所有请求的接口,我的处理就不能进行。要注意的是你仍然需要调用Realease来释放所有返回成功的接口。

  对于跨网络的DCOM来说,错误码的识别是非常重要的。HRESULT中包含有许多你需要的信息,这些信息对于跟踪网络错误是特别有用的。我将在本文的后面更详细地讨论这些错误。

  一旦你已经确定接口被返回了,你就可以使用接口指针。在本文的例子中,我将拷贝请求来的指针到一个IGetInfo接口指针中,以便在程序中使用。

// pointer to interface
IGetInfo *pI;
if (SUCCEEDED(hr))
{
// Basic style string
BSTR bsName;

// Extract the interface from the MULTI_QI structure
pI = (IGetInfo*)qi[0].pItf;

// Call a method on the remote server
hr = pI->GetComputerName( &bsName );

pI->Release();
...


  剩余的代码只是一般的COM客户代码。一旦你连接到服务器,DCOM的代码就没有什么特别的。我们将在后面继续讨论一个很大的区别--错误。在你首次令客户和服务器一起经过网络工作时,你会遇到很多的问题。大部分的这些问题都与服务器和proxy/stub登记有关。

  登记服务器和proxy/stub

  如果你在同一台机器上工作,那么登记DCOM和标准的COM是一样的。当你使用-REGSERVER开关运行服务器程序时,它将会自动登记自己。使用向导产生的标准服务器都内置有这些代码,只要该EXE带-REGSERVER开关运行,它都会自动在注册表登记自己并且退出。

   C/> remoteServer -regserver

  在这些例子中我们使用自定义的接口。这意味着在客户端的机器上需要proxy/stub DLL。proxy/stub是用来通过网络在客户和服务器之间发送所有信息的组件。要使用一个proxy/stub DLL,你需要登记它。

   C:/> REGSVR32 remoteserverps.dll

  上面将会在客户端注册proxy/stub DLL,以便DCOM可以自动激活它。如果你使用一个基于IDispatch(或者dual)的自动操作客户端,你将没有一个proxy/stub DLL。这种情况下,你将使用一个类库来登记。

  拷贝客户端到另一个计算机上

  你可能已经在同一部计算机上建立了自己的客户和服务器,并且在同一台机器上作了测试。现在你可将该客户端的程序拷贝到另一台计算机上,并且进行远程的测试。将客户端的EXE文件和由服务器产生的proxy/stub DLL拷贝过去。你不必在客户端的机器登记服务器。因为我们已经在COSERVERINFO的结构体中指定了服务器计算机的名字。当然,服务器必须在服务器端的计算机注册,否则不能工作。

  在客户端的计算机创建一个新的目录来,并将客户端的代码拷贝到该目录。然后登记proxy/stub。在客户端的计算机,你可在MS-DOS方式下输入类似如下的命令登记:

   C:> COPY //Raoul/UnderCOM/RempteClient/Debug/Remoteclient.EXE

   C:> COPY //Raoul/UnderCOM/RemoteServer/RemoteserverPS.DLL

   C:> REGSVR32 RemoteserverPS.DLL

  许多人都忘了第三行中登记proxy/stub DLL步骤。没有这一行,也是不能工作的。

  安全是一个大问题

  安全是一个真正重要的问题,特别是对于网络应用来说。最后,你将必须确保你的DCOM应用是安全的。一旦你理解了这些概念,并且让服务器工作,然后你就可以为你的应用加入安全的特性。我的意见是首先让应用运行起来,然后再考虑安全。

  Win95/98和Windows NT提供的安全性是不同的。Windows 95/98只提供有限的安全特性,而Windows NT中的安全性则强大得多。

  你可以在程序中管理客户和服务器的安全设置,通过调用CoInitializeSecurity API完成。你可以使用这个调用来加入或者关闭安全性。你可以在调用CoInitialize后就马上调用这个方法。

// turn off security - overrides defaults
hr = CoInitializeSecurity(NULL, -1, NULL, NULL,
RPC_C_AUTHN_LEVEL_NONE,
RPC_C_IMP_LEVEL_IMPERSONATE,
NULL,
EOAC_NONE,
NULL);


  CoInitializeSecurity可接收一些参数,这些参数都很重要并且代表了有意义的安全概念。你可以在 CoInitializeSecurity 和RPC 安全的帮助文件中找到更多相关的资料。(参见MSDN中的“Security in COM”部分)

  要注意的是你还可以通过DCOMCNFG和OLEVIEW来指定一个服务器的安全信息。这些工具将安全信息保存在注册表中。你还可以通过显式调用CoInitializeSecurity 来指定客户端程序的安全性(这样将会覆盖默认的注册表设定)。

  CoInitializeSecurity有趣的地方是它可以被客户和服务器调用。许多的参数是客户端用的,另一些是服务器用的。

  错误,问题和更多的错误:调试

  在第一次启动时,你的客户和服务器程序可能不能工作。这里就讨论一下DCOM会遇到的大问题。远程连接问题通常有以下4种:

  1。客户和服务器一开始就不能工作。先在本地调试好,再尝试放到网络上。

  2。连接问题。如果客户和服务器计算机间没有一个好的网络连接,DCOM将不能工作。网络配置可以是很复杂的。

  3。启动问题。COM不能启动服务器计算机上的COM服务器程序。这通常是安全性和登记的问题。

  4。你不能连接到服务器上的COM对象。这通常是安全性和登记的问题。

以下部分将讨论一下在网络上工作时你可能遇到的一些问题。我尝试将一些我认为有利于诊断和修复问题的方法概括出来。以下是一些如何调试DCOM的网络问题的建议。

  第一部:先让它在本地工作

  调试的第一部是先让客户和服务器可以在本地工作。在服务器的机器上安装好全部的组件,直到你可以成功通信为止。如果一个组件不能在本地工作,它也不可以跨网络工作。你可能已经在一台计算机上开发和测试了该应用,要是你将服务器安装在一部不同的计算机上,你还需要在该系统上同时测试客户和服务器。

  如果该系统可以在本地工作,你就消除了大部分常见的编程和登记错误。还有其它的方面需要测试,例如安全和远程激活,这些只能在网络上测试。在你COSERVERINFO结构体中指定你的本地计算机,这样可测试大部分的网络相关代码。

  第二步:确定你可以连接

  在尝试安装你的程序之前,你首先要测试调试客户和服务器机器之间的网络配置。可以通过打开网络邻居,看能否浏览到远程的计算机。不过这并不是一定的,不能浏览并不意味着DCOM不能工作,但是,在大多数的情况下,能否浏览是检查连接的一个好开始。注意,要检查两个方向。

  最有用的工具可能是PING。PING发送一系列的网络包到一部指定的机器,并且等待一个响应。许多系统都支持PING。

   C:/> PING www.iftech.com

   Pinging www.iftech.com 216.27.33.21 with 32 bytes of data:

   Reply from 216.27.33.21: bytes=32 time=217ms TTL=120

   Reply from 216.27.33.21: bytes=32 time=210ms TTL=120

   Reply from 216.27.33.21: bytes=32 time=197ms TTL=120

   Reply from 216.27.33.21: bytes=32 time=209ms TTL=120


   Ping statistics for 216.27.33.21:

   Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),

   Approximate round trip times in milli-seconds:

   Minimum = 197ms, Maximum = 217ms, Average = 208ms

   C:/>

  PING为你做了不少有趣的事情。首先,它解析远程计算机的名字。如果你使用TCP/IP,远程的计算机名字将会被转换为一个TCP/IP地址。在上面的例子中,PING将"www.iftech.com"的名字转换为TCP/IP地址: 216.27.33.21。

  你应该在两个方向尝试PING。如果你使用回叫(callback)或者连接点(connection points),你必须让COM可双向工作。如果网络连接不行的话,要调试Callbacks或者connection points是非常困难的。

  第三步:尝试工作。不要忘了登记proxy/stub DLL或者类库

  一旦你完成了上面的两个步骤,你就可以尝试进行远程的连接。

  连接失败的另一个常见原因是注册问题。你要确保在服务器的计算机上登记了COM服务器,确保在客户和服务器计算机上都登记了proxy/stub DLL。如果你使用一个自动操作或者dual接口,你要确保在两个系统中都登记了类库。(要登记一个proxy/stub DLL,使用REGSVR32命令)

  第四步:尝试解决命名解析问题

  在远程连接中,命名解析是一个恼人的问题。大部分人都想直接使用“//RAOUL”或者“//SERVER”的名字进行工作,而不是TCP/IP地址。将可读的名字转换为一个网络地址的过程就称为命名解析,在某些系统配置上,它可能是非常复杂的。一个简单的解决办法是通过服务器的TCP/IP地址来访问它。这将可以消除许多命名解析问题--这个问题已经超出了本文的讨论。在COSERVERINFO结构体中,你可以使用TCP/IP地址来代替标准的计算机名字。

  你也可以从TRACERT(跟踪路由)工具中得到有趣的信息。如果你有一个复杂的网络配置,跟踪结果可能是:

   C:/> TRACERT www.iftech.com

   Tracing route to www.iftech.com 216.27.33.21

   over a maximum of 30 hops:

   1 184 ms 169 ms 182 ms ct1.intercenter.net [207.211.129.2]

   2 182 ms 189 ms 188 ms ts-gw1.intercenter.net [207.211.129.1]

   3 195 ms 192 ms 161 ms ilan-gw1.intercenter.net [207.211.128.1]

   4 220 ms 178 ms 206 ms 206.152.70.33

   5 188 ms 207 ms 216 ms 207.211.122.2

   6 196 ms 189 ms 205 ms 216.27.1.71

   7 * * * Request timed out.

   8 201 ms 221 ms 197 ms rampart.iftech.com [216.27.12.142]

   9 210 ms 205 ms 192 ms www.iftech.com [216.27.33.21]

   Trace complete.

   C:/>

  就象你看到的,你的DCOM包到目的地的路由可以是很复杂的。

  要特别留意网关、路由器、代理和防火墙,因为它们经常会阻塞你的连接。特别是防火墙,因为它们经常会阻塞DCOM包,可与网络管理员一起检查一下。

  第5步:不能启动服务器?

  谈到启动,我们指的是COM可以在收到请求时自动启动服务器。在一个本地的COM连接时,通常都可以工作,不过要是使用网络连接的话,就会有一些问题。如果你使用Windows 95/98的话,COM将不会自动启动一个服务器。这个限制是必要的,因为这些操作系统缺乏安全性。而Windows NT提供较全面的安全性,因此它可以自动启动服务器。

  对于Windows 95/98的客户来说,解决这个问题的方法是很简单的。那就是在客户连接前,手动启动服务器。一旦服务器被启动,连接也就正常了。

  还有一个问题,当最后的客户调用Release()时,服务器就会关闭。COM服务器的引用计数为0时,服务器将会关闭自己。这意味着客户端在下一次尝试连接时将会失败。你将会在调用CoCreateInstanceEx得到RPC_S_SERVER_UNAVAILABLE或者 E_ACCESSDENIED的信息。

  我爱用的解决办法是在服务器的计算机上运行一个简单的“监控”程序。这个简单的应用负责维护一个到服务器的COM连接。因为该程序运行在服务器计算机中,它可以在启动的时候自动启动服务器。只要监控程序正在运行,服务器将保持活动。你还可以将该监控程序放到启动组中,这样在机器启动的时候就会打开该服务器。

  第6步:尝试使用DCOMCNFG和OLEVIEW

  你可能已经使用过这两个工具了。DCOMCNFG和OLEVIEW是两个微软的工具,可将你的COM服务器的登记信息展现出来。这两个工具都是将你的注册信息以一个更加结构化的形式显示出来。这两个工具都有缺点。OLEVIEW展现很多的信息,但不少都是你不需要的。这个工具的最新版本有很多好用的特性,你可以从微软的网站下载这个最新的版本:http://www.microsoft.com/com/resource/oleview.asp。 DCOMCNFG是一个一般的工具,它展现的信息不少,但是很多的人都不能有效地使用它。如果你还没有使用以上一款工具,你应该现在开始试一下。在 Internet上还有一些好用的第三方COM工具。

  第7步:重新登记服务器和proxy/stub(或者类库)

  有时重新登记服务器和proxy/stub可以解决问题。如果你使用Automation(或者dual)接口,则重新登记类库。

  第8步:询问一个网络管理员

  有时最好的防线是一个有能力的网络管理员。如果你有一个,你将是非常幸运的。遗憾的是,有能力的网络管理员通常都是 a)少,b)很忙,c)不愿意和编程者工作。管理员可以帮助你解决安全和命名解析的问题。

典型的DCOM错误和它们的含义

以下的错误代码是你的客户程序可能得到的典型HRESULT值。我还加入了一些它们含义的解释。还有象征的名字和解释后的信息文本。这不是一个全面的列表,不过对于大部分的常见错误来说,是一个不错的开始。






CO_E_BAD_SERVER_NAME
需要进行远程激活,但是提供的服务器名字是无效的


 
这是其中一个容易看懂的错误信息。要注意的它并不意味着你输入了一个错误的服务器名字。它意味着你输入了一个无效的服务器名字。检查名字是否使用了正确的网络格式--检查有没有无效的字符
不能解析或者不存在的服务器使用一个不同的错误信息:
RPC_S_SERVER_UNAVAILABLE.
CO_E_SERVER_EXEC_FAILURE
服务器执行失败
服务器执行失败 查看COM的安全FAQ得到更多的信息
Microsoft Support - Article Q158508
(该站点需要注册)
E_ACCESSDENIED
一般的访问拒绝错误
这是一个来自安全子系统的错误。服务器系统拒绝一个连接。这个问题可以是很难诊断的。这个错误很可能在使用DCOM作远程连接时发生
检查激活问题,特别是在Windows 95/98

检查DCOMCNFG的安全性设置

重新安装服务器和Proxy/Stub DLL.

确保你打开了文件/打印共享


E_FAIL
未指定的错误
这通常是一个由一个正在返回E_FAIL的方法导致的应用指定错误

E_NOINTERFACE
不支持该接口
你向一个服务器请求一个不支持的接口。这意味着你的CLSID可能是对的,不过IID不对,在调用QueryInterface (或者通过CoCreateInstance)时,如果它不能识别该接口,将返回这个错误。它可能是一个proxy/stub问题
这可能是一个注册问题。尝试重新登记服务器和proxy/stub
E_OUTOFMEMORY
没有内存
这个信息可能与真正的错误无关。参见安全FAQ得到其它可能性
Microsoft Support - Article Q158508 (该站点需要注册)
ERROR_INVALID_PARAMETER
参数不正确
在你的函数调用中,其中一个参数有问题。这通常发生在诸如CoCreateInstance,CoCreateInstanceEx, CoInitializeSecurity等的函数中
ERROR_SUCCESS
操作完全成功
与S_OK和NO_ERROR的含义一样
REGDB_E_CLASSNOTREG
类没有登记
登记或者CLSID问题。检查你的GUID 这个服务器不能在远程的Windows 95/98系统运行
RPC_S_SERVER_UNAVAILABLE
找不到RPC服务器
在使用远程服务器是,这个问题很常见。这是一个普通的远程连接错误。RPC是用来实现DCOM的协议。这可能是一个系统设置问题或者是一个安全性问题。
你可能正在尝试连接到一个不正确或者断开的计算机。检查服务器的名字

确保计算机上的DCOM和RPC打开了。可使用DCOMCNFG 或者 OLEVIEW (在文件菜单下的“系统设置”中)

重新注册登记服务器和proxy/stub.



*********表一**********************

结论

将程序由本地移到远程的服务器,编程上的区别并不大。实现上却有不少的困难。设置一个远程的连接需要花费不少的时间和努力。不过,我们的收益也是很大的。在远程的计算机上运行你的服务器可令你的应用有了质的变化。

对于COM的编程者来说,还有一些好消息,例如COM+可让DCOM的编程更加简单。不过对COM+的头几次发布不要寄予太大的期望,大部分的DCOM问题还有待解决。

在该系列的文章中,你已经了解了如何使用COM来很简单地创建一个对象服务器,并且在一个客户应用中访问它。你也懂得了如何使用DCOM来实现一个运行在网络上的远程对象服务器。掌握了这些知识,你可以做到:

a)在创建一个DLL时使用COM;
 
b)创建基于对象的服务器来组件化你的代码。

DCOM揭秘之六——远程COM调用(服务器端和客户端)

DCOM揭秘之六 2001-08-23 09:27作者:QQ新人类出处:Yesky责任编辑:方舟    Distributed COM --跨网络工作   ...

DCOM揭秘之一

理解最简单的COM客户   要理解COM的最直接方法是通过一个客户应用来考察它。COM编程的目的是为了让客户应用可以得到有用的对象。一旦你理解了客户,要理解服务端就变得非常的简单。相反,同时直接...
  • stypace
  • stypace
  • 2012年12月13日 12:12
  • 843

分布式组件对象模型DCOM揭秘

介绍   对于许多人来说,学习COM和DCOM是一件吃力的事情。COM的用处很大,不少微软的产品和编程者工具都是基于COM,不过,COM是一门颇难掌握的技术,你可能曾经想去学习它,阅读过一些书,...
  • stypace
  • stypace
  • 2012年12月13日 11:55
  • 895

DCOM 揭秘之三

Distributed COM --跨网络工作   DCOM代表的是“Distributed(分布式)”COM。在前面的部分中,我们已经讲解了运行在同一部计算机的COM客户和服务器。在这一部...
  • stypace
  • stypace
  • 2012年12月13日 21:06
  • 5509

[软件体系结构]DCOM,CORBA,EJB介绍

背景介绍90年代出现的分布式对象技术为网络计算平台上软件的开发提供了强有力的解决方案。目前,分布式对象技术已经成为建立服务应用框架和软件构件的核心技术,在开发大型分布式应用系统中表现出强大的生命力,逐...

COM、COM+和DCOM的定义和区别

From:http://zouyou1986.blog.163.com/blog/static/1772625920105461931335/   解释COM、COM+和DCOM的定义和区别? ...

DCOM列集散集的内部具体实现的研究初探。

我也不罗嗦什么理论基础,相信看这篇文章的人肯定对COM有一定的功底了。下面的介绍只是我对COM实现的自己的理解因此肯定有错误的地方,目的是希望大家指出和讨论,以求共同进步。 1.套间的注册 ...
  • Andeewu
  • Andeewu
  • 2013年11月16日 15:33
  • 737

中间件(RPC,CORBA,DCOM,web service等)

http://en.wikipedia.org/wiki/Middleware_(distributed_applications) http://baike.baidu.com/view/23...

COM、COM+和DCOM的定义和区别

解释COM、COM+和DCOM的定义和区别? COM是组件对象模型,是实现3/N层应用的基础,它的目的就是组件化,应用程序分层.DCOM是分布式的COM,也就说可以远程的创建,最初它利用远程自动化来...
  • Baple
  • Baple
  • 2014年09月25日 09:30
  • 1192

MS历史学习:OLE COM DCOM COM+技术与OPC技术

微软的许多技术,如OLE、ActiveX、以及DirectX等都是基于COM技术而建立起来的。微软本身也大量地使用COM组件来定制他们的应用程序及操作系统。 OLE(Object Link ...
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:DCOM揭秘
举报原因:
原因补充:

(最多只允许输入30个字)