none
COM+ objects fail to deactivate when created with specified identity. RRS feed

  • Question

  • We have a client program that talks to a COM+ application on a server. On some customers' sites, it is required that the client's computer is not a member of the Windows domain which the server belongs to (they running Novell and refuse absolutely to add these machines to the Windows domain).

    I've added a login utility that attempts to CoCreate a specific object in the COM+ application; if this fails with E_ACCESSDENIED, it prompts the user for domain, username and password, and uses these in all further calls to CoCreateInstanceEx() and CoSetProxyBlanket().

    This is mostly working fine, particularly if the client process is actually running as a legitimate domain user (in which case I prompt for credentials before making any COM+ calls). However, when running as a local user (which is what the customer will be doing), while the CoCreateInstanceEx(), CoSetProxyBlanket() and actual method call work fine, when the object is Release()'d by the client, the numbers reported by Component Services on the server for "Objects" and "Activated" has not gone back down; it seems to take about 20 minutes for the object to be released by the server. Exiting the client process makes no difference.

    I've created a test program; it doesn't seem to matter if CoSetProxyBlanket() is called, or a method call is made on the COM+ object: either way the COM+ object on the server is not released if the client process' identity is not a domain user.

    Any ideas? I've tried a variety of settings when calling CoInitializeSecurity(), and I've tried fiddling with some of the security settings on COM+ application; I've tried with Windows XP as both client and server, Windows 7 as client and Windows 2008 as server.

     

    Cheers,

    Alex

     

    Thursday, May 26, 2011 4:19 PM

Answers

  • As I said, when calling CoCreateInstanceEx(), if you also ask for IUnknown, and call CoSetProxyBlanket() on that, everything works fine:

    HRESULT CreateRemoteInstance(const IID & clsid, REFIID iid, void** ppv)
    {
      MULTI_QI_auto  mqi[2] = { MULTI_QI_auto(&iid), MULTI_QI_auto(&IID_IUnknown) };
    
      HRESULT hr = CoCreateInstanceEx(clsid, NULL, CLSCTX_REMOTE_SERVER | CLSCTX_LOCAL_SERVER, &m_serverinfo,
                      _countof(mqi), mqi);
    
      if ( hr == S_OK )
      {
        for ( unsigned i = 0 ; i < _countof(mqi) ; ++i )
        {
          HRESULT hr2 = S_OK;
          if ( (hr2 = mqi[i].hr) != S_OK
           || (hr2 = SetIdentity(mqi[i].pItf)) != S_OK
            )
          {
            hr = hr2;
          }
        }
      }
    
      if ( hr == S_OK )
      {
        *ppv = mqi[0].Detach();
      }
    
      return hr;
    }
    
    
    

    where SetIdentity() calls CoSetProxyBlanket() with appropriate parameters.

    It would be useful if it were documented that you need to call CoSetProxyBlanket() on IUnknown so that the object can be released properly - or the standard interface proxy code changed so that the default identity is set to that used in CoCreateInstanceEx(), so that CoSetProxyBlanket() need not be called at all.

    Alex

    Monday, June 6, 2011 7:41 AM

