近段时间,我将以前的COM应用中远程激活对象的代码做了整理,并偿试将远程对象激活封装成一个类来简化操作。经本机测试后,代码能够正常激活COM对象,而且在客户端释放后,服务端也正常销毁。但是该代码在激活远程对象后,客户端正常退出,可是服务端却不释放。观查服务端的线程数量,发现每激活一个客户端,服务器均正确的增加一个线程。但释放客户端时,线程数量却没有减少,这意味着服务器端远程对象引用计数未能正确促使对象销毁。本人使用了多种方法仍然无法解决问题。现将代码贴出,请方家指点。
unit RmtComObjectCreater;interfaceuses windows,comobj,activex,sysutils,messages;type
   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;  TRmtComObjectCreater = class(TObject)
  private
    FAuthnLevel: DWORD;
    FAuthnSvc: DWORD;
    FAuthzSvc: DWORD;
    FCapabilities: DWORD;
    FCoAuthIdentity: _CoAuthIdentity;
    FCoAuthInfo: _CoAuthInfo;
    FComputerName: WideString;
    FCoServerInfo: COSERVERINFO;
    FImpersonationLevel: DWORD;
    FLogPSW: WideString;
    FUserName: WideString;
    procedure SetAuthnLevel(Value: DWORD);
    procedure SetAuthnSvc(Value: DWORD);
    procedure SetComputerName(Value: WideString);
    procedure SetImpersonationLevel(Value: DWORD);
    procedure SetLogPSW(Value: WideString);
    procedure SetUserName(Value: WideString);
  protected
    function InternalCreateRmtComObject(const Class_IID,itf_iid:PIID): IUnknown;
    function ItfSetBlanket(var itf: IUnknown; const vCai: _CoAuthInfo): HRESULT;
  public
    constructor Create;
    function CreateRmtComObject(const Class_IID,itf_iid:PIID): IUnknown;
        overload;
    property AuthnLevel: DWORD read FAuthnLevel write SetAuthnLevel;
    property AuthnSvc: DWORD read FAuthnSvc write SetAuthnSvc;
    property AuthzSvc: DWORD read FAuthzSvc write FAuthzSvc;
    property Capabilities: DWORD read FCapabilities write FCapabilities;
    property ComputerName: WideString read FComputerName write SetComputerName;
    property ImpersonationLevel: DWORD read FImpersonationLevel write
        SetImpersonationLevel;
    property LogPSW: WideString read FLogPSW write SetLogPSW;
    property UserName: WideString read FUserName write SetUserName;
  end;
implementation{
***************************** TRmtComObjectCreater *****************************
}
constructor TRmtComObjectCreater.Create;
begin
  inherited Create;
  FComputerName:='';//主机名
  FUserName:='';//用户登录名
  FLogPSW:='';//登录密码
  FAuthnSvc:=10;//RPC_C_AUTHN_WINxNT  NTML认证服务
  FAuthzSvc:=0;// RPC_C_AUTHZ_NONE
  FAuthnLevel:=0;//RPC_C_AUTHN_LEVEL_DEFAULT 默认级别
  FImpersonationLevel:=3;//身份模拟
  FillMemory(@FCoAuthIdentity,SizeOf(FCoAuthIdentity),0);
  FillMemory(@FCoAuthInfo,SizeOf(FCoAuthInfo),0);
  FillMemory(@FCoServerInfo,SizeOf(FCoServerInfo),0);
  FCapabilities:=$0800;//静态跟踪
end;function TRmtComObjectCreater.CreateRmtComObject(const Class_IID,itf_iid:PIID):
    IUnknown;
begin
  Result:=InternalCreateRmtComObject(Class_IID,Itf_IID);
end;function TRmtComObjectCreater.InternalCreateRmtComObject(const Class_IID,
    itf_iid:PIID): IUnknown;
var
  Mqi: MULTI_QI;
  RemoteComputer: Boolean;
  Size: Cardinal;
  LocalMachine: array [0..MAX_COMPUTERNAME_LENGTH] of char;
