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

来源:互联网 发布:中国人民大网络教育 编辑:程序博客网 时间:2024/05/01 15:41

DCOM揭秘之六

2001-08-23 09:27作者:QQ新人类出处:Yesky责任编辑:方舟


  
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将尝试启动远程计算机上的服务器。



*************图一*******************

  一旦创建了远程的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/stubE_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)创建基于对象的服务器来组件化你的代码。

  好好尝试一下吧!
原文地址
http://www.yesky.com/397/193397_1.shtml
原创粉丝点击