text: /********************************************************************
  
  Copyright (c) 1999 Microsoft Corporation
  
  Module Name:
      Slave.cpp
  
  Abstract:
      File for Implementation of CPCHMasterSlave and CPCHUserProcess classes,
      used to establish a connection between the service and a slave process.
  
  Revision History:
      Davide Massarenti created  03/28/2000
  
  ********************************************************************/
  
  #include "stdafx.h"
  
  #include <UserEnv.h>
  
  /////////////////////////////////////////////////////////////////////////////
  
  static const WCHAR c_HelpHost[] = HC_ROOT_HELPSVC_BINARIES L"\\HelpHost.exe";
  static const DWORD c_Timeout = 100 * 1000;
  
  /////////////////////////////////////////////////////////////////////////////
  
  CPCHUserProcess::UserEntry::UserEntry()
  {
                             // CComBSTR                  m_bstrUser
      m_dwSessionID = 0;     // DWORD                     m_dwSessionID;
                             //
                             // CComBSTR                  m_bstrVendorID;
                             // CComBSTR                  m_bstrPublicKey;
                             //
                             // GUID                      m_guid;
                             // CComPtr<IPCHSlaveProcess> m_spConnection;
      m_hToken      = NULL;  // HANDLE                    m_hToken;
      m_hProcess    = NULL;  // HANDLE                    m_hProcess;
      m_phEvent     = NULL;  // HANDLE*                   m_phEvent;
  
      ::ZeroMemory( &m_guid, sizeof(m_guid) );
  }
  
  CPCHUserProcess::UserEntry::~UserEntry()
  {
      Cleanup();
  }
  
  void CPCHUserProcess::UserEntry::Cleanup()
  {
      m_bstrUser     .Empty  ();
      m_dwSessionID = 0;
  
      m_bstrVendorID .Empty  ();
      m_bstrPublicKey.Empty  ();
  
      m_spConnection .Release();
  
      if(m_hProcess)
      {
          ::CloseHandle( m_hProcess ); m_hProcess = NULL;
      }
  
      if(m_hToken)
      {
          ::CloseHandle( m_hToken ); m_hToken = NULL;
      }
  }
  
  ////////////////////
  
  bool CPCHUserProcess::UserEntry::operator==( /*[in]*/ const UserEntry& ue ) const
  {
      if(ue.m_bstrUser)
      {
          if(m_bstrUser == ue.m_bstrUser && m_dwSessionID == ue.m_dwSessionID) return true;
      }
  
      if(ue.m_bstrVendorID)
      {
          if(MPC::StrICmp( m_bstrVendorID, ue.m_bstrVendorID ) == 0) return true;
      }
  
      return false;
  }
  
  bool CPCHUserProcess::UserEntry::operator==( /*[in]*/ const GUID& guid ) const
  {
      return ::IsEqualGUID( m_guid, guid ) ? true : false;
  }
  
  ////////////////////
  
  HRESULT CPCHUserProcess::UserEntry::Clone( /*[in]*/ const UserEntry& ue )
  {
      __HCP_FUNC_ENTRY( "CPCHUserProcess::UserEntry::Clone" );
  
      HRESULT hr;
  
      Cleanup();
  
      m_bstrUser      = ue.m_bstrUser;      // CComBSTR                  m_bstrUser;
      m_dwSessionID   = ue.m_dwSessionID;   // DWORD                     m_dwSessionID;
                                            //
      m_bstrVendorID  = ue.m_bstrVendorID;  // CComBSTR                  m_bstrVendorID;
      m_bstrPublicKey = ue.m_bstrPublicKey; // CComBSTR                  m_bstrPublicKey;
                                            //
                                            // GUID                      m_guid;          // Used for establishing the connection.
                                            // CComPtr<IPCHSlaveProcess> m_spConnection;  // Live object.
                                            // HANDLE                    m_hToken;        // User token.
                                            // HANDLE                    m_hProcess;      // Process handle.
                                            // HANDLE*                   m_phEvent;       // To notify activator.
  
      if(ue.m_hToken)
      {
          __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::DuplicateTokenEx( ue.m_hToken, 0, NULL, SecurityImpersonation, TokenPrimary, &m_hToken ));
      }
  
      hr = S_OK;
  
  
      __HCP_FUNC_CLEANUP;
  
      __HCP_FUNC_EXIT(hr);
  }
  
  HRESULT CPCHUserProcess::UserEntry::Connect( /*[out]*/ HANDLE& hEvent )
  {
      __HCP_FUNC_ENTRY( "CPCHUserProcess::UserEntry::Connect" );
  
      HRESULT hr;
  
  
      //
      // Logon the user, if not already done.
      //
      if(m_hToken == NULL)
      {
          CPCHAccounts acc;
  		LPCWSTR      szUser = SAFEBSTR( m_bstrUser );
  
  ////    // DEBUG
  ////    // DEBUG
  ////    // DEBUG
  ////    __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::OpenProcessToken( ::GetCurrentProcess(), TOKEN_ALL_ACCESS, &m_hToken ));
  
  		//
  		// Only keep the account enabled for the time it takes to create its token.
  		//
  		__MPC_EXIT_IF_METHOD_FAILS(hr, acc.ChangeUserStatus( szUser, /*fEnable*/true  ));
          __MPC_EXIT_IF_METHOD_FAILS(hr, acc.LogonUser       ( szUser, NULL, m_hToken   ));
  		__MPC_EXIT_IF_METHOD_FAILS(hr, acc.ChangeUserStatus( szUser, /*fEnable*/false ));
      }
  
      if(m_hProcess     == NULL ||
         m_spConnection == NULL  )
      {
          __MPC_EXIT_IF_METHOD_FAILS(hr, SendActivation( hEvent ));
      }
  
      hr = S_OK;
  
  
      __HCP_FUNC_CLEANUP;
  
      __HCP_FUNC_EXIT(hr);
  }
  
  HRESULT CPCHUserProcess::UserEntry::SendActivation( /*[out]*/ HANDLE& hEvent )
  {
      __HCP_FUNC_ENTRY( "CPCHUserProcess::UserEntry::SendActivation" );
  
      HRESULT             hr;
      DWORD               dwRes;
      PROCESS_INFORMATION piProcessInformation;
      STARTUPINFOW        siStartupInfo;
      VOID*               pEnvBlock = NULL;
      MPC::wstring        strExe( c_HelpHost ); MPC::SubstituteEnvVariables( strExe );
      WCHAR               rgCommandLine[1024];
  
      ::ZeroMemory( (PVOID)&piProcessInformation, sizeof( piProcessInformation ) );
      ::ZeroMemory( (PVOID)&siStartupInfo       , sizeof( siStartupInfo        ) ); siStartupInfo.cb = sizeof( siStartupInfo );
  
  
      //
      // Generate random ID.
      //
      __MPC_EXIT_IF_METHOD_FAILS(hr, ::CoCreateGuid( &m_guid ));
  
  
      //
      // Create event.
      //
      __MPC_EXIT_IF_CALL_RETURNS_NULL(hr, (hEvent = ::CreateEvent( NULL, FALSE, FALSE, NULL )));
  
      //
      // Create process as user.
      //
      {
          CComBSTR bstrGUID( m_guid );
  
          swprintf( rgCommandLine, L"\"%s\" -guid %s", strExe.c_str(), (BSTR)bstrGUID );
      }
  
  	__MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::CreateEnvironmentBlock( &pEnvBlock, m_hToken, TRUE ));
  
  ////    // DEBUG
  ////    // DEBUG
  ////    // DEBUG
  ////    __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::CreateProcessW( NULL,
  ////                                                           rgCommandLine,
  ////                                                           NULL,
  ////                                                           NULL,
  ////                                                           FALSE,
  ////                                                           NORMAL_PRIORITY_CLASS,
  ////                                                           NULL,
  ////                                                           NULL,
  ////                                                           &siStartupInfo,
  ////                                                           &piProcessInformation ));
  
      // REAL
      // REAL
      // REAL
      __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::CreateProcessAsUserW( m_hToken                                           ,
                                                                   NULL                                               ,
                                                                   rgCommandLine                                      ,
                                                                   NULL                                               ,
                                                                   NULL                                               ,
                                                                   FALSE                                              ,
                                                                   NORMAL_PRIORITY_CLASS | CREATE_UNICODE_ENVIRONMENT ,
                                                                   pEnvBlock                                          ,
                                                                   NULL                                               ,
                                                                   &siStartupInfo                                     ,
                                                                   &piProcessInformation                              ));
  
      m_hProcess = piProcessInformation.hProcess; piProcessInformation.hProcess = NULL;
      hr         = S_OK;
  
  
      __HCP_FUNC_CLEANUP;
  
  	if(pEnvBlock) ::DestroyEnvironmentBlock( pEnvBlock );
  
      if(piProcessInformation.hProcess) ::CloseHandle( piProcessInformation.hProcess );
      if(piProcessInformation.hThread ) ::CloseHandle( piProcessInformation.hThread  );
  
      __HCP_FUNC_EXIT(hr);
  }
  
  ////////////////////
  
  HRESULT CPCHUserProcess::UserEntry::InitializeForVendorAccount( /*[in]*/ BSTR bstrUser      ,
                                                                  /*[in]*/ BSTR bstrVendorID  ,
                                                                  /*[in]*/ BSTR bstrPublicKey )
  {
      __HCP_FUNC_ENTRY( "CPCHUserProcess::UserEntry::InitializeForVendorAccount" );
  
      HRESULT hr;
  
      Cleanup();
  
      m_bstrUser      = bstrUser;
  
      m_bstrVendorID  = bstrVendorID;
      m_bstrPublicKey = bstrPublicKey;
  
      hr = S_OK;
  
  
      __HCP_FUNC_EXIT(hr);
  }
  
  HRESULT CPCHUserProcess::UserEntry::InitializeForImpersonation( /*[in]*/ HANDLE hToken )
  {
      __HCP_FUNC_ENTRY( "CPCHUserProcess::UserEntry::InitializeForImpersonation" );
  
      HRESULT            hr;
      MPC::Impersonation imp;
      MPC::wstring       strUser;
      DWORD              dwSize;
      PSID               pUserSid = NULL;
  
  
      if(hToken == NULL)
      {
          __MPC_EXIT_IF_METHOD_FAILS(hr, imp.Initialize( MAXIMUM_ALLOWED ));
  
          hToken = imp;
      }
  
      __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::DuplicateTokenEx( hToken, 0, NULL, SecurityImpersonation, TokenPrimary, &m_hToken ));
  
      __MPC_EXIT_IF_CALL_RETURNS_FALSE(hr, ::GetTokenInformation( m_hToken, TokenSessionId, &m_dwSessionID, sizeof(m_dwSessionID), &dwSize ));
  
      __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::SecurityDescriptor::GetTokenSids         ( m_hToken, &pUserSid, NULL    ));
      __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::SecurityDescriptor::ConvertSIDToPrincipal(            pUserSid, strUser )); m_bstrUser = strUser.c_str();
  
  
      hr = S_OK;
  
  
      __HCP_FUNC_CLEANUP;
  
      MPC::SecurityDescriptor::ReleaseMemory( pUserSid );
  
      __HCP_FUNC_EXIT(hr);
  }
  
  /////////////////////////////////////////////////////////////////////////////
  /////////////////////////////////////////////////////////////////////////////
  
  CPCHUserProcess::CPCHUserProcess()
  {
      // List m_lst;
  
      (void)MPC::_MPC_Module.RegisterCallback( this, (void (CPCHUserProcess::*)())Shutdown );
  }
  
  CPCHUserProcess::~CPCHUserProcess()
  {
      MPC::CallDestructorForAll( m_lst );
  
      MPC::_MPC_Module.UnregisterCallback( this );
  }
  
  ////////////////////
  
  CPCHUserProcess* CPCHUserProcess::s_GLOBAL( NULL );
  
  HRESULT CPCHUserProcess::InitializeSystem()
  {
      if(s_GLOBAL == NULL)
      {
          s_GLOBAL = new CPCHUserProcess;
      }
  
      return s_GLOBAL ? S_OK : E_OUTOFMEMORY;
  }
  
  void CPCHUserProcess::FinalizeSystem()
  {
      if(s_GLOBAL)
      {
          delete s_GLOBAL; s_GLOBAL = NULL;
      }
  }
  
  ////////////////////////////////////////////////////////////////////////////////
  
  void CPCHUserProcess::Shutdown()
  {
      __HCP_FUNC_ENTRY( "CPCHUserProcess::Shutdown" );
  
      MPC::SmartLock<_ThreadModel> lock( this );
  
  
      m_lst.clear();
  }
  
  
  CPCHUserProcess::UserEntry* CPCHUserProcess::Lookup( /*[in]*/ const UserEntry& ue, /*[in]*/ bool fRelease )
  {
      UserEntry* ueReal;
  
      //
      // Locate vendor and connect to it.
      //
      for(Iter it = m_lst.begin(); it != m_lst.end(); )
      {
          ueReal = *it;
          if(ueReal && *ueReal == ue)
          {
              if(fRelease)
              {
                  delete ueReal;
  
                  m_lst.erase( it++ ); continue;
              }
              else
              {
                  return ueReal;
              }
          }
  
          it++;
      }
  
      return NULL;
  }
  
  HRESULT CPCHUserProcess::Remove( /*[in]*/ const UserEntry& ue )
  {
      __HCP_FUNC_ENTRY( "CPCHUserProcess::Remove" );
  
      HRESULT                      hr;
      MPC::SmartLock<_ThreadModel> lock( this );
  
  
      (void)Lookup( ue, /*fRelease*/true );
  
  
      hr = S_OK;
  
  
      __HCP_FUNC_EXIT(hr);
  }
  
  HRESULT CPCHUserProcess::Connect( /*[in ]*/ const UserEntry&   ue           ,
                                    /*[out]*/ IPCHSlaveProcess* *spConnection )
  {
      __HCP_FUNC_ENTRY( "CPCHUserProcess::Connect" );
  
      HRESULT                      hr;
      MPC::SmartLock<_ThreadModel> lock( this );
      HANDLE                       hEvent = NULL;
      UserEntry*                   ueReal = NULL;
  
  	DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHUserProcess::Connect" );
  
      //
      // Locate vendor and connect to it.
      //
      ueReal = Lookup( ue, /*fRelease*/false );
      if(ueReal == NULL)
      {
          __MPC_EXIT_IF_ALLOC_FAILS(hr, ueReal, new UserEntry);
          m_lst.push_back( ueReal );
  
          __MPC_EXIT_IF_METHOD_FAILS(hr, ueReal->Clone( ue ));
      }
  
      __MPC_EXIT_IF_METHOD_FAILS(hr, ueReal->Connect( hEvent ));
  
  
      //
      // If "Connect" returns an event handle, wait on it.
      //
      if(hEvent)
      {
          ueReal->m_phEvent = &hEvent; // For waiting response...
  
          lock = NULL;
  
          if(::WaitForSingleObject( hEvent, c_Timeout ) != WAIT_OBJECT_0)
          {
              __MPC_SET_ERROR_AND_EXIT(hr, E_FAIL);
          }
  
          lock = this;
  
          //
          // Relocate vendor (we release the lock on the object, so "ueReal" is not valid anymore.
          //
          ueReal = Lookup( ue, /*fRelease*/false );
          if(ueReal == NULL)
          {
              __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_FILE_NOT_FOUND);
          }
      }
  
      __MPC_EXIT_IF_METHOD_FAILS(hr, ueReal->m_spConnection.QueryInterface( spConnection ));
  
      hr = S_OK;
  
  
      __HCP_FUNC_CLEANUP;
  
      if(hEvent) ::CloseHandle( hEvent );
  
  	DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHUserProcess::Connect - done" );
  
      __HCP_FUNC_EXIT(hr);
  }
  
  HRESULT CPCHUserProcess::RegisterHost( /*[in]*/ BSTR              bstrID ,
                                         /*[in]*/ IPCHSlaveProcess* pObj   )
  {
      __HCP_FUNC_ENTRY( "CPCHUserProcess::RegisterHost" );
  
      HRESULT                      hr;
      MPC::SmartLock<_ThreadModel> lock( this );
      UserEntry*                   ueReal = NULL;
      GUID                         guid;
  
  
      //
      // Validate input.
      //
      __MPC_EXIT_IF_METHOD_FAILS(hr, ::CLSIDFromString( bstrID, &guid ));
  
  
      //
      // Locate vendor and connect to it.
      //
      for(Iter it = m_lst.begin(); it != m_lst.end(); it++)
      {
          ueReal = *it;
          if(ueReal && *ueReal == guid)
          {
              break;
          }
      }
      if(ueReal == NULL)
      {
          __MPC_SET_WIN32_ERROR_AND_EXIT(hr, ERROR_FILE_NOT_FOUND);
      }
  
      __MPC_EXIT_IF_METHOD_FAILS(hr, pObj->Initialize( ueReal->m_bstrVendorID, ueReal->m_bstrPublicKey ));
  
      ueReal->m_spConnection = pObj;
  
      hr = S_OK;
  
  
      __HCP_FUNC_CLEANUP;
  
      if(ueReal)
      {
          if(ueReal->m_phEvent)
          {
              //
              // Signal the event handle, to awake the activator.
              //
              ::SetEvent( *(ueReal->m_phEvent) );
  
              ueReal->m_phEvent = NULL;
          }
      }
  
      __HCP_FUNC_EXIT(hr);
  }
  
  ////////////////////////////////////////////////////////////////////////////////
  ////////////////////////////////////////////////////////////////////////////////
  
  HRESULT CPCHUserProcess::SendResponse( /*[in]*/ DWORD    dwArgc   ,
                                         /*[in]*/ LPCWSTR* lpszArgv )
  {
      __HCP_FUNC_ENTRY( "CPCHUserProcess::SendResponse" );
  
      HRESULT                   hr;
      int                       i;
      CComBSTR                  bstrGUID;
      CComPtr<IPCHService>      srv;
      CComPtr<CPCHSlaveProcess> obj;
  #ifdef DEBUG
      bool                      fDebug = false;
  #endif
  
  
      //
      // Parse the arguments.
      //
      for(i=1; i<dwArgc; i++)
      {
          LPCWSTR szArg = lpszArgv[i];
  
          if(szArg[0] == '-' ||
             szArg[0] == '/'  )
          {
              szArg++;
  
              if(_wcsicmp( szArg, L"guid" ) == 0 && (i<dwArgc-1))
              {
                  bstrGUID = lpszArgv[++i];
                  continue;
              }
  
  #ifdef DEBUG
              if(_wcsicmp( szArg, L"debug" ) == 0)
              {
                  fDebug = true;
                  continue;
              }
  #endif
          }
  
          __MPC_SET_ERROR_AND_EXIT(hr, E_FAIL);
      }
  
  #ifdef DEBUG
      if(fDebug) DebugBreak();
  #endif
  
      //
      // Create the COM object.
      //
      __MPC_EXIT_IF_METHOD_FAILS(hr, MPC::CreateInstance( &obj ));
  
      //
      // Register it with the service.
      //
      __MPC_EXIT_IF_METHOD_FAILS(hr, ::CoCreateInstance( CLSID_PCHService, NULL, CLSCTX_ALL, IID_IPCHService, (void**)&srv ));
  
      __MPC_EXIT_IF_METHOD_FAILS(hr, srv->RegisterHost( bstrGUID, obj ));
  
  
      hr = S_OK;
  
  
      __HCP_FUNC_CLEANUP;
  
      __HCP_FUNC_EXIT(hr);
  }
  
  //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  CPCHSlaveProcess::CPCHSlaveProcess()
  {
      						 // CComBSTR                    m_bstrVendorID;
      						 // CComBSTR                    m_bstrPublicKey;
  	m_ScriptLauncher = NULL; // CPCHScriptWrapper_Launcher* m_ScriptLauncher;
  }
  
  CPCHSlaveProcess::~CPCHSlaveProcess()
  {
  	delete m_ScriptLauncher;
  }
  
  HRESULT CPCHSlaveProcess::Initialize( /*[in]*/ BSTR bstrVendorID, /*[in]*/ BSTR bstrPublicKey )
  {
      m_bstrVendorID  = bstrVendorID;
      m_bstrPublicKey = bstrPublicKey;
  
      return S_OK;
  }
  
  HRESULT CPCHSlaveProcess::CreateInstance( /*[in ]*/ REFCLSID   rclsid    ,
                                            /*[in ]*/ IUnknown*  pUnkOuter ,
                                            /*[out]*/ IUnknown* *ppvObject )
  {
  	HRESULT hr;
  
  	DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::CreateInstance" );
  
      hr = ::CoCreateInstance( rclsid, pUnkOuter, CLSCTX_ALL, IID_IUnknown, (void**)ppvObject );
  
  	DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::CreateInstance - done" );
  
  	return hr;
  }
  
  HRESULT CPCHSlaveProcess::CreateScriptWrapper( /*[in ]*/ REFCLSID   rclsid   ,
                                                 /*[in ]*/ BSTR       bstrCode ,
                                                 /*[in ]*/ BSTR       bstrURL  ,
                                                 /*[out]*/ IUnknown* *ppObj    )
  {
      __HCP_FUNC_ENTRY( "CPCHSlaveProcess::CreateScriptWrapper" );
  
      HRESULT                      hr;
      MPC::SmartLock<_ThreadModel> lock( this );
  
  	if(!m_ScriptLauncher)
  	{
  		__MPC_EXIT_IF_ALLOC_FAILS(hr, m_ScriptLauncher, new CPCHScriptWrapper_Launcher);
  	}
  
  	__MPC_EXIT_IF_METHOD_FAILS(hr, m_ScriptLauncher->CreateScriptWrapper( rclsid, bstrCode, bstrURL, ppObj ));
  
      hr = S_OK;
  
  
      __HCP_FUNC_CLEANUP;
  
      __HCP_FUNC_EXIT(hr);
  }
  
  HRESULT CPCHSlaveProcess::OpenBlockingStream( /*[in ]*/ BSTR       bstrURL   ,
                                                /*[out]*/ IUnknown* *ppvObject )
  {
      __HCP_FUNC_ENTRY( "CPCHSlaveProcess::OpenBlockingStream" );
  
      HRESULT          hr;
      CComPtr<IStream> stream;
  
  
  	DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::OpenBlockingStream" );
  
      __MPC_EXIT_IF_METHOD_FAILS(hr, URLOpenBlockingStreamW( NULL, bstrURL, &stream, 0, NULL ));
  
      __MPC_EXIT_IF_METHOD_FAILS(hr, stream.QueryInterface( ppvObject ));
  
      hr = S_OK;
  
  
      __HCP_FUNC_CLEANUP;
  
  	DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::OpenBlockingStream - done" );
  
      __HCP_FUNC_EXIT(hr);
  }
  
  HRESULT CPCHSlaveProcess::IsNetworkAlive( /*[out]*/ VARIANT_BOOL* pfRetVal )
  {
      __HCP_FUNC_ENTRY( "CPCHSlaveProcess::IsNetworkAlive" );
  
      HRESULT hr;
  
      __MPC_PARAMCHECK_BEGIN(hr)
          __MPC_PARAMCHECK_POINTER_AND_SET(pfRetVal,VARIANT_FALSE);
      __MPC_PARAMCHECK_END();
  
  	DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::IsNetworkAlive" );
  
      if(SUCCEEDED(MPC::Connectivity::NetworkAlive( HC_TIMEOUT_CONNECTIONCHECK )))
      {
          *pfRetVal = VARIANT_TRUE;
      }
  
      hr = S_OK;
  
  
      __HCP_FUNC_CLEANUP;
  
  	DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::IsNetworkAlive - done" );
  
      __HCP_FUNC_EXIT(hr);
  }
  
  HRESULT CPCHSlaveProcess::IsDestinationReachable( /*[in ]*/ BSTR bstrDestination, /*[out]*/ VARIANT_BOOL *pfRetVal )
  {
      __HCP_FUNC_ENTRY( "CPCHSlaveProcess::IsDestinationReachable" );
  
      HRESULT hr;
  
      __MPC_PARAMCHECK_BEGIN(hr)
          __MPC_PARAMCHECK_STRING_NOT_EMPTY(bstrDestination);
          __MPC_PARAMCHECK_POINTER_AND_SET(pfRetVal,VARIANT_FALSE);
      __MPC_PARAMCHECK_END();
  
  	DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::IsDestinationReachable" );
  
      if(SUCCEEDED(MPC::Connectivity::DestinationReachable( bstrDestination, HC_TIMEOUT_CONNECTIONCHECK )))
      {
          *pfRetVal = VARIANT_TRUE;
      }
  
      hr = S_OK;
  
  
      __HCP_FUNC_CLEANUP;
  
  	DEBUG_AppendPerf( DEBUG_PERF_HELPHOST, "CPCHSlaveProcess::IsDestinationReachable - done" );
  
      __HCP_FUNC_EXIT(hr);
  }
  
  