////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1999-2000 Microsoft Corporation // // Module Name: // ScriptResource.cpp // // Description: // CScriptResource class implementation. // // Maintained By: // gpease 14-DEC-1999 // ////////////////////////////////////////////////////////////////////////////// #include "pch.h" #include #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"" : ei.bstrSource ), ( ei.bstrDescription == NULL ? L"" : 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"" : ei.bstrDescription ), ( ei.bstrSource == NULL ? L"" : 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( 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( 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( 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( 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( &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( &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 }