DCOM

原创 2007年09月20日 13:44:00

2000下的配置:
DCOM配置

  如果在Windows NT环境下正常运行应用服务器,必须进行DCOM配置。配置方法如下:

  1.运行NT服务器上的dcomcnfg程序,进行DCOM配置。

  2.进入DCOM的总体默认属性页面,将“在这台计算机上启用分布式COM”打上勾,将默认
身份级别改为“无”。

  3.进入DCOM的总体默认安全机制页面,确认默认访问权限和默认启动权限中的默认值无EveryOne,
如果不去掉EveryOne,应用服务器不能正常启动。

  4.在常规页面中,双击你的应用服务器,打开你的应用服务器DCOM属性设置。

  5.将常规页面中的身份验证级别改为“无”。

  6.位置页面中选上“在这台计算机上运行应用程序”。

  7.将安全性页面设置中,均选择“使用自定义访问权限”,编辑每一个权限,将EveryOne加入用
户列表中。

  8.身份标识页面中,选择“交互式用户”。

  9.NT的GUEST用户不能禁用。
注意:关键所在,在控制面板--用户和密码里administrators用户的属性--隶属于里添加power users,其它用户也这样做,guest用户绝对不可以禁用

 

以远程计算机上的用户身份访问Com+应用

    DELPHI程序员开发com+应用的速度是非常快的,其主要原因是其较好地封装了com+的windows底层功能,
开发人员通过较为简单的类继承就避开了复杂的com+底层技术细节,使开发人员将精力放在应用本身的
功能上面。Delphi在封装com+应用时采取了许多折衷,在保留通用性的同时也避开了一些实现起来困难
但是应用面不太广的com+底层特性。这些避开的特性中最令delphi com开发人员关心的就是安全特性。
从delphi 5开始,有许多人都面临过这样的问题:com应用开发出来并且在本机上运行一切正常,
但是一旦分发出去实施远程访问时,就无法正常运行了。我自己有段时间在看到“拒绝访问”错误提示时
会本能的头晕。其实认真追究起来,还是因为自己对windows安全技术了解不多造成的。多年来我一直
没有发现国内有windows安全方面比较系统的资料和书籍,直到Keith Brownr的<windows安全性编程>
中文版的出现。正是基于这本书我才有了下面的一些试验,也知道了为什么我老是被拒绝的原因。

下面的讨论只是我在解决自身现有代码的安全访问问题时,总结出的几个小经难方法。
建议愿意了解windows安全性的朋友去看一看<windows安全性编程>一书,你会发现windows的安全不再神秘。
这篇文章将会说明如何以远程工作站上的用户身份激活com+对象,并以此用户身份访问Interface。

1、Delphi默认com+对象的远程激活

    Delph中远程com+对象激活一般通过TdispatchConnection及其子类来实现,实际代码中多用
TDCOMConnection或TsocketConnectoion这两个组件,TDCOMConnection组件最终调用CoCreateInstanceEx
创建com+对象。CoCreateInstanceEx(const clsid: TCLSID; unkOuter: IUnknown; dwClsCtx: Longint;
ServerInfo:PCoServerInfo;dwCount: Longint; rgmqResults: PMultiQIArray): HResult。
TDCOMConnection在调用CoCreateInstanceEx时为pCoServerInfo参数中的pAuthInfo传递了Null值,
因此TdcomConnection在创建Com对象时使用的是本地计算机登录者的用户令牌。假若A计算机上的登录用户
Auser使用TDCOMConnection类连接远程计算机B上的com+对象,则B计算机会使用Auser的用户名/密码在B计
算机上建立登录会话并最终创建com+对象。但是一台windows工作站上的本地用户只能在本地登录而无法在
别的计算机上登录,因此A计算机上的Auser就无法在B工作站上建立登录会话,当然也就无法创建com+对象,
此时远程工作站B会尝试用Guest帐户建立会话并使用该账户激活com+对象。在这种情况下,如果B工作站上
的Guest账户没有启用或Guest没有激活com+对象的权限,你就会看见令人头晕的提示“拒绝访问”。看到这里
你是不对现在网上最“流行”的dcom配置方法有所悟了呢。那个方法就是允许everyone访问、激活com对象、
并且将“默认身份验证级别”设置成无。这种方法能够使你的com应用可以“用了”,但是,它可以上“任何人”
访问。而且这种设置你将无法利用com+基于角色的安全访问控制功能。

2、怎样不用GUEST账户激活

    这个问题的实际上应该是:怎样用远程工作站上的用户激活远程com对象。解决这个问题其实很简单:
只要你在调用CoCreateInstanceEx时为它指定远程工作站上的用户名和密码,只要用户名/密码通过远程
计算机的验证,并且该用户被授予了“远程激活”com+对象的权限,那么远程工作站会用该用户身份激活com+
对象。

看一下代码:

var
  mts:IMTSXjpimsDB;
  ov:Variant;
  i:integer;
  cai:_CoAuthInfo;
  cid:_CoAuthIdentity;
  csi:COSERVERINFO;
  mqi:MULTI_QI;
  iid_unk:TGUID;
  idsp:IDispatch;
  wUser,wDomain,wPsw:WideString;
begin
  wUser:=eduser.text;//用户名
  wDomain:=edSvr.Text;//远程计算机名
  wPsw:=edPsw.Text;//密码
  cid.user:=pUnshort(@wUser[1]);
  cid.UserLength:=length(wUser);
  cid.Domain:=pUnshort(@wDomain[1]);
  cid.DomainLength:=length(wDomain);
  cid.password:=pUnshort(@wPsw[1]);
  cid.PasswordLength:=length(wPsw);
  cid.Flags:=2;
  //以上填充_CoAuthIdentity结构
  cai.dwAuthnSvc:=10;//winNt默认的鉴证服务
  cai.dwAuthzSvc:=0;
  cai.pwszServerPrincName:=wDomain;
  cai.dwAuthnLevel:=0;
  cai.dwImpersonationLevel:=3;//必须设置成模拟
  cai.pAuthIdentityData:=@cid;
  cai.dwCapabilities:=$0800;
  //以上填充_CoAuthInfo结构
  FillChar(csi, sizeof(csi), 0);
  csi.dwReserved1:=0;
  csi.pwszName:=pwidechar(wdomain);
  csi.pAuthInfo:=@cai;
  //以上填充COSERVERINFO结构
  iid_unk:=IUnknown;
  mqi.IID:=@iid_unk;mqi.Itf:=nil;mqi.hr:=0;
  Screen.Cursor:=crHourGlass; olecheck(CoCreateInstanceEx(CLASS_MTSXjpimsDB,nil,
  CLSCTX_REMOTE_SERVER,@csi,1,@mqi));

这段代码中除了最后实际调用CoCreateInstanceEx外,前面的代码都是设置参数。这些参数的含义请大家
参考msdn,除了用户名、主机名、密码外,只有一个重要要部分要说明:cai.dwImpersonationLevel必须
设置成允许模拟(值为3),否则远程计算机将无法按提供的用户/密码建议网络会话。
3、不修改现有代码,可以实现用远程用户身份激活吗?
当然可以,我扩展了TDcomConnection类,为其加入了用户名和密码,并修改其默认的DoConnect方法,
使其在调用CoCreateInstanceEx时用指定的用户名和密码填充参数。代码如下:

 

unit SecDComConnection;

interface

uses
  windows,SysUtils, Classes,ActiveX, DB, DBClient, MConnect,comobj,Midas;

type

{typedef struct _SEC_WINNT_AUTH_IDENTITY
 unsigned short __RPC_FAR* User;
  unsigned long UserLength;
  unsigned short __RPC_FAR* Domain;
  unsigned long DomainLength;
  unsigned short __RPC_FAR* Password;
  unsigned long PasswordLength;
  unsigned long Flags;
 SEC_WINNT_AUTH_IDENTITY, *PSEC_WINNT_AUTH_IDENTITY;
}
  {typedef struct _COAUTHIDENTITY
    USHORT * User;
    ULONG UserLength;
    USHORT * Domain;
    ULONG DomainLength;
    USHORT * Password;
    ULONG PasswordLength;
    ULONG Flags;
COAUTHIDENTITY;}

{#define RPC_C_AUTHN_NONE            0
#define RPC_C_AUTHN_DCE_PRIVATE     1
#define RPC_C_AUTHN_DCE_PUBLIC      2
#define RPC_C_AUTHN_DEC_PUBLIC      4
#define RPC_C_AUTHN_GSS_NEGOTIATE   9
#define RPC_C_AUTHN_WINNT          10
#define RPC_C_AUTHN_GSS_SCHANNEL   14
#define RPC_C_AUTHN_GSS_KERBEROS   16
#define RPC_C_AUTHN_MSN            17
#define RPC_C_AUTHN_DPA            18
#define RPC_C_AUTHN_MQ            100
#define RPC_C_AUTHN_DEFAULT       0xFFFFFFFFL
}

