//////////////////////////////////////////////////////////////////////////////
//
//  Copyright (c) 1999-2000 Microsoft Corporation
//
//  Module Name:
//      ScriptResource.cpp
//
//  Description:
//      CScriptResource class implementation.
//
//  Maintained By:
//      gpease 14-DEC-1999
//
//////////////////////////////////////////////////////////////////////////////

#include "pch.h"
#include <clusudef.h>
#include "ActiveScriptSite.h"
#include "ScriptResource.h"
#include "SpinLock.h"
#include "clusrtl.h"

DEFINE_THISCLASS("CScriptResource")

//
//  KB:  gpease  08-FEB-2000
//
//  The Generic Scripting Resource uses a separate working thread to do all
//  calls into the Script. This is because the Scripting Host Engines require
//  only the creating thread to call them (remember, scripting is designed
//  to be used in a user mode application where usually the UI thread runs
//  the script). To make this possible, we serialize the threads entering the
//  the script using a user-mode spinlock (m_lockSerialize). We then use two events
//  to signal the "worker thread" (m_EventWait) and to signal when the "worker 
//  thread" has completed the task (m_EventDone).
//
//  LooksAlive is implemented by returning the last result of a LooksAlive. It
//  will start the "worker thread" doing the LooksAlive, but not wait for the
//  thread to return the result. Because of this, all the other threads must
//  make sure that the "Done Event" (m_EventDone) is signalled before writing
//  into the common buffers (m_msg and m_hr).
//

//////////////////////////////////////////////////////////////////////////////
//
//  LPUNKNOWN
//  CScriptResource_CreateInstance( void )
//
//  Description:
//      Creates an intialized instance of CScriptResource.
//
//  Arguments:
//      None.
//
//  Return Values:
//      NULL    - Failure to create or initialize.
//      valid pointer to a CScriptResource.
//
//////////////////////////////////////////////////////////////////////////////
CScriptResource *
CScriptResource_CreateInstance( 
    LPCWSTR pszNameIn, 
    HKEY hkeyIn, 
    RESOURCE_HANDLE hResourceIn
    )
{
    TraceFunc( "CScriptResource_CreateInstance( )\n" );

    CScriptResource * lpcc = new CScriptResource( );
    if ( lpcc != NULL )
    {
        HRESULT hr = THR( lpcc->Init( pszNameIn, hkeyIn, hResourceIn ) );
        if ( SUCCEEDED( hr ) )
        {
            RETURN( lpcc );
        } // if: success

        delete lpcc;

    } // if: got object

    RETURN(NULL);
} //*** CScriptResource_CreateInstance( )

//////////////////////////////////////////////////////////////////////////////
//
//  Constructor
//
//////////////////////////////////////////////////////////////////////////////
CScriptResource::CScriptResource( ) :
    m_dispidOpen(DISPID_UNKNOWN),
    m_dispidClose(DISPID_UNKNOWN),
    m_dispidOnline(DISPID_UNKNOWN),
    m_dispidOffline(DISPID_UNKNOWN),
    m_dispidTerminate(DISPID_UNKNOWN),
    m_dispidLooksAlive(DISPID_UNKNOWN),
    m_dispidIsAlive(DISPID_UNKNOWN)
{
    TraceClsFunc1( "%s( )\n", __THISCLASS__ );
    Assert( m_cRef == 0 );

    TraceFuncExit( );
} //*** constructor

//////////////////////////////////////////////////////////////////////////////
//
//  Destructor
//
//////////////////////////////////////////////////////////////////////////////
CScriptResource::~CScriptResource( )
{
    TraceClsFunc1( "~%s( )\n", __THISCLASS__ );

    HRESULT hr;

    CSpinLock SpinLock( &m_lockSerialize, INFINITE );

    //
    // Make sure no one else has this lock.... else why are we going away?
    //
    hr = SpinLock.AcquireLock( );
    Assert( hr == S_OK );

    //
    //  Kill the worker thread.
    //
    if ( m_hThread != NULL )
    {
        //  Tell it to DIE
        m_msg = msgDIE;

        //  Signal the event.
        SetEvent( m_hEventWait );

        //  Wait for it to happen. This shouldn't take long at all.
        WaitForSingleObject( m_hThread, 30000 );    // 30 seconds

        //  Cleanup the handle.
        CloseHandle( m_hThread );
    }

    if ( m_hEventDone != NULL )
    {
        CloseHandle( m_hEventDone );
    }

    if ( m_hEventWait != NULL )
    {
        CloseHandle( m_hEventWait );
    }
    
    if ( m_pszName != NULL )
    {
        TraceFree( m_pszName );
    } // if: m_pszName

    if ( m_hkeyParams != NULL )
    {
        ClusterRegCloseKey( m_hkeyParams );
    } // if: m_hkeyParams

#if defined(DEBUG)
    //
    // Make the debug build happy. Not needed in RETAIL.
    //
    SpinLock.ReleaseLock( );
#endif // defined(DEBUG)

    TraceFuncExit( );

} //*** destructor

//////////////////////////////////////////////////////////////////////////////
//
//  HRESULT
//  CScriptResource::Init( 
//      LPCWSTR             pszNameIn,
//      HKEY                hkeyIn,
//      RESOURCE_HANDLE     hResourceIn
//      )
//
//  Description:
//      Initializes the class.
//
//  Arguments:
//      pszNameIn   - Name of resource instance.
//      hkeyIn      - The cluster key root for this resource instance.
//      hResourceIn - The hResource for this instance.
//
//  Return Value:
//      S_OK -
//          Success.
//      HRESULT_FROM_WIN32( ) error - 
//          if Win32 call failed.
//      E_OUTOFMEMORY - 
//          Out of memory.
//      other HRESULT errors.
//
//////////////////////////////////////////////////////////////////////////////