begin
  Result:=nil;
  RemoteComputer:=False;
  if Length(FComputerName)> 0 then
  begin
    {$IFDEF LOCAL}
    Size := Sizeof(LocalMachine);  // Win95 is hypersensitive to size
    if GetComputerName(LocalMachine, Size) and
       (AnsiCompareText(LocalMachine, FComputerName) <> 0) then
    {$ENDIF}
      RemoteComputer:=true;
  end;
  if RemoteComputer then begin
    with FCoAuthIdentity do begin
      user:=pUnshort(FUserName);
      UserLength:=length(FUsername);
      Domain:=pUnshort(FComputerName);
      DomainLength:=length(FComputerName);
      password:=pUnShort(FLogPSW);
      PasswordLength:=length(FLogPSW);
      Flags:=2;//Unicode 字符串
    end;
    with FCoAuthInfo do begin
      dwAuthnSvc:=FAuthnSvc;//RPC_C_AUTHN_WINNT  NTML认证服务
      dwAuthzSvc:=FauthzSvc;// RPC_C_AUTHZ_NONE
      dwAuthnLevel:=FAuthnLevel;//RPC_C_AUTHN_LEVEL_DEFAULT 默认级别
      dwImpersonationLevel:=FImpersonationLevel;//身份模拟
      pAuthIdentityData:=@FCoAuthIdentity;//主机名/用户名/密码:替换登录用户
      dwCapabilities:=FCapabilities;//静态跟踪
    end;
    FCoServerInfo.pwszName:=PWideChar(FComputerName);
    FCoServerInfo.pAuthInfo:=@FCoAuthInfo;
    //IID_IUnknown:=IUnknown;
    //mqi.IID:=@IID_IUnknown;mqi.Itf:=nil;mqi.hr:=0;
    with mqi do begin
      iid:=itf_iid;
      itf:=nil;
      hr:=0;
    end;
    olecheck(CoCreateInstanceEx(class_iid^,nil,CLSCTX_REMOTE_SERVER,@FCoServerInfo,1,@mqi));
    olecheck(mqi.hr);
    olecheck(ItfSetBlanket(mqi.Itf,FCoAuthInfo));
    //qr:=mqi.Itf.QueryInterface(IID_IMySendKey,result);
    //olecheck(qr);
    //MySetBlanket(mqi.itf,FCai);
    result:=mqi.itf;
  end

  else
    OleCheck(CoCreateInstance(class_iid^, nil, CLSCTX_INPROC_SERVER or
      CLSCTX_LOCAL_SERVER, itf_iid^, Result));
end;function TRmtComObjectCreater.ItfSetBlanket(var itf: IUnknown; const vCai:
    _CoAuthInfo): HRESULT;
begin
  with vCai do  begin
    result:=CoSetProxyBlanket(Itf,dwAuthnSvc,dwAuthzSvc,pwidechar(pAuthIdentityData^.Domain),
     dwAuthnLevel,dwImpersonationLevel,pAuthIdentityData,dwCapabilities);
  end;
end;procedure TRmtComObjectCreater.SetAuthnLevel(Value: DWORD);
begin
  FAuthnLevel := Value;
end;procedure TRmtComObjectCreater.SetAuthnSvc(Value: DWORD);
begin
  FAuthnSvc := Value;
end;procedure TRmtComObjectCreater.SetComputerName(Value: WideString);
begin
  FComputerName := Value;
end;procedure TRmtComObjectCreater.SetImpersonationLevel(Value: DWORD);
begin
  FImpersonationLevel := Value;
end;procedure TRmtComObjectCreater.SetLogPSW(Value: WideString);
begin
  FLogPSW := Value;
end;procedure TRmtComObjectCreater.SetUserName(Value: WideString);
begin
  FUserName := Value;
end;end.  上述代码中红色部分就是远程激活对象的关键,在这段代码中,我未能发现不正常的增加引用计数的语句。请大家帮忙解决问题。
若能解决,我会将远程发送键盘消息的c/s代码放在BLOG上与大家共议。

解决方案 »

  1.   

    没有这样用过,不过以我所知,COM的配置可能跟你的问题有关:由于COM+为了提高性能,一般会在用户端断开后,再保持COM元件一定的时间,若在此时间范围内无访问,才会清除该对象。你可以检查一下Window->控制面板->组件服务->你的组件的属性,检查一下设置。
      

  2.   

    下面的文章对楼主应该有所参考价值。
    http://dev.21tx.com/2005/06/18/11039.html
      

  3.   

    sz9214e:上述代码我在DCOM环境中调试出现的问题,在COM+的托管环境中,由于资源管理由COM+统管,只要服务对象实现问题,服务对象的资源释放一般不用操心。上述代码在DCOM环境中测试有资源泻漏,如果放在COM+环境中也一样会有泄漏,而不管对象是否被POOLING。
    unsinged:http://dev.21tx.com/2005/06/18/11039.html,该文章系本人以前DCOM应用的整理,上述代码是对这些整理代码的OBJECT封装。我不能确定是这段代码引起了资源泄漏,还是我服务端的问题。如果可能,请大家在客户端做下测试,可将结果反馈给我。谢谢