{#define RPC_C_AUTHZ_NONE      0
#define RPC_C_AUTHZ_NAME      1
#define RPC_C_AUTHZ_DCE       2
#define RPC_C_AUTHZ_DEFAULT   0xFFFFFFFF }

{
#define RPC_C_AUTHN_LEVEL_DEFAULT         0
#define RPC_C_AUTHN_LEVEL_NONE            1
#define RPC_C_AUTHN_LEVEL_CONNECT         2
#define RPC_C_AUTHN_LEVEL_CALL            3
#define RPC_C_AUTHN_LEVEL_PKT             4
#define RPC_C_AUTHN_LEVEL_PKT_INTEGRITY   5
#define RPC_C_AUTHN_LEVEL_PKT_PRIVACY     6 }

{SEC_WINNT_AUTH_IDENTITY_UNICODE=2 }

   pUnShort=^Word;

   pCoAuthIdentity=^_CoAuthIdentity;
   _CoAuthIdentity=record
     user:pUnShort;
     UserLength:ULONG;
     Domain:pUnShort;
     DomainLength:Ulong;
     password:pUnShort;
     PasswordLength:ulong;
     Flags:ulong;
   end;

  _CoAuthInfo=record
    dwAuthnSvc:DWORD;
    dwAuthzSvc:DWORD;
    pwszServerPrincName:WideString;
    dwAuthnLevel:Dword;
    dwImpersonationLevel:dword;
    pAuthIdentityData:pCoAuthIdentity;
    dwCapabilities:DWORD;
  end;

  TSecDComConnection = class(TDCOMConnection)
  private
   FCai:_CoAuthInfo;
   FCid:_CoAuthIdentity;
   FSvInfo:COSERVERINFO;
   FUser:WideString;
   FPassWord:WideString;
   procedure SetPassword(const Value: wideString);
   procedure SetUser(const Value: wideString);
    procedure SetSvInfo(const Value: COSERVERINFO);
  protected
    procedure DoConnect; override;

  public
    property SvInfo:COSERVERINFO read FSvInfo write SetSvInfo;
    constructor Create(AOwner: TComponent); override;
    procedure MySetBlanket(itf:IUnknown;const vCai:_CoAuthInfo);
    function GetServer: IAppServer; override;
  published
    property User:wideString read FUser write SetUser;
    Property Password:wideString read FPassword write SetPassword;
  end;

procedure Register;

implementation

constructor TSecDCOMConnection.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);
  FillMemory(@Fcai,sizeof(Fcai),0);
  FillMemory(@FCid,sizeof(FCid),0);
  FillMemory(@FSvInfo,sizeof(FSvInfo),0);
  with FCai do begin
    dwAuthnSvc:=10;//RPC_C_AUTHN_WINNT
    dwAuthzSvc:=0;// RPC_C_AUTHZ_NONE
    dwAuthnLevel:=0;//RPC_C_AUTHN_LEVEL_DEFAULT
    dwImpersonationLevel:=3;
    pAuthIdentityData:=@fcid;
    dwCapabilities:=$0800;
  end;
end;

procedure TSecDCOMConnection.DoConnect;
var
 tmpCmpName:widestring;
 IID_IUnknown:TGUID;
 iiu:IDispatch;
 Mqi:MULTI_QI;
 qr:HRESULT;
begin
  if (ObjectBroker) <> nil then
  begin
    repeat
      if ComputerName = '' then
        ComputerName := ObjectBroker.GetComputerForGUID(GetServerCLSID);
      try
        SetAppServer(CreateRemoteComObject(ComputerName, GetServerCLSID) as IDispatch);
        ObjectBroker.SetConnectStatus(ComputerName, True);
      except
        ObjectBroker.SetConnectStatus(ComputerName, False);
        ComputerName := '';
      end;
    until Connected;
  end
  else if (ComputerName <> '') then
    begin
      with fcid do begin
        user:=pUnshort(@fuser[1]);
        UserLength:=length(fuser);
        tmpCmpName:=ComputerName;
        Domain:=pUnshort(@tmpCmpName[1]);
        DomainLength:=length(TmpCmpName);
        password:=pUnShort(@FPassword[1]);
        PasswordLength:=length(FPassword);
        Flags:=2;//Unicode
      end;
      FSvInfo.pwszName:=pwidechar(tmpCmpName);
      FSvinfo.pAuthInfo:=@Fcai;
      IID_IUnknown:=IUnknown;
      mqi.IID:=@IID_IUnknown;mqi.Itf:=nil;mqi.hr:=0;
      olecheck(CoCreateInstanceEx(GetServerCLSID,nil,CLSCTX_REMOTE_SERVER,@FSvinfo,1,@mqi));
      olecheck(mqi.hr);
      MySetBlanket(mqi.Itf,Fcai);
      qr:=mqi.Itf.QueryInterface(idispatch,iiu);
      olecheck(qr);
      MySetBlanket(IUnknown(iiu),FCai);
      SetAppServer(iiu);
    end
    else
      inherited DoConnect;
end;

function TSecDComConnection.GetServer: IAppServer;
var
  QIResult: HResult;
begin
  Connected := True;
  QIResult := IDispatch(AppServer).QueryInterface(IAppServer, Result);
  if QIResult <> S_OK then
  begin
    Result := TDispatchAppServer.Create(IAppServerDisp(IDispatch(AppServer)));
  end;
  MySetBlanket(IUnknown(Result),FCai);
end;

procedure TSecDCOMConnection.MySetBlanket(itf: IUnknown;
  const vCai: _CoAuthInfo);
begin
 with vCai do
 CoSetProxyBlanket(Itf,dwAuthnSvc,dwAuthzSvc,pwidechar(pAuthIdentityData^.Domain),
    dwAuthnLevel,dwImpersonationLevel,pAuthIdentityData,dwCapabilities);
end;

procedure TSecDCOMConnection.SetPassword(const Value: wideString);
begin
  FPassword := Value;
end;

procedure TSecDCOMConnection.SetSvInfo(const Value: COSERVERINFO);
begin
  FSvInfo := Value;
end;

procedure TSecDCOMConnection.SetUser(const Value: wideString);
begin
  FUser := Value;
end;

procedure Register;
begin
  RegisterComponents('DataSnap', [TSecDComConnection]);
end;

end.

 

OPC DCOM配置的一点经验

最近做了一个小项目,功能很简单,就是编写一个OPC客户端软件实现从OPC服务器上获取数据然后转存到DB中。项目虽小但是值得考虑的地方仍不少,所谓麻雀虽小,五脏俱全。...
  • rizhaolutong
  • rizhaolutong
  • 2014年03月03日 22:07
  • 4046

在DCOM 中不存在WORD、EXCEL等OFFICE组件

最近在做一个关于office转存PDF的Web项目。开发过程一切顺利。 起初在网上找到一些Word,PPT转PDF的代码。很好用。一切顺利项目开发成功。在这里需要说明一点,PPT中会存在流媒体内...
  • hanaixia2007
  • hanaixia2007
  • 2016年08月04日 11:47
  • 990

【三层架构】——COM/DCOM初识

背景:在学习三层架构的时候,知道三层分为UI层(表现层)、BLL层(业务逻辑层)、DAL层(数据访问层),相对于传统的二层架构(客户端和数据库)来说,多了一个中间层BLL(业务逻辑层),处于UI层和D...
  • zt15732625878
  • zt15732625878
  • 2016年03月23日 16:08
  • 1495

DCOM远程调用权限设置

最近几天被搞得焦头烂额,由于原先开发的DCOM客户端程序s
  • ervinsas
  • ervinsas
  • 2014年07月02日 16:25
  • 7561

Delphi COM编程技术六(DCOM技术)

在Delphi中能实现远程调用的有DCOM或COM+,这两种方式实现起来非常的相似。DCOM属于旧版本的远程调用技术,非常实用,所提供的服务保存在应用程序工程中。COM+是新版本的远程调用技术,所提供...
  • zang141588761
  • zang141588761
  • 2016年04月26日 10:00
  • 661

DCOM服务端及客户端指定用户访问的安全设置

前言:对于分布式组件,在创建组件过程中常因权限问题导致出现“拒绝访问”,无法创建组件对象或使用接口。本人水平有限,只能谈些个人经验和体会,希望能抛砖引玉!   有两种方法能使得DCOM组件...
  • embededvc
  • embededvc
  • 2014年01月29日 06:04
  • 3424

COM/DCOM开发之远程进程外组件(DCOM)

一 目的 使用VC++的ATL编程实现远程进程外组件。同时实现客户端这些组件的调用。二 要求 1)使用C++语言实现远程进程外组件,组建提供加、减、乘、除、判断是否素数等计算服务;客户端部分包括录入和...
  • u011402642
  • u011402642
  • 2015年06月16日 12:03
  • 533

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

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

DCOM揭秘之一

传说中的COM,很难明,咬牙切齿……转载天极的一篇文章,说得稍微易懂点,但依然还是稀里糊涂,不懂……              COM的基本要素         首先要弄懂COM是怎...
  • embededvc
  • embededvc
  • 2014年01月09日 17:57
  • 808

DCOM 示例:演示如何远程调用 COM 对象

DCOM 示例:演示如何远程调用 COM 对象更新:2007 年 11 月DCOM 示例说明如何从运行于不同计算机上的多个客户端调用在 Windows 服务中实现的 COM 对象。它由三部分组成: D...
  • shixin_0125
  • shixin_0125
  • 2015年01月09日 14:29
  • 1570
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:DCOM
举报原因:
原因补充:

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