HRESULT
CScriptResource::Init( 
    LPCWSTR             pszNameIn,
    HKEY                hkeyIn,
    RESOURCE_HANDLE     hResourceIn
    )
{
    TraceClsFunc1( "Init( pszNameIn = '%s' )\n", pszNameIn );

    DWORD   dwErr;

    HRESULT hr = S_OK;

    // IUnknown
    AddRef( );

    // Other
    m_hResource = hResourceIn;
    Assert( m_pszName == NULL );
    Assert( m_pszScriptFilePath == NULL );
    Assert( m_pszScriptEngine == NULL );
    Assert( m_hEventWait == NULL );
    Assert( m_hEventDone == NULL );
    Assert( m_lockSerialize == FALSE );

    //
    // Create some event to wait on.
    //

    // scripting engine thread wait event
    m_hEventWait = CreateEvent( NULL, TRUE, FALSE, NULL );
    if ( m_hEventWait == NULL )
        goto Win32Error;

    // task completion event
    m_hEventDone = CreateEvent( NULL, TRUE, FALSE, NULL );
    if ( m_hEventDone == NULL )
        goto Win32Error;

    //
    // Copy the resource name.
    //

    m_pszName = TraceStrDup( pszNameIn );
    if ( m_pszName == NULL )
        goto OutOfMemory;

    //
    // Open the parameters key.
    //

    dwErr = ClusterRegOpenKey( hkeyIn, L"Parameters", KEY_ALL_ACCESS, &m_hkeyParams );
    if ( dwErr != ERROR_SUCCESS )
    {
        TW32( dwErr );
        hr = HRESULT_FROM_WIN32( dwErr );
        goto Error;
    } // if: failed

    //
    // Create the scripting engine thread.
    //

    m_hThread = CreateThread( NULL,
                              0,
                              &S_ThreadProc,
                              this,
                              0,
                              &m_dwThreadId
                              );
    if ( m_hThread == NULL )
        goto Win32Error;

Cleanup:
    //
    // All class variable clean up should be done in the destructor.
    //
    HRETURN( hr );

Error:
    LogError( hr );
    goto Cleanup;

OutOfMemory:
    hr = E_OUTOFMEMORY;
    goto Error;

Win32Error:
    dwErr = GetLastError( );
    TW32( dwErr );
    hr = HRESULT_FROM_WIN32( dwErr );
    goto Error;

} //*** Init( )

//****************************************************************************
//
//  IUnknown
//
//****************************************************************************


//////////////////////////////////////////////////////////////////////////////
//
//  STDMETHODIMP
//  CScriptResource::[IUnknown] QueryInterface(
//      REFIID      riid,
//      LPVOID *    ppv
//      )
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CScriptResource::QueryInterface(
    REFIID      riid,
    LPVOID *    ppv
    )
{
    TraceClsFunc1( "[IUnknown] QueryInterface( riid, ppv = 0x%08x )\n",                   
                   ppv
                   );

    HRESULT hr = E_NOINTERFACE;

    if ( IsEqualIID( riid, IID_IUnknown ) )
    {
        *ppv = TraceInterface( __THISCLASS__, IUnknown, static_cast< IUnknown* >( this ), 0 );
        hr   = S_OK;
    } // if: IUnknown

    if ( SUCCEEDED( hr ) )
    {
        ((IUnknown *) *ppv)->AddRef( );
    } // if: success

    QIRETURN( hr, riid );

} //*** QueryInterface( )

//////////////////////////////////////////////////////////////////////////////
//
//  STDMETHODIMP_( ULONG )
//  CScriptResource::[IUnknown] AddRef( void )
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_( ULONG )
CScriptResource::AddRef( void )
{
    TraceClsFunc( "[IUnknown] AddRef( )\n" );

    LONG cRef = InterlockedIncrement( &m_cRef );

    RETURN( cRef );

} //*** AddRef( )

//////////////////////////////////////////////////////////////////////////////
//
//  STDMETHODIMP_( ULONG )
//  CScriptResource::[IUnknown] Release( void )
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP_( ULONG )
CScriptResource::Release( void )
{
    TraceClsFunc( "[IUnknown] Release( )\n" );

    LONG cRef = InterlockedDecrement( &m_cRef );

    if ( cRef == 0 )
    {
        TraceDo( delete this );
    } // if: reference count decremented to zero

    RETURN( cRef );

} //*** Release( )


//****************************************************************************
//
//  Publics
//
//****************************************************************************


//////////////////////////////////////////////////////////////////////////////
//
//  STDMETHODIMP
//  CScriptResource::Close( 
//  )
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CScriptResource::Close( 
    )
{
    TraceClsFunc( "Close( )\n" );

    HRESULT hr;

    hr = THR( WaitForMessageToComplete( msgCLOSE ) );

    HRETURN( hr );

} //*** Close( )

//////////////////////////////////////////////////////////////////////////////
//
//  STDMETHODIMP
//  CScriptResource::Open(
//      )
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CScriptResource::Open(
    )
{
    TraceClsFunc( "Open( )\n" );

    HRESULT hr;

    hr = THR( WaitForMessageToComplete( msgOPEN ) );

    // CMCM:+ 19-Dec-2000 commented this out to make the DBG PRINT quiet since we now return ERROR_RETRY
    // HRETURN( hr ); 
    return hr;

} //*** Open( )

//////////////////////////////////////////////////////////////////////////////
//
//  STDMETHODIMP
//  CScriptResource::Online(
//      )
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CScriptResource::Online(
    )
{
    TraceClsFunc( "Online( )\n" );

    HRESULT hr;

    hr = THR( WaitForMessageToComplete( msgONLINE ) );

    HRETURN( hr );
} //*** Online( )

//////////////////////////////////////////////////////////////////////////////
//
//  STDMETHODIMP
//  CScriptResource::Offline(
//      )
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CScriptResource::Offline(
    )
{
    TraceClsFunc( "Offline( )\n" );

    HRESULT hr;

    hr = THR( WaitForMessageToComplete( msgOFFLINE ) );

    HRETURN( hr );
} //*** Offline( )

//////////////////////////////////////////////////////////////////////////////
//
//  STDMETHODIMP
//  CScriptResource::Terminate(
//      )
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CScriptResource::Terminate(
    )
{
    TraceClsFunc( "Terminate( )\n" );

    HRESULT hr;

    hr = THR( WaitForMessageToComplete( msgTERMINATE ) );

    HRETURN( hr );
} //*** Terminate( )

//////////////////////////////////////////////////////////////////////////////
//
//  STDMETHODIMP
//  CScriptResource::LooksAlive(
//      )
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CScriptResource::LooksAlive(
    )
{
    TraceClsFunc( "LooksAlive( )\n" );

    HRESULT hr;
    BOOL    b;
    DWORD   dw;

    CSpinLock SerializeLock( &m_lockSerialize, INFINITE );

    //
    //  Acquire the serialization lock.
    //
    hr = THR( SerializeLock.AcquireLock( ) );
    if ( FAILED( hr ) )
    {
        //
        //  Can't "goto Error" because we didn't acquire the lock.
        //
        LogError( hr );
        goto Cleanup;
    }

    //
    //  Wait for the script thread to be "done." 
    //
    dw = WaitForSingleObject( m_hEventDone, INFINITE );
    if ( dw != WAIT_OBJECT_0 )
        goto Win32Error;

    //
    //  Reset the done event to indicate that the thread is not busy.
    //
    b = ResetEvent( m_hEventDone );
    if ( !b )
        goto Win32Error;

    //
    //  Store the message in the common memory buffer.
    //
    m_msg = msgLOOKSALIVE;

    //
    //  Signal the script thread to process the message, but don't wait for 
    //  it to complete.
    //
    dw = SetEvent( m_hEventWait );

    if ( m_fLastLooksAlive )
    {
        hr = S_OK;
    }
    else
    {
        hr = S_FALSE;
    }

ReleaseLockAndCleanup:
    SerializeLock.ReleaseLock( );

Cleanup:
    HRETURN( hr );

Error:
    LogError( hr );
    goto ReleaseLockAndCleanup;

Win32Error:
    hr = HRESULT_FROM_WIN32( GetLastError( ) );
    goto Error;

} //*** LooksAlive( )