All replies

  • Hi  Alex,

     

    Would you please provide us some codes about this issue, like what function you called in your project which it does not work fine.

     

    Best regards,

    Jesse


    Jesse Jiang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Friday, May 27, 2011 6:03 AM
    Moderator
  • Hi.

    Collecting the code from various functions, and stripped right back, this is what I'm doing:

     

        CoInitializeEx(NULL, COINIT_MULTITHREADED);
        CoInitializeSecurity(
            NULL,    // Security descriptor
            -1,     //  Count of services, only for server
            NULL,   //  Services, ditto
            NULL,    // reserved
            RPC_C_AUTHN_LEVEL_CONNECT,
            RPC_C_IMP_LEVEL_IMPERSONATE,
            NULL,        // don't specify auth service/information
            EOAC_NONE,    // Additional capabilities, none in this case
            NULL        // reserved
            );

        COSERVERINFO    serverinfo;
        COAUTHINFO      authinfo;
        COAUTHIDENTITY  identity;
        CComBSTR        domain;
        CComBSTR        username;
        CComBSTR        password;

        HRESULT hr = S_OK;

        memset(&serverinfo, 0, sizeof (COSERVERINFO));
        memset(&authinfo, 0, sizeof (COAUTHINFO));
        memset(&identity, 0, sizeof (COAUTHIDENTITY));

        authinfo.dwAuthnSvc           = RPC_C_AUTHN_WINNT;
        authinfo.dwAuthzSvc           = RPC_C_AUTHZ_NONE;
        authinfo.pwszServerPrincName  = NULL;

        authinfo.dwAuthnLevel         = RPC_C_AUTHN_LEVEL_CONNECT;
        authinfo.dwImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE;
        authinfo.dwCapabilities       = EOAC_NONE;

        identity.User = (USHORT*) username.m_str;
        identity.UserLength = username.Length();
        identity.Domain = (USHORT*) domain.m_str;
        identity.DomainLength = domain.Length();
        identity.Password = (USHORT*) password.m_str;
        identity.PasswordLength = password.Length();
        identity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;

        authinfo.pAuthIdentityData = &identity;

        serverinfo.pAuthInfo = &authinfo;

        {
            MULTI_QI        mqi = { &__uuidof(IFoo) };
            hr = CoCreateInstanceEx(__uuidof(Foo), NULL, CLSCTX_REMOTE_SERVER | CLSCTX_LOCAL_SERVER, &serverinfo, 1, &mqi);
            CComPtr<IFoo>   spFoo;
            if ( mqi.pItf )
            {
                spFoo.Attach(static_cast<IFoo*>(mqi.pItf));
            }
            if ( hr == S_OK )
            {
                hr = CoSetProxyBlanket(spFoo,
                                       authinfo.dwAuthnSvc,
                                       authinfo.dwAuthzSvc,
                                       authinfo.pwszServerPrincName,
                                       authinfo.dwAuthnLevel,
                                       authinfo.dwImpersonationLevel,
                                       authinfo.pAuthIdentityData,
                                       authinfo.dwCapabilities);
            }
        }

        CoUninitialize();

     

    Replace Foo and IFoo with your favourite COM+ object and interface on it. The client machine has the COM+ proxy for the remote COM+ server, which is why serverinfo.pwszName is left blank.

    Note: If the client process's identity is a domain user or a local user with credentials matching a local user on the server, this works fine. If the client process's identity is a local user which doesn't exist on the server, the COM+ object is not de-activated by the server, neither when Release() is called nor when the client process exits.

    Thanks for your help,

    Alex

    Friday, May 27, 2011 8:03 AM
  •  Hi Alex,

     

    Based on my understanding, in this Authentication Level Constants document, the RPC_C_AUTHN_LEVEL_CONNECT means that Authenticates the credentials of the client only when the client establishes a relationship with the server. I think the relationship should be the same account in both client and server, if the account just exist in the client, this level authentication will fail.

     

    I suggest you to use RPC_C_AUTHN_LEVEL_PKT instead of it.

     

    I hope these information can help you.

     

    Best regards,

    Jesse


    Jesse Jiang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, May 30, 2011 3:43 AM
    Moderator
  • This is mostly working fine, particularly if the client process is actually running as a legitimate domain user (in which case I prompt for credentials before making any COM+ calls). However, when running as a local user (which is what the customer will be doing), while the CoCreateInstanceEx(), CoSetProxyBlanket() and actual method call work fine, when the object is Release()'d by the client, the numbers reported by Component Services on the server for "Objects" and "Activated" has not gone back down; it seems to take about 20 minutes for the object to be released by the server. Exiting the client process makes no difference.

    I've created a test program; it doesn't seem to matter if CoSetProxyBlanket() is called, or a method call is made on the COM+ object: either way the COM+ object on the server is not released if the client process' identity is not a domain user.

    This is very interesting. Do you have object pooling enabled in your COM+ settings?

    The time delay is very important. Are you sure it is 20 minutes and not 6 minutes? 6 minutes is the magic number that COM uses to tear down disconnected clients.

     

    Monday, May 30, 2011 4:11 AM
  • Some more information: I'm getting

        First-chance exception at 0x7c812afb (kernel32.dll) in PoolTest.exe: 0x80070005: Access is denied.

    when running my test program under the debugger. Putting a breakpoint at 0x7c812af5 (the instruction before) gives the following stack trace:

         kernel32.dll!_RaiseException@16()  + 0x4c bytes   
         rpcrt4.dll!_RpcpRaiseException@4()  + 0x21 bytes   
         rpcrt4.dll!_NdrProxySendReceive@8()  + 0x187b bytes   
         rpcrt4.dll!_NdrClientCall2()  + 0x9dd bytes   
         rpcrt4.dll!_ObjectStublessClient@8()  + 0x5d bytes   
         rpcrt4.dll!_ObjectStubless@0()  + 0xf bytes   
         ole32.dll!RemoteReleaseRifRefHelper()  + 0x48 bytes   
         ole32.dll!RemoteReleaseRifRef()  + 0x66 bytes   
         ole32.dll!CStdMarshal::DisconnectCliIPIDs()  + 0x1fd bytes   
         ole32.dll!CStdMarshal::Disconnect()  + 0x1f93d bytes   
         ole32.dll!CStdIdentity::~CStdIdentity()  + 0x7a bytes   
         ole32.dll!CStdIdentity::`scalar deleting destructor'()  + 0xd bytes   
         ole32.dll!CStdIdentity::CInternalUnk::Release()  + 0x17c5 bytes   
         rpcrt4.dll!_IUnknown_Release_Proxy@4()  + 0x11 bytes   
         PoolTest.exe!CallAMI(void * lpThreadParameter=0x00000000)  Line 108    C++
         kernel32.dll!_BaseThreadStart@8()  + 0x37 bytes   

    I'm guessing that the Release() call is failing remotely, presumably because it's using the client process's identity, rather than the identity used either when CoCreate'ing the object or when CoSetProxyBlanket() ... is ... called...

    Ok, a light bulb has appeared: if I QI for IUnknown and call CoSetProxyBlanket() on *that*, everything's groovy.

    This does bring me back to the question that's been bugging since shortly after I started on this work: if I call CoCreateInstanceEx() passing the desired identity, why isn't that identity automatically used for all remote calls on all the object's interface proxies? Why must I call CoSetProxyBlanket() at all after passing the desired identity to CoCreateInstanceEx()?

    Alex

    PS Looks like the time delay is indeed 6 minutes, but it's rather eccentric. My test program is creating and releasing one object every second, and tracing the constructor and destructor on the server, I can see the first object destruction after ~380s, and thereafter once every 20s. After a while, the server then starts destroying objects in batches of up to 19. I assume the extra delay after 6 minutes is because there's another 20s timeout, but I don't understand why I don't then see one being destroyed every second. *shrug*

     

    Tuesday, May 31, 2011 9:34 AM
  • As for object pooling, it doesn't seem to make any difference if it's enabled or disabled.

    Alex

     

    Tuesday, May 31, 2011 9:35 AM
  • Hi Alex,

     

    I guess the CoSetProxyBlanket Function released the object already after we called it, so that when we call the release function will fail.

     

    Please see the Remarks section in  CoSetProxyBlanket Function document. It call the release at the end.

     

    Best regards,

    Jesse


    Jesse Jiang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Wednesday, June 1, 2011 3:51 AM
    Moderator
  • Hi Alex,

     

    Would you mind letting me know the result of the suggestions? If you need further assistance, feel free to let me know. I will be more than happy to be of assistance.

     

    Best regards,

    Jesse


    Jesse Jiang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Monday, June 6, 2011 6:56 AM
    Moderator
  • As I said, when calling CoCreateInstanceEx(), if you also ask for IUnknown, and call CoSetProxyBlanket() on that, everything works fine:

    HRESULT CreateRemoteInstance(const IID & clsid, REFIID iid, void** ppv)
    {
      MULTI_QI_auto  mqi[2] = { MULTI_QI_auto(&iid), MULTI_QI_auto(&IID_IUnknown) };
    
      HRESULT hr = CoCreateInstanceEx(clsid, NULL, CLSCTX_REMOTE_SERVER | CLSCTX_LOCAL_SERVER, &m_serverinfo,
                      _countof(mqi), mqi);
    
      if ( hr == S_OK )
      {
        for ( unsigned i = 0 ; i < _countof(mqi) ; ++i )
        {
          HRESULT hr2 = S_OK;
          if ( (hr2 = mqi[i].hr) != S_OK
           || (hr2 = SetIdentity(mqi[i].pItf)) != S_OK
            )
          {
            hr = hr2;
          }
        }
      }
    
      if ( hr == S_OK )
      {
        *ppv = mqi[0].Detach();
      }
    
      return hr;
    }
    
    
    

    where SetIdentity() calls CoSetProxyBlanket() with appropriate parameters.

    It would be useful if it were documented that you need to call CoSetProxyBlanket() on IUnknown so that the object can be released properly - or the standard interface proxy code changed so that the default identity is set to that used in CoCreateInstanceEx(), so that CoSetProxyBlanket() need not be called at all.

    Alex

    Monday, June 6, 2011 7:41 AM
  • Hi Alex,

     

    I'm not clear your point, did you solve this problem already, if so please remember to mark the correct answer. If I misunderstand of you please elaborate question again.

     

    Best regards,

    Jesse


    Jesse Jiang [MSFT]
    MSDN Community Support | Feedback to us
    Get or Request Code Sample from Microsoft
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    Tuesday, June 7, 2011 7:53 AM
    Moderator