//////////////////////////////////////////////////////////////////////////////
//
//  STDMETHODIMP
//  CScriptResource::IsAlive(
//      )
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CScriptResource::IsAlive(
    )
{
    TraceClsFunc( "IsAlive( )\n" );

    HRESULT hr;
    
    hr = THR( WaitForMessageToComplete( msgISALIVE ) );

    HRETURN( hr );
} //*** IsAlive( )

//****************************************************************************
//
//  Privates
//
//****************************************************************************

//////////////////////////////////////////////////////////////////////////////
//
//  HRESULT
//  CScriptResource::WaitForMessageToComplete(
//      SMESSAGE msgIn
//      )
//
//////////////////////////////////////////////////////////////////////////////
HRESULT
CScriptResource::WaitForMessageToComplete(
    EMESSAGE msgIn
    )
{
    TraceClsFunc( "WaitForMessageToComplete( )\n" );

    HRESULT hr;
    BOOL    b;
    DWORD   dw;

    CSpinLock SerializeLock( &m_lockSerialize, INFINITE );

    //
    //  Acquire the serialization lock.
    //
    hr = THR( SerializeLock.AcquireLock( ) );
    if ( FAILED( hr ) )
    {
        //
        //  Can't "goto Error" because we didn't acquire the lock.
        //
        LogError( hr );
        goto Cleanup;
    }

    //
    //  Wait for the script thread to be "done."
    //
    dw = WaitForSingleObject( m_hEventDone, INFINITE );
    if ( dw != WAIT_OBJECT_0 )
        goto Win32Error;

    //
    //  Reset the done event to indicate that the thread is not busy.
    //
    b = ResetEvent( m_hEventDone );
    if ( !b )
        goto Win32Error;

    //
    //  Store the message in the common memory buffer.
    //
    m_msg = msgIn;

    //
    //  Signal the script thread to process the message.
    //
    b = SetEvent( m_hEventWait );
    if ( !b )
        goto Win32Error;

    //
    //  Wait for the thread to complete.
    //
    dw = WaitForSingleObject( m_hEventDone, INFINITE );
    if ( dw != WAIT_OBJECT_0 )
        goto Win32Error;

    //
    //  Get the result of the task from the common buffer.
    //
    hr = m_hr;

ReleaseLockAndCleanup:
    SerializeLock.ReleaseLock( );

Cleanup:
    HRETURN( hr );

Error:
    LogError( hr );
    goto ReleaseLockAndCleanup;

Win32Error:
    hr = HRESULT_FROM_WIN32( GetLastError( ) );
    goto Error;

} //*** WaitForMessageToComplete( )

//////////////////////////////////////////////////////////////////////////////
//
//  STDMETHODIMP
//  CScriptResource::LogError(
//      HRESULT hrIn
//      )
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CScriptResource::LogError(
    HRESULT hrIn
    )
{
    TraceClsFunc1( "LogError( hrIn = 0x%08x )\n", hrIn );

    TraceMsg( mtfCALLS, "%s failed. HRESULT: 0x%08x\n", m_pszName, hrIn );

    (ClusResLogEvent)( m_hResource, LOG_ERROR, L"HRESULT: 0x%1!08x!\n", hrIn );

    HRETURN( S_OK );

} //*** LogError( )

//////////////////////////////////////////////////////////////////////////////
//
//  STDMETHODIMP
//  CScriptResource::LogScriptError( 
//      EXCEPINFO ei 
//      )
//
//////////////////////////////////////////////////////////////////////////////
STDMETHODIMP
CScriptResource::LogScriptError( 
    EXCEPINFO ei 
    )
{
    TraceClsFunc( "LogScriptError( ... )\n" );

    HRESULT hr;

    if ( ei.pfnDeferredFillIn != NULL )
    {
        hr = THR( ei.pfnDeferredFillIn( &ei ) );
    }

    TraceMsg( mtfCALLS, "%s failed.\nError: %u\nSource: %s\nDescription: %s\n", 
              m_pszName, 
              ( ei.wCode == 0 ? ei.scode : ei.wCode ), 
              ( ei.bstrSource == NULL ? L"<null>" : ei.bstrSource ),
              ( ei.bstrDescription == NULL ? L"<null>" : ei.bstrDescription )
              );
    (ClusResLogEvent)( m_hResource, 
                       LOG_ERROR, 
                       L"Error: %1!u! - Description: %2 (Source: %3)\n", 
                       ( ei.wCode == 0 ? ei.scode : ei.wCode ), 
                       ( ei.bstrDescription == NULL ? L"<null>" : ei.bstrDescription ),
                       ( ei.bstrSource == NULL ? L"<null>" : ei.bstrSource )
                       );
    HRETURN( S_OK );
} //*** LogScriptError( )

//////////////////////////////////////////////////////////////////////////////
//
//  HRESULT
//  CScriptResource::OnOpen(
//      )
//
//////////////////////////////////////////////////////////////////////////////
HRESULT
CScriptResource::OnOpen(
    )
{
    TraceClsFunc( "OnOpen( ... )\n" );

    HRESULT hr = S_OK;
    hr = HRESULT_FROM_WIN32( ERROR_RETRY );
    (ClusResLogEvent)( m_hResource, 
                       LOG_INFORMATION, 
                       L"Leave OnOpen without calling connect.  Fail call so we don't try to use it.\n");
    return hr;

} //*** OnOpen( )

//////////////////////////////////////////////////////////////////////////////
//
//  HRESULT
//  CScriptResource::OnClose(
//      )
//
//////////////////////////////////////////////////////////////////////////////
HRESULT
CScriptResource::OnClose(
    )
{
    TraceClsFunc( "OnClose( )\n" );

    HRESULT hr;
    
    EXCEPINFO   ei;
    VARIANT     varResult;

    DISPPARAMS  dispparamsNoArgs = { NULL, NULL, 0, 0 };

    VariantInit( &varResult );

    // Assert( m_pidm != NULL );

    if ( m_pidm != NULL 
      && m_dispidClose != DISPID_UNKNOWN
       )
    {
        hr = THR( m_pidm->Invoke( m_dispidClose,
                                  IID_NULL,
                                  LOCALE_USER_DEFAULT,
                                  DISPATCH_METHOD,
                                  &dispparamsNoArgs, 
                                  &varResult,
                                  &ei,
                                  NULL
                                  ) );
    }
    else
    {
        hr = S_OK;
    }
    if ( hr == DISP_E_EXCEPTION )
    {
        LogScriptError( ei );
    }
    else if ( hr == DISP_E_MEMBERNOTFOUND )
    {
        //
        // No-op
        //
        hr = S_OK;
    } 
    else if ( FAILED( hr ) )
    {
        LogError( hr );
    }

    VariantClear( &varResult );

    //
    // Disconnect script engine.  Note that it may not be connected
    // but DoDisconnect is safe to call either way.
    //
    DoDisconnect( );

    HRETURN( hr );
} //*** OnClose( )

//////////////////////////////////////////////////////////////////////////////
//
//  HRESULT
//  CScriptResource::OnOnline( 
//      )
//
//////////////////////////////////////////////////////////////////////////////
HRESULT
CScriptResource::OnOnline( 
    )
{
    TraceClsFunc( "OnOnline( ... )\n" );

    HRESULT hr;
    DWORD   dwErr;
    DWORD   cbSize;
    DWORD   dwLow;
    DWORD   dwRead;

    LPWSTR      pszCommand;
    EXCEPINFO   ei;
    LPWSTR      pszScriptFilePathTmp = NULL;

    BOOL    b;
    BOOL    bDoneConnect = FALSE;
    VARIANT varResult;

    HANDLE  hFile = INVALID_HANDLE_VALUE;

    LPWSTR pszScriptName = NULL;
    LPSTR  paszText = NULL;
    LPWSTR pszScriptText = NULL;

    VariantInit( &varResult );

    DISPPARAMS  dispparamsNoArgs = { NULL, NULL, 0, 0 };

    //
    // Figure out how big the filepath is.
    //
    dwErr = TW32( ClusterRegQueryValue( m_hkeyParams, CLUSREG_NAME_GENSCRIPT_SCRIPT_FILEPATH, NULL, NULL, &cbSize ) );
    if ( dwErr != ERROR_SUCCESS )
    {
        hr = HRESULT_FROM_WIN32( dwErr );
        goto Error;
    } // if: failed

    //
    // Make a buffer big enough.
    //    
    cbSize += sizeof(L"");

    pszScriptFilePathTmp = reinterpret_cast<LPWSTR>( TraceAlloc( LMEM_FIXED, cbSize ) );
    if ( pszScriptFilePathTmp == NULL )
        goto OutOfMemory;

    //
    // Grab it for real this time,
    //
    dwErr = TW32( ClusterRegQueryValue( m_hkeyParams, CLUSREG_NAME_GENSCRIPT_SCRIPT_FILEPATH, NULL, reinterpret_cast<LPBYTE>( pszScriptFilePathTmp ), &cbSize ) );
    if ( dwErr != ERROR_SUCCESS )
    {
        hr = HRESULT_FROM_WIN32( dwErr );
        goto Error;
    }
    
    //
    // If we have some old data from before then free this first.
    //
    if ( m_pszScriptFilePath != NULL )
    {
        LocalFree( m_pszScriptFilePath );
    }

    m_pszScriptFilePath = ClRtlExpandEnvironmentStrings( pszScriptFilePathTmp );
    if ( m_pszScriptFilePath == NULL ) 
    {
        hr = HRESULT_FROM_WIN32( GetLastError( ) );
        goto Error;
    }
        
    hr = DoConnect (m_pszScriptFilePath);    
    if ( FAILED( hr ) )
    {
        goto Error;
    }
    bDoneConnect = TRUE;

    //
    // Open the script file.
    //
    hFile = CreateFile( m_pszScriptFilePath,
                        GENERIC_READ,
                        FILE_SHARE_READ,
                        NULL,
                        OPEN_EXISTING,
                        0,
                        NULL
                        );
    if ( hFile == INVALID_HANDLE_VALUE )
    {
        hr = THR( HRESULT_FROM_WIN32( GetLastError( ) ) );
        goto Error;
    } // if: failed to open

    //
    // Figure out its size.
    //
    dwLow = GetFileSize( hFile, NULL );
    if ( dwLow == -1 )
    {
        hr = THR( HRESULT_FROM_WIN32( GetLastError( ) ) );
        goto Error;
    } // if: failed to figure out size
    else if ( dwLow == -2 )
        goto OutOfMemory;

    //
    // Make a buffer big enough to hold it.
    //
    dwLow++;    // add one for trailing NULL.
    paszText = reinterpret_cast<LPSTR>( TraceAlloc( LMEM_FIXED, dwLow ) );
    if ( paszText == NULL )
        goto OutOfMemory;

    //
    // Read the script into memory.
    //
    b = ReadFile( hFile, paszText, dwLow - 1, &dwRead, NULL );
    if ( !b )
    {
        hr = HRESULT_FROM_WIN32( TW32( GetLastError( ) ) );
        goto Error;
    } // if: failed

    if ( dwRead == - 1 )
        goto OutOfMemory;
    if ( dwLow - 1 != dwRead )
        goto OutOfMemory;   // TODO: figure out a better error code.

    //
    // Make sure it is terminated.
    //
    paszText[ dwRead ] = L'\0';

    //
    // Make a buffer to convert the text into UNICODE.
    //
    dwRead++;
    pszScriptText = reinterpret_cast<LPWSTR>( TraceAlloc( LMEM_FIXED, dwRead * sizeof(WCHAR) ) );
    if ( pszScriptText == NULL )
        goto OutOfMemory;

    //
    // Convert it to UNICODE.
    //
    Assert( lstrlenA( paszText ) + 1 == (signed)dwRead );
    mbstowcs( pszScriptText, paszText, dwRead );

    //
    // Load the script into the engine for pre-parsing.
    //
    hr = THR( m_pasp->ParseScriptText( pszScriptText,
                                       NULL,
                                       NULL,
                                       NULL,
                                       0,
                                       0,
                                       0,
                                       &varResult,
                                       &ei
                                       ) );
    if ( hr == DISP_E_EXCEPTION )
        goto ErrorWithExcepInfo;
    else if ( FAILED( hr ) )
        goto Error;

    VariantClear( &varResult );

    Assert( m_pidm != NULL );

    //
    // Get DISPIDs for each method we will call.
    //
    pszCommand = L"Online";
    hr = THR( m_pidm->GetIDsOfNames( IID_NULL, 
                                    &pszCommand, 
                                    1, 
                                    LOCALE_USER_DEFAULT, 
                                    &m_dispidOnline 
                                    ) );
    if ( hr == DISP_E_UNKNOWNNAME )
    {
        m_dispidOnline = DISPID_UNKNOWN;
    }
    else if ( FAILED( hr ) )
    {
        goto Error;
    }

    pszCommand = L"Close";
    hr = THR( m_pidm->GetIDsOfNames( IID_NULL, 
                                    &pszCommand, 
                                    1, 
                                    LOCALE_USER_DEFAULT, 
                                    &m_dispidClose 
                                    ) );
    if ( hr == DISP_E_UNKNOWNNAME )
    {
        m_dispidClose = DISPID_UNKNOWN;
    }
    else if ( FAILED( hr ) )
    {
        goto Error;
    }

    pszCommand = L"Offline";
    hr = THR( m_pidm->GetIDsOfNames( IID_NULL, 
                                    &pszCommand, 
                                    1, 
                                    LOCALE_USER_DEFAULT, 
                                    &m_dispidOffline
                                    ) );
    if ( hr == DISP_E_UNKNOWNNAME )
    {
        m_dispidOffline = DISPID_UNKNOWN;
    }
    else if ( FAILED( hr ) )
    {
        goto Error;
    }

    pszCommand = L"Terminate";
    hr = THR( m_pidm->GetIDsOfNames( IID_NULL, 
                                     &pszCommand, 
                                     1, 
                                     LOCALE_USER_DEFAULT, 
                                     &m_dispidTerminate 
                                     ) );
    if ( hr == DISP_E_UNKNOWNNAME )
    {
        m_dispidTerminate = DISPID_UNKNOWN;
    }
    else if ( FAILED( hr ) )
    {
        goto Error;
    }

    pszCommand = L"LooksAlive";
    hr = THR( m_pidm->GetIDsOfNames( IID_NULL, 
                                     &pszCommand, 
                                     1, 
                                     LOCALE_USER_DEFAULT, 
                                     &m_dispidLooksAlive 
                                     ) );
    if ( hr == DISP_E_UNKNOWNNAME )
    {
        m_dispidLooksAlive = DISPID_UNKNOWN;
    }
    else if ( FAILED( hr ) )
    {
        goto Error;
    }

    pszCommand = L"IsAlive";
    hr = THR( m_pidm->GetIDsOfNames( IID_NULL, 
                                     &pszCommand, 
                                     1, 
                                     LOCALE_USER_DEFAULT, 
                                     &m_dispidIsAlive 
                                     ) );
    if ( hr == DISP_E_UNKNOWNNAME )
    {
        m_dispidIsAlive = DISPID_UNKNOWN;
    }
    else if ( FAILED( hr ) )
    {
        goto Error;
    }

    //
    // Invoke the Online function.
    //
    if ( m_dispidOnline != DISPID_UNKNOWN )
    {
        hr = THR( m_pidm->Invoke( m_dispidOnline,
                                  IID_NULL,
                                  LOCALE_USER_DEFAULT,
                                  DISPATCH_METHOD,
                                  &dispparamsNoArgs, 
                                  &varResult,
                                  &ei,
                                  NULL
                                  ) );
        if ( hr == DISP_E_EXCEPTION )
        {
            LogScriptError( ei );
        }
        else if ( hr == DISP_E_MEMBERNOTFOUND )
        {
            //
            // No-op
            //
            hr = S_OK;
        } 
        else if ( FAILED( hr ) )
        {
            LogError( hr );
        }
    }

    //
    // Assume the resource LooksAlive...
    //
    m_fLastLooksAlive = TRUE;

    //
    // TODO:    gpease  16-DEC-1999
    //          Record and process the result of the Online call.
    //

Cleanup:
    VariantClear( &varResult );

    if ( pszScriptFilePathTmp )
    {
        TraceFree( pszScriptFilePathTmp );
    } // if: pszScriptFilePathTmp

    if ( paszText != NULL )
    {
        TraceFree( paszText );
    } // if: paszText

    if ( pszScriptText != NULL )
    {
        TraceFree( pszScriptText );
    } // if: pszScriptText;

    if ( hFile != INVALID_HANDLE_VALUE )
    {
        CloseHandle( hFile );
    } // if: hFile

    HRETURN( hr );
Error:
    LogError( hr );
    if ( bDoneConnect == TRUE )
    {
        DoDisconnect( );
    }
    goto Cleanup;
ErrorWithExcepInfo:
    LogScriptError( ei );
    goto Cleanup;
OutOfMemory:
    hr = E_OUTOFMEMORY;
    goto Cleanup;
} //*** OnOnline( )

//////////////////////////////////////////////////////////////////////////////
//
//  HRESULT
//  CScriptResource::OnOffline(
//      )
//
//////////////////////////////////////////////////////////////////////////////
HRESULT
CScriptResource::OnOffline(
    )
{
    TraceClsFunc( "OnOffline( ... )\n" );

    HRESULT hr;

    EXCEPINFO   ei;
    VARIANT     varResult;

    DISPPARAMS  dispparamsNoArgs = { NULL, NULL, 0, 0 };

    VariantInit( &varResult );

    Assert( m_pidm != NULL );

    if ( m_pidm != NULL 
      && m_dispidOffline != DISPID_UNKNOWN
       )
    {
        hr = THR( m_pidm->Invoke( m_dispidOffline,
                                  IID_NULL,
                                  LOCALE_USER_DEFAULT,
                                  DISPATCH_METHOD,
                                  &dispparamsNoArgs, 
                                  &varResult,
                                  &ei,
                                  NULL
                                  ) );
    }
    else
    {
        hr = S_OK;
    }
    if ( hr == DISP_E_EXCEPTION )
    {
        LogScriptError( ei );
    }
    else if ( hr == DISP_E_MEMBERNOTFOUND )
    {
        //
        // No-op
        //
        hr = S_OK;
    } 
    else if ( FAILED( hr ) )
    {
        LogError( hr );
    }

    VariantClear( &varResult );

    //
    // Tear down the scripting engine association as it is recreated in OnOnline.
    //
    DoDisconnect( );

    HRETURN( hr );
} //*** OnOffline( )

//////////////////////////////////////////////////////////////////////////////
//
//  HRESULT
//  CScriptResource::OnTerminate(
//      )
//
//////////////////////////////////////////////////////////////////////////////
HRESULT
CScriptResource::OnTerminate(
    )
{
    TraceClsFunc( "OnTerminate( ... )\n" );

    HRESULT hr;

    EXCEPINFO   ei;
    VARIANT     varResult;

    DISPPARAMS  dispparamsNoArgs = { NULL, NULL, 0, 0 };

    VariantInit( &varResult );

//    Assert( m_pidm != NULL );

    if ( m_pidm != NULL 
      && m_dispidTerminate != DISPID_UNKNOWN
       )
    {
        hr = THR( m_pidm->Invoke( m_dispidTerminate,
                                  IID_NULL,
                                  LOCALE_USER_DEFAULT,
                                  DISPATCH_METHOD,
                                  &dispparamsNoArgs, 
                                  &varResult,
                                  &ei,
                                  NULL
                                  ) );
    }
    else
    {
        hr = S_OK;
    }
    if ( hr == DISP_E_EXCEPTION )
    {
        LogScriptError( ei );
    }
    else if ( hr == DISP_E_MEMBERNOTFOUND )
    {
        //
        // No-op
        //
        hr = S_OK;
    } 
    else if ( FAILED( hr ) )
    {
        LogError( hr );
    }
    HRETURN( hr );
} //*** OnTerminate( )

//////////////////////////////////////////////////////////////////////////////
//
//  HRESULT
//  CScriptResource::OnLooksAlive(
//      )
//
//////////////////////////////////////////////////////////////////////////////
HRESULT
CScriptResource::OnLooksAlive(
    )
{
    TraceClsFunc( "OnLooksAlive( ... )\n" );

    HRESULT hr;

    EXCEPINFO   ei;
    VARIANT     varResult;

    DISPPARAMS  dispparamsNoArgs = { NULL, NULL, 0, 0 };

    VariantInit( &varResult );

    Assert( m_pidm != NULL );

    if ( m_pidm != NULL 
      && m_dispidLooksAlive != DISPID_UNKNOWN
       )
    {
        hr = THR( m_pidm->Invoke( m_dispidLooksAlive,
                                  IID_NULL,
                                  LOCALE_USER_DEFAULT,
                                  DISPATCH_METHOD,
                                  &dispparamsNoArgs, 
                                  &varResult,
                                  &ei,
                                  NULL
                                  ) );
    }
    else
    {
        (ClusResLogEvent)( m_hResource, 
                           LOG_ERROR, 
                           L"%1 did not implement Function LooksAlive( ). This is a required function.\n", 
                           m_pszName 
                           );
        hr = DISP_E_MEMBERNOTFOUND;
        goto Cleanup;
    }
    if ( hr == DISP_E_EXCEPTION )
    {
        LogScriptError( ei );
        goto Cleanup;
    }
    else if ( FAILED( hr ) )
    {
        LogError( hr );
        goto Cleanup;
    }

    if ( V_VT( &varResult ) == VT_BOOL )
    {
        if ( !V_BOOL( &varResult ) )
        {
            hr = S_FALSE;
        } // if: not alive

    } // if: correct type returned
    else
    {
        hr = THR( E_INVALIDARG );
        LogError( hr );
    } // else: failed

Cleanup:
    VariantClear( &varResult );

    //
    //  Only if the result of this function is S_OK is the resource
    //  considered alive.
    //
    if ( hr == S_OK )
    {
        m_fLastLooksAlive = TRUE;
    } // if: S_OK
    else
    {
        m_fLastLooksAlive = FALSE;
    } // else: failed

    HRETURN( hr );
} //*** OnLooksAlive( )

//////////////////////////////////////////////////////////////////////////////
//
//  HRESULT
//  CScriptResource::OnIsAlive(
//      )
//
//////////////////////////////////////////////////////////////////////////////
HRESULT
CScriptResource::OnIsAlive(
    )
{
    TraceClsFunc( "IsAlive( ... )\n" );

    HRESULT hr;

    EXCEPINFO   ei;
    VARIANT     varResult;

    DISPPARAMS  dispparamsNoArgs = { NULL, NULL, 0, 0 };

    VariantInit( &varResult );

    if ( m_pidm != NULL 
      && m_dispidIsAlive != DISPID_UNKNOWN
       )
    {
        hr = THR( m_pidm->Invoke( m_dispidIsAlive,
                                  IID_NULL,
                                  LOCALE_USER_DEFAULT,
                                  DISPATCH_METHOD,
                                  &dispparamsNoArgs, 
                                  &varResult,
                                  &ei,
                                  NULL
                                  ) );
    }
    else
    {
        (ClusResLogEvent)( m_hResource, 
                           LOG_ERROR, 
                           L"%1 did not implement Function IsAlive( ). This is a required function.\n", 
                           m_pszName 
                           );
        hr = DISP_E_MEMBERNOTFOUND;
        goto Cleanup;
    }
    if ( hr == DISP_E_EXCEPTION )
    {
        LogScriptError( ei );
        goto Cleanup;
    }
    else if ( FAILED( hr ) )
    {
        LogError( hr );
        goto Cleanup;
    }

    if ( V_VT( &varResult ) == VT_BOOL )
    {
        if ( !V_BOOL( &varResult ) )
        {
            hr = S_FALSE;
        } // if: not alive

    } // if: correct type returned
    else
    {
        hr = THR( E_INVALIDARG );
        LogError( hr );
    } // else: failed

Cleanup:
    VariantClear( &varResult );

    HRETURN( hr );
} //*** OnIsAlive( )

/////////////////////////////////////////////////////////////////////////////
//
//  DWORD 
//  WINAPI
//  CScriptResource::S_ThreadProc( 
//      LPVOID pParam 
//      )
//
/////////////////////////////////////////////////////////////////////////////
DWORD 
WINAPI
CScriptResource::S_ThreadProc( 
    LPVOID pParam 
    )
{
    MSG     msg;
    HRESULT hr;
    DWORD   dw;
    BOOL    b;

    CScriptResource * pscript = reinterpret_cast< CScriptResource * >( pParam );

    Assert( pscript != NULL );

    //
    // Initialize COM.
    //
    hr = THR( CoInitializeEx( NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE ) );
    if ( FAILED( hr ) )
        goto Error;

    for( ;; ) // ever
    {
        //
        //  Indicate that we are ready to do something.
        //
        b = SetEvent( pscript->m_hEventDone );
        if ( !b )
            goto Win32Error;

        //
        //  Wait for someone to need something.
        //
        dw = WaitForSingleObject( pscript->m_hEventWait, INFINITE );
        if ( dw != WAIT_OBJECT_0 )
        {
            hr = HRESULT_FROM_WIN32( dw );
            goto Error;
        }

        //
        //  Reset the event.
        //
        b = ResetEvent( pscript->m_hEventWait );
        if ( !b )
            goto Win32Error;

        //
        //  Do what they ask.
        //
        switch ( pscript->m_msg )
        {
        case msgOPEN:
            pscript->m_hr = THR( pscript->OnOpen( ) );
            break;

        case msgCLOSE:
            pscript->m_hr = THR( pscript->OnClose( ) );
            break;

        case msgONLINE:
            pscript->m_hr = THR( pscript->OnOnline( ) );
            break;

        case msgOFFLINE:
            pscript->m_hr = THR( pscript->OnOffline( ) );
            break;

        case msgTERMINATE:
            pscript->m_hr = THR( pscript->OnTerminate( ) );
            break;

        case msgLOOKSALIVE:
            pscript->m_hr = STHR( pscript->OnLooksAlive( ) );
            break;

        case msgISALIVE:
            pscript->m_hr = STHR( pscript->OnIsAlive( ) );
            break;

        case msgDIE:
            //
            // This means the resource is being released.
            //
            goto Cleanup;
        }

    } // spin forever

Cleanup:
    CoUninitialize( );
    return hr;

Error:
    pscript->LogError( hr );
    goto Cleanup;

Win32Error:
    hr = HRESULT_FROM_WIN32( GetLastError( ) );
    goto Error;
} //*** S_ThreadProc( )

//////////////////////////////////////////////////////////////////////////////
//
//  DWORD
//  CScriptResource::MakeScriptEngineAssociation( pszScriptFileName )
//
//  Description:
//      Takes the filename and splits off the extension then queries
//      the registry to obtain the association and finally queries the
//      ScriptingEngine key under that association and allocates and
//      returns a buffer containing the engine name.  This engine name
//      is suitable for input into CLSIDFromProgID.
//
//  Arguments:
//      pszScriptFileName - Pointer to null terminated script file name (full path with environment expanded).
//
//  Return Values:
//      NULL    - Failure to read engine for this key, consult GetLastError() for details.
//      Valid pointer to string buffer containing engine prog id.
//
//////////////////////////////////////////////////////////////////////////////

#define SCRIPTENGINE_KEY_STRING L"\\ScriptEngine"
LPWSTR
CScriptResource::MakeScriptEngineAssociation(
    IN    LPCWSTR     pszScriptFileName 
    )
{
    LPWSTR  pszAssociation = NULL;
    LPWSTR  pszEngineName  = NULL;
    LONG    lRegStatus     = ERROR_SUCCESS;
    HKEY    hKey           = NULL;
    WCHAR   szExtension[_MAX_EXT];
    DWORD   dwType, cbAssociationSize, cbEngineNameSize, dwNumChars;

    //
    // First split the path to get the extension.
    //
    _wsplitpath (pszScriptFileName, NULL, NULL, NULL, szExtension);
    if (szExtension[0] == L'\0') {
        SetLastError (ERROR_FILE_NOT_FOUND);
        goto Cleanup;
    }

    //
    // Pre-parse to patch up .scr association!
    //
    if (_wcsicmp (szExtension, L".scr") == 0)
    {
        LPWSTR pszSCREngine=NULL;
        pszSCREngine = (LPWSTR) TraceAlloc( GPTR, sizeof( L"VBScript" ) );
        if ( pszSCREngine == NULL )
            goto ErrorOutOfMemory;
        else
        {
            wcscpy (pszSCREngine, L"VBScript");
            return pszSCREngine;
        }
    }

    //
    // If the pre-parse didn't get it then go to the registry to do
    // the right thing.
    //
    lRegStatus = RegOpenKeyExW( HKEY_CLASSES_ROOT,              // handle to open key
                                szExtension,                    // subkey name
                                0,                              // reserved
                                KEY_READ,                       // security access desired.
                                &hKey);                         // key handle returned
    if (lRegStatus != ERROR_SUCCESS)
        goto Error;
    
    //
    // Query the value to get the size of the buffer to allocate.
    // NB cbSize contains the size including the '\0'
    //
    lRegStatus = RegQueryValueExW( hKey,                        // handle to key
                                   NULL,                        // value name
                                   0,                           // reserved
                                   &dwType,                     // type buffer
                                   NULL,                        // data buffer
                                   &cbAssociationSize);         // size of data buffer
    if ( lRegStatus != ERROR_SUCCESS )
        goto Error;
    if ( dwType != REG_SZ )
        goto ErrorBadType;

    dwNumChars = cbAssociationSize / sizeof (WCHAR);
    pszAssociation = (LPWSTR) TraceAlloc( GPTR, cbAssociationSize + sizeof (SCRIPTENGINE_KEY_STRING) );
    if ( pszAssociation == NULL )
        goto ErrorOutOfMemory;

    // Get the value for real.
    //
    lRegStatus = RegQueryValueExW( hKey,                        // handle to key
                                   NULL,                        // value name
                                   0,                           // reserved
                                   &dwType,                     // type buffer
                                   (LPBYTE) pszAssociation,     // data buffer
                                   &cbAssociationSize );        // size of data buffer
    if ( lRegStatus != ERROR_SUCCESS )
        goto Error;
    if ( dwType != REG_SZ )
        goto ErrorBadType;
    
    lRegStatus = RegCloseKey( hKey );
    if ( lRegStatus != ERROR_SUCCESS )
        goto Error;
    
    hKey = NULL;

    //
    // Take the data and make a key with \ScriptEngine on the end.  If
    // we find this then we can use the file.
    //
    swprintf( &pszAssociation[ dwNumChars - 1 ], SCRIPTENGINE_KEY_STRING );
    pszAssociation[ dwNumChars + (sizeof( SCRIPTENGINE_KEY_STRING ) / sizeof ( WCHAR ) )  - 1 ] = L'\0';
    
    lRegStatus = RegOpenKeyExW( HKEY_CLASSES_ROOT,              // handle to open key
                                pszAssociation,                 // subkey name
                                0,                              // reserved
                                KEY_READ,                       // security access
                                &hKey );                        // key handle 

    lRegStatus = RegQueryValueExW( hKey,                        // handle to key
                                   NULL,                        // value name
                                   0,                           // reserved
                                   &dwType,                     // type buffer
                                   NULL,                        // data buffer
                                   &cbEngineNameSize);          // size of data buffer
    
    if ( lRegStatus != ERROR_SUCCESS )
        goto Error;
    if ( dwType != REG_SZ )
        goto ErrorBadType;

    dwNumChars = cbEngineNameSize / sizeof (WCHAR);
    pszEngineName = (LPWSTR) TraceAlloc( GPTR, cbEngineNameSize );
    if ( NULL == pszEngineName )
    {
        goto ErrorOutOfMemory;
    }
    pszEngineName[ dwNumChars - 1 ] = '\0';
        
    //
    // Get the value for real.
    //
    lRegStatus = RegQueryValueExW( hKey,                        // handle to key
                                   NULL,                        // value name
                                   0,                           // reserved
                                   &dwType,                     // type buffer
                                   (LPBYTE) pszEngineName,      // data buffer
                                   &cbEngineNameSize);          // size of data buffer

    if ( lRegStatus != ERROR_SUCCESS )
        goto Error;
    if ( dwType != REG_SZ )
        goto ErrorBadType;
    
    lRegStatus = RegCloseKey( hKey );
    if ( lRegStatus != ERROR_SUCCESS )
        goto Error;
    
    hKey = NULL;
    goto Cleanup;

Error:
    SetLastError (lRegStatus);
    goto ErrorCleanup;

ErrorBadType:
    SetLastError (ERROR_FILE_NOT_FOUND);
    goto ErrorCleanup;

ErrorOutOfMemory:
    SetLastError (ERROR_NOT_ENOUGH_MEMORY);
    goto ErrorCleanup;

ErrorCleanup:
    if (pszEngineName) 
    {
        TraceFree (pszEngineName);
        pszEngineName = NULL;
    }
Cleanup:
    if (pszAssociation)
        TraceFree (pszAssociation);
    if (hKey)
        (void) RegCloseKey (hKey);

    return pszEngineName;
}
#undef SCRIPTENGINE_KEY_STRING


//////////////////////////////////////////////////////////////////////////////
//
//  HRESULT
//  CScriptResource::DoConnect( szScriptFilePath )
//
//  Description:
//      Connects to the script engine associated with the script passed in.
//
//  Arguments:
//      pszScriptFileName - Pointer to null terminated script file name (full path with environment expanded).
//
//  Return Values:
//      S_OK - connected OK.
//      Failure status - local cleanup performed.
//
//////////////////////////////////////////////////////////////////////////////
HRESULT
CScriptResource::DoConnect(
    IN  LPWSTR  szScriptFilePath
    )
{
    HRESULT hr = S_OK;
    DWORD cbSize;
    DWORD dwErr;
    CLSID clsidScriptEngine;

    CActiveScriptSite * psite;

    //
    // Create the scripting site.
    //
    psite = new CActiveScriptSite( m_hResource, ClusResLogEvent, m_hkeyParams, m_pszName );
    if ( psite == NULL )
        goto OutOfMemory;

    hr = THR( psite->QueryInterface( IID_IActiveScriptSite, reinterpret_cast<void**>( &m_pass ) ) );
    psite->Release( );      // release promptly
    if ( FAILED( hr ) )
        goto Error;

    //
    // Find the Active Engine.
    //
    if (szScriptFilePath == NULL)
    {
        (ClusResLogEvent)( m_hResource, 
                           LOG_INFORMATION, L"DoConnect: Default to VBScript\n");

        hr = THR( CLSIDFromProgID( L"VBScript", &clsidScriptEngine ) );
        if ( FAILED( hr ) )
            goto Error;
    }
    else
    {
        (ClusResLogEvent)( m_hResource, 
                           LOG_INFORMATION, L"DoConnect: Got path: %1\n", szScriptFilePath);

        //
        // Find the program associated with the extension.
        //
        if ( m_pszScriptEngine != NULL )
        {
            TraceFree( m_pszScriptEngine );
        }
        m_pszScriptEngine = MakeScriptEngineAssociation( szScriptFilePath );
        if ( m_pszScriptEngine == NULL) 
        {
            (ClusResLogEvent)( m_hResource, 
                               LOG_ERROR, L"Error getting engine\n");
            hr = HRESULT_FROM_WIN32( GetLastError( ) );
            goto Error;
        }

        (ClusResLogEvent)( m_hResource, 
                           LOG_ERROR, L"Got engine %1\n", m_pszScriptEngine);

        hr = THR( CLSIDFromProgID( m_pszScriptEngine, &clsidScriptEngine ) );
        if ( FAILED( hr ) ) 
        {
            (ClusResLogEvent)( m_hResource, 
                               LOG_ERROR, L"Error getting prog ID\n");
            goto Error;
        }
    }
    //
    // Create an instance of it.
    //
    TraceDo( hr = THR( CoCreateInstance( clsidScriptEngine, 
                                NULL, 
                                CLSCTX_SERVER, 
                                IID_IActiveScriptParse, 
                                reinterpret_cast<void**>( &m_pasp ) 
                                ) )
            );
    if ( FAILED( hr ) ) {
        (ClusResLogEvent)( m_hResource, LOG_ERROR, L"DoConnect: Failed to create instance of CLSID\n");
        goto Error;
    }
    m_pasp = TraceInterface( L"Active Script Engine", IActiveScriptParse, m_pasp, 1 );

    TraceDo( hr = THR( m_pasp->QueryInterface( IID_IActiveScript, (void**) &m_pas ) ) );
    if ( FAILED( hr ) )
        goto Error;
    m_pas = TraceInterface( L"Active Script Engine", IActiveScript, m_pas, 1 );

    //
    // Initialize it.
    //
    TraceDo( hr = THR( m_pasp->InitNew( ) ) );
    if ( FAILED( hr ) ) 
    {
        (ClusResLogEvent)( m_hResource, LOG_ERROR, L"DoConnect: Failed to InitNew\n");
        goto Error;
    }

#if defined(DEBUG)
    //
    // Set our site. We'll give out a new tracking interface to track this separately.
    //
    {
        IActiveScriptSite * psite;
        hr = THR( m_pass->TypeSafeQI( IActiveScriptSite, &psite ) );
        Assert( hr == S_OK );

        TraceDo( hr = THR( m_pas->SetScriptSite( psite ) ) );
        psite->Release( );      // release promptly
        if ( FAILED( hr ) )
            goto Error;
    }
#else
    TraceDo( hr = THR( m_pas->SetScriptSite( m_pass ) ) );
    if ( FAILED( hr ) )
    {
        (ClusResLogEvent)( m_hResource, LOG_ERROR, L"DoConnect: Failed to SetScriptSite\n");
        goto Error;
    }
#endif

    //
    // Add Document to the global members.
    //
    TraceDo( hr = THR( m_pas->AddNamedItem( L"Resource", SCRIPTITEM_ISVISIBLE ) ) );
    if ( FAILED( hr ) )
        goto Error;

    //
    // Connect the script.
    //
    TraceDo( hr = THR( m_pas->SetScriptState( SCRIPTSTATE_CONNECTED ) ) );
    if ( FAILED( hr ) ) {
        (ClusResLogEvent)( m_hResource, LOG_ERROR, L"DoConnect: Failed to SetScriptState\n");
        goto Error;
    }
    //
    // Get the dispatch inteface to the script.
    //
    TraceDo( hr = THR( m_pas->GetScriptDispatch( NULL, &m_pidm ) ) );
    if ( FAILED( hr) ) {
        (ClusResLogEvent)( m_hResource, LOG_ERROR, L"DoConnect: Failed to GetScriptDispatch\n");
        goto Error;
    }
    m_pidm = TraceInterface( L"Active Script", IDispatch, m_pidm, 1 );

    hr = S_OK;

Cleanup:
    HRETURN( hr );

Error:
    LogError( hr );
    goto Cleanup;

OutOfMemory:
    hr = E_OUTOFMEMORY;
    goto Cleanup;
}

//////////////////////////////////////////////////////////////////////////////
//
//  void
//      CScriptResource::DoDisconnect( )
//
//  Description:
//      Disconnects from any currently connected script engine.
//
//  Arguments:
//      none.
//
//  Return Values:
//      none.
//
//////////////////////////////////////////////////////////////////////////////
void
CScriptResource::DoDisconnect( )
{
    //
    // Cleanup the scripting engine.
    //
    if ( m_pszScriptFilePath != NULL )
    {
        LocalFree( m_pszScriptFilePath );
        m_pszScriptFilePath = NULL;
    } // if: m_pszScriptFilePath

    if ( m_pszScriptEngine != NULL )
    {
        TraceFree( m_pszScriptEngine );
        m_pszScriptEngine = NULL;
    } // if: m_pszScriptEngine

    if ( m_pidm != NULL )
    {
        TraceDo( m_pidm->Release( ) );
        m_pidm = NULL;
    } // if: m_pidm

    if ( m_pasp != NULL )
    {
        TraceDo( m_pasp->Release( ) );
        m_pasp = NULL;
    } // if: m_pasp

    if ( m_pas != NULL )
    {
        TraceDo( m_pas->Close( ) );
        TraceDo( m_pas->Release( ) );
        m_pas = NULL;
    } // if: m_pas

    if ( m_pass != NULL )
    {
        TraceDo( m_pass->Release( ) );
        m_pass = NULL;
    } // if: m_pass
}