////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1999-2003 Microsoft Corporation // // Module Name: // ScriptResource.cpp // // Description: // CScriptResource class implementation. // // Maintained By: // Ozan Ozhan (OzanO) 22-MAR-2002 // David Potter (DavidP) 14-JUN-2001 // Geoff Pease (GPease) 14-DEC-1999 // ////////////////////////////////////////////////////////////////////////////// #include "Pch.h" #include "ActiveScriptSite.h" #include "ScriptResource.h" #include "SpinLock.h" DEFINE_THISCLASS("CScriptResource") // // We need this to log to the system event log // #define LOG_CURRENT_MODULE LOG_MODULE_GENSCRIPT // // 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). // ////////////////////////////////////////////////////////////////////////////// // // CScriptResource_CreateInstance // // 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 * lpcc = new CScriptResource(); if ( lpcc != NULL ) { HRESULT hr = THR( lpcc->HrInit( pszNameIn, hkeyIn, hResourceIn ) ); if ( SUCCEEDED( hr ) ) { RETURN( lpcc ); } // if: success delete lpcc; } // if: got object RETURN(NULL); } //*** CScriptResource_CreateInstance ////////////////////////////////////////////////////////////////////////////// // // Constructor // ////////////////////////////////////////////////////////////////////////////// CScriptResource::CScriptResource( void ) : 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), m_pszScriptFilePath( NULL ), m_pszHangEntryPoint( NULL ), m_hScriptFile( INVALID_HANDLE_VALUE ), m_fHangDetected( FALSE ), m_fPendingTimeoutChanged( TRUE ), m_dwPendingTimeout( CLUSTER_RESOURCE_DEFAULT_PENDING_TIMEOUT ), m_msgLastExecuted( msgUNKNOWN ), m_pProps( NULL ) { TraceFunc1( "%s", __THISCLASS__ ); Assert( m_cRef == 0 ); Assert( m_pass == NULL ); Assert( m_pasp == NULL ); Assert( m_pas == NULL ); Assert( m_pidm == NULL ); TraceFuncExit(); } //*** constructor ////////////////////////////////////////////////////////////////////////////// // // Destructor // ////////////////////////////////////////////////////////////////////////////// CScriptResource::~CScriptResource( void ) { TraceFunc( "" ); 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 ); } LocalFree( m_pszScriptFilePath ); TraceFree( m_pszName ); delete [] m_pszHangEntryPoint; if ( m_hkeyParams != NULL ) { ClusterRegCloseKey( m_hkeyResource ); } // if: m_hkeyResource 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 ////////////////////////////////////////////////////////////////////////////// // // CScriptResource::Init // // 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::HrInit( LPCWSTR pszNameIn, HKEY hkeyIn, RESOURCE_HANDLE hResourceIn ) { TraceFunc1( "pszNameIn = '%ws'", pszNameIn ); HRESULT hr = S_OK; DWORD scErr; // 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 ) { scErr = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // Task completion event m_hEventDone = CreateEvent( NULL, TRUE, FALSE, NULL ); if ( m_hEventDone == NULL ) { scErr = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // // Copy the resource name. // m_pszName = TraceStrDup( pszNameIn ); if ( m_pszName == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // // Save the registry key for this resource in m_kheyResource. // scErr = TW32( ClusterRegOpenKey( hkeyIn, L"", KEY_ALL_ACCESS, &m_hkeyResource ) ); if ( scErr != ERROR_SUCCESS ) { hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // if: failed // // Open the parameters key. // scErr = TW32( ClusterRegOpenKey( hkeyIn, L"Parameters", KEY_ALL_ACCESS, &m_hkeyParams ) ); if ( scErr != ERROR_SUCCESS ) { hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // if: failed // // Create the scripting engine thread. // m_hThread = CreateThread( NULL, 0, &S_ThreadProc, this, 0, &m_dwThreadId ); if ( m_hThread == NULL ) { scErr = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } Cleanup: // // All class variable clean up should be done in the destructor. // HRETURN( hr ); Error: LogError( hr, L"HrInit() failed." ); goto Cleanup; } //*** CScriptResource::Init //**************************************************************************** // // IUnknown // //**************************************************************************** ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::QueryInterface // // Description: // Query this object for the passed in interface. // // Arguments: // riidIn // Id of interface requested. // // ppvOut // Pointer to the requested interface. // // Return Value: // S_OK // If the interface is available on this object. // // E_NOINTERFACE // If the interface is not available. // // E_POINTER // ppvOut was NULL. // // Remarks: // None. // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CScriptResource::QueryInterface( REFIID riidIn , LPVOID * ppvOut ) { TraceQIFunc( riidIn, ppvOut ); HRESULT hr = S_OK; // // Validate arguments. // Assert( ppvOut != NULL ); if ( ppvOut == NULL ) { hr = THR( E_POINTER ); goto Cleanup; } // // Handle known interfaces. // if ( IsEqualIID( riidIn, IID_IUnknown ) ) { *ppvOut = TraceInterface( __THISCLASS__, IUnknown, static_cast< IUnknown * >( this ), 0 ); } // if: IUnknown else { *ppvOut = NULL; hr = THR( E_NOINTERFACE ); } // else // // Add a reference to the interface if successful. // if ( SUCCEEDED( hr ) ) { ((IUnknown *) *ppvOut)->AddRef(); } // if: success Cleanup: QIRETURN( hr, riidIn ); } //*** CScriptResource::QueryInterface ////////////////////////////////////////////////////////////////////////////// // // STDMETHODIMP_( ULONG ) // CScriptResource::[IUnknown] AddRef( void ) // ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP_( ULONG ) CScriptResource::AddRef( void ) { TraceFunc( "[IUnknown]" ); LONG cRef = InterlockedIncrement( &m_cRef ); RETURN( cRef ); } //*** CScriptResource::AddRef ////////////////////////////////////////////////////////////////////////////// // // STDMETHODIMP_( ULONG ) // CScriptResource::[IUnknown] Release( void ) // ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP_( ULONG ) CScriptResource::Release( void ) { TraceFunc( "[IUnknown]" ); LONG cRef = InterlockedDecrement( &m_cRef ); if ( cRef == 0 ) { TraceDo( delete this ); } // if: reference count decremented to zero RETURN( cRef ); } //*** CScriptResource::Release //**************************************************************************** // // Publics // //**************************************************************************** ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::Open // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CScriptResource::Open( void ) { TraceFunc( "" ); 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 ); // DavidP 27-MAR-2002 Reverting the above change. DBG PRINTs are okay. // Besides, it needs to balance out the TraceFunc above. // return hr; HRETURN( hr ); } //*** CScriptResource::Open ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::Close // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CScriptResource::Close( void ) { TraceFunc( "" ); HRESULT hr; hr = THR( WaitForMessageToComplete( msgCLOSE ) ); HRETURN( hr ); } //*** CScriptResource::Close ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::Online // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CScriptResource::Online( void ) { TraceFunc( "" ); HRESULT hr; hr = THR( WaitForMessageToComplete( msgONLINE ) ); HRETURN( hr ); } //*** CScriptResource::Online ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::Offline // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CScriptResource::Offline( void ) { TraceFunc( "" ); HRESULT hr; hr = THR( WaitForMessageToComplete( msgOFFLINE ) ); HRETURN( hr ); } //*** CScriptResource::Offline ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::Terminate // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CScriptResource::Terminate( void ) { TraceFunc( "" ); HRESULT hr; hr = THR( WaitForMessageToComplete( msgTERMINATE ) ); HRETURN( hr ); } //*** CScriptResource::Terminate ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::LooksAlive // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CScriptResource::LooksAlive( void ) { TraceFunc( "" ); HRESULT hr; BOOL fSuccess; DWORD dw; DWORD scErr; CSpinLock SerializeLock( &m_lockSerialize, INFINITE ); // // A potential hang has already been detected in this script. Therefore we // will not process any other calls to this script. // if ( m_fHangDetected == TRUE ) { LogHangMode( msgLOOKSALIVE ); scErr = TW32( ERROR_TIMEOUT ); hr = HRESULT_FROM_WIN32( scErr ); goto Cleanup; } // if: ( m_fHangDetected == TRUE ) if ( m_fPendingTimeoutChanged == TRUE ) { // // Read the new pending timeout from the cluster hive. // m_dwPendingTimeout = DwGetResourcePendingTimeout(); m_fPendingTimeoutChanged = FALSE; } // if: pending timeout has changed. // // Acquire the serialization lock. // hr = THR( SerializeLock.AcquireLock() ); if ( FAILED( hr ) ) { // // Can't "goto Error" because we didn't acquire the lock. // LogError( hr, L"LooksAlive() failed to acquire the serialization lock." ); goto Cleanup; } // // Wait for the script thread to be "done." // dw = WaitForSingleObject( m_hEventDone, m_dwPendingTimeout ); if ( dw == WAIT_TIMEOUT ) { m_fHangDetected = TRUE; hr = HrSetHangEntryPoint(); if ( FAILED( hr ) ) { goto Error; } scErr = TW32( ERROR_TIMEOUT ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // if: ( dw == WAIT_TIMEOUT ) else if ( dw != WAIT_OBJECT_0 ) { scErr = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // else if: ( dw != WAIT_OBJECT_0 ) // // Reset the done event to indicate that the thread is not busy. // fSuccess = ResetEvent( m_hEventDone ); if ( fSuccess == FALSE ) { scErr = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // // 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, L"LooksAlive() failed." ); goto ReleaseLockAndCleanup; } //*** CScriptResource::LooksAlive ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::IsAlive // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CScriptResource::IsAlive( void ) { TraceFunc( "" ); HRESULT hr; hr = THR( WaitForMessageToComplete( msgISALIVE ) ); HRETURN( hr ); } //*** CScriptResource::IsAlive ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::SetPrivateProperties // // Description: // Handle the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control code. // // Arguments: // pProps // // Return Values: // //-- ////////////////////////////////////////////////////////////////////////////// DWORD CScriptResource::SetPrivateProperties( PGENSCRIPT_PROPS pProps ) { TraceFunc( "" ); HRESULT hr = S_OK; DWORD sc = ERROR_SUCCESS; hr = STHR( WaitForMessageToComplete( msgSETPRIVATEPROPERTIES , pProps ) ); sc = STATUS_TO_RETURN( hr ); W32RETURN( sc ); } //*** CScriptResource::SetPrivateProperties //**************************************************************************** // // Privates // //**************************************************************************** ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::DwGetResourcePendingTimeout // // Description: // Returns the resource pending timeout from the cluster hive. If it can // not read this value for some reason, it returns the default resource // pending timeout. // // Return Values: // Resource pending timeout. // //-- ////////////////////////////////////////////////////////////////////////////// DWORD CScriptResource::DwGetResourcePendingTimeout( void ) { DWORD scErr = ERROR_SUCCESS; DWORD dwType; DWORD dwValue = CLUSTER_RESOURCE_DEFAULT_PENDING_TIMEOUT; DWORD cbSize= sizeof( DWORD ); scErr = ClusterRegQueryValue( m_hkeyResource, CLUSREG_NAME_RES_PENDING_TIMEOUT, &dwType, (LPBYTE) &dwValue, &cbSize ); if ( scErr != ERROR_SUCCESS ) { if ( scErr != ERROR_FILE_NOT_FOUND ) { // // Log an error to the cluster log. // (ClusResLogEvent)( m_hResource , LOG_ERROR , L"DwGetResourcePendingTimeout: Failed to query the cluster hive for the resource pending time out. SCODE: 0x%1!08x! \n" , scErr ); } dwValue = CLUSTER_RESOURCE_DEFAULT_PENDING_TIMEOUT; goto Cleanup; } //if: ( scErr != ERROR_SUCCESS ) Assert( dwType == REG_DWORD ); Cleanup: return dwValue; } //*** CScriptResource::DwGetResourcePendingTimeout ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::LogHangMode // // Description: // Log an error that informs that the incoming request will not be // proccessed due to a hang mode. // // Arguments: // msgIn - Incoming request message. // //-- ////////////////////////////////////////////////////////////////////////////// void CScriptResource::LogHangMode( EMESSAGE msgIn ) { // // If the msgIn request is a known user request, let's log an entry to // the system event log. // if ( ( msgIn > msgUNKNOWN ) && ( msgIn < msgDIE ) ) { // // Cluster logging infrastructure can display upto LOGENTRY_BUFFER_SIZE // characters. Since our error message text is too long, we'll cut it into two // and display it as two error messages. // // // Log an error to the cluster log. // (ClusResLogEvent)( m_hResource , LOG_ERROR , L"Request to perform the %1 operation will not be processed. This is because of a previous failed attempt to execute " L"the %2 entry point in a timely fashion. Please review the script code for this entry point to make sure there is no infinite " L"loop or a hang in it, and then consider increasing the resource pending timeout value if necessary.\n" , g_rgpszScriptEntryPointNames[ msgIn ] , m_pszHangEntryPoint == NULL ? L"" : m_pszHangEntryPoint ); // // Log an error to the cluster log. // (ClusResLogEvent)( m_hResource , LOG_ERROR , L"In a command shell, run \"cluster res \"%1\" /prop PersistentState=0\" to disable this resource, and then run \"net stop clussvc\" " L"to stop the cluster service. Ensure that any problem in the script code is fixed. Then run \"net start clussvc\" to start the cluster " L"service. If necessary, ensure that the pending time out is increased before bringing the resource online again.\n" , m_pszName ); // // Log an error to the system event log. // ClusterLogEvent3( LOG_CRITICAL , LOG_CURRENT_MODULE , __FILE__ , __LINE__ , RES_GENSCRIPT_HANGMODE , 0 , NULL , m_pszName , g_rgpszScriptEntryPointNames[ msgIn ] , m_pszHangEntryPoint == NULL ? L"" : m_pszHangEntryPoint ); } // if: ( pszEntryPoint != NULL ) } //*** CScriptResource::LogHangMode ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::HrSetHangEntryPoint // // Description: // Allocates memory and sets m_pszHangEntryPoint and logs an error // // Return Values: // S_OK on success // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::HrSetHangEntryPoint( void ) { TraceFunc( "" ); HRESULT hr = S_OK; size_t cch = 0; // // m_msgLastExecuted is initially set to msgUNKNOWN in the constructor. // if ( m_msgLastExecuted != msgUNKNOWN ) { delete [] m_pszHangEntryPoint; cch = wcslen( g_rgpszScriptEntryPointNames[ m_msgLastExecuted ] ) + 1; m_pszHangEntryPoint = new WCHAR[ cch ]; if ( m_pszHangEntryPoint == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Cleanup; } // if: ( m_pszHangEntryPoint == NULL ) hr = THR( StringCchCopyW( m_pszHangEntryPoint, cch, g_rgpszScriptEntryPointNames[ m_msgLastExecuted ] ) ); if ( FAILED( hr ) ) { goto Cleanup; } // if: ( FAILED( hr ) ) // // Cluster logging infrastructure can display upto LOGENTRY_BUFFER_SIZE // characters. Since our error message text is too long, we'll cut it into two // and display it as two error messages. // // // Log an error to the cluster log. // (ClusResLogEvent)( m_hResource , LOG_ERROR , L"%1 entry point did not complete execution in a timely manner. " L"This could be due to an infinite loop or a hang in this entry point, or the pending timeout may be too short for this resource. " L"Please review the script code for this entry point to make sure there is no infinite loop or a hang in it, and then consider " L"increasing the resource pending timeout value if necessary.\n" , m_pszHangEntryPoint ); // // Log an error to the cluster log. // (ClusResLogEvent)( m_hResource , LOG_ERROR , L"In a command shell, run \"cluster res \"%1\" /prop PersistentState=0\" " L"to disable this resource, and then run \"net stop clussvc\" to stop the cluster service. Ensure that any problem in the script code is fixed. " L"Then run \"net start clussvc\" to start the cluster service. If necessary, ensure that the pending time out is increased before bringing the " L"resource online again.\n" , m_pszName ); // // Log an error to the system event log. // ClusterLogEvent2( LOG_CRITICAL , LOG_CURRENT_MODULE , __FILE__ , __LINE__ , RES_GENSCRIPT_TIMEOUT , 0 , NULL , m_pszName , m_pszHangEntryPoint ); } // if: ( m_msgLastExecuted != msgUNKNOWN ) else { (ClusResLogEvent)( m_hResource , LOG_ERROR , L"HrSetHangEntryPoint: Unsupported entry point. \n" ); } // else: Cleanup: HRETURN( hr ); } //*** CScriptResource::HrSetHangEntryPoint ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::WaitForMessageToComplete // // Description: // Send a message to the script thread and wait for it to complete. // // Arguments: // msgIn // pProps // // Return Values: // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::WaitForMessageToComplete( EMESSAGE msgIn , PGENSCRIPT_PROPS pProps ) { TraceFunc( "" ); HRESULT hr; BOOL fSuccess; DWORD dw; DWORD scErr; CSpinLock SerializeLock( &m_lockSerialize, INFINITE ); // // A potential hang has already been detected in this script. Therefore we // will not process any other calls to this script. // if ( m_fHangDetected == TRUE ) { LogHangMode( msgIn ); scErr = TW32( ERROR_TIMEOUT ); hr = HRESULT_FROM_WIN32( scErr ); goto Cleanup; } // if: ( m_fHangDetected == TRUE ) if ( m_fPendingTimeoutChanged == TRUE ) { // // Read the new pending timeout from the cluster hive. // m_dwPendingTimeout = DwGetResourcePendingTimeout(); m_fPendingTimeoutChanged = FALSE; } // if: pending timeout has changed. // // Acquire the serialization lock. // hr = THR( SerializeLock.AcquireLock() ); if ( FAILED( hr ) ) { // // Can't "goto Error" because we didn't acquire the lock. // LogError( hr, L"WaitForMessageToComplete() failed to acquire the serialization lock." ); goto Cleanup; } // // Wait for the script thread to be "done." // dw = WaitForSingleObject( m_hEventDone, m_dwPendingTimeout ); if ( dw == WAIT_TIMEOUT ) { m_fHangDetected = TRUE; hr = HrSetHangEntryPoint(); if ( FAILED( hr ) ) { goto Error; } scErr = TW32( ERROR_TIMEOUT ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // if: ( dw == WAIT_TIMEOUT ) else if ( dw != WAIT_OBJECT_0 ) { scErr = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // else if: ( dw != WAIT_OBJECT_0 ) // // Reset the done event to indicate that the thread is not busy. // fSuccess = ResetEvent( m_hEventDone ); if ( fSuccess == FALSE ) { scErr = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // // Store the message in the common memory buffer. // m_msg = msgIn; m_pProps = pProps; // // Signal the script thread to process the message. // fSuccess = SetEvent( m_hEventWait ); if ( fSuccess == FALSE ) { scErr = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // // Wait for the thread to complete. // dw = WaitForSingleObject( m_hEventDone, m_dwPendingTimeout ); if ( dw == WAIT_TIMEOUT ) { m_fHangDetected = TRUE; hr = HrSetHangEntryPoint(); if ( FAILED( hr ) ) { goto Error; } scErr = TW32( ERROR_TIMEOUT ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // if: ( dw == WAIT_TIMEOUT ) else if ( dw != WAIT_OBJECT_0 ) { scErr = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // else if: ( dw != WAIT_OBJECT_0 ) // // Get the result of the task from the common buffer. // hr = m_hr; ReleaseLockAndCleanup: SerializeLock.ReleaseLock(); Cleanup: m_pProps = NULL; HRETURN( hr ); Error: LogError( hr, L"WaitForMessageToComplete() failed.\n" ); goto ReleaseLockAndCleanup; } //*** CScriptResource::WaitForMessageToComplete ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::LogError // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CScriptResource::LogError( HRESULT hrIn , LPCWSTR pszPrefixIn ) { TraceFunc1( "hrIn = 0x%08x", hrIn ); Assert( pszPrefixIn != NULL ); static WCHAR s_szFormat[] = L"HRESULT: 0x%1!08x!\n"; LPWSTR pszFormat = NULL; size_t cchAlloc; HRESULT hr = S_OK; TraceMsg( mtfCALLS, "%ws failed. HRESULT: 0x%08x\n", m_pszName, hrIn ); cchAlloc = RTL_NUMBER_OF( s_szFormat ) + wcslen( pszPrefixIn ); pszFormat = new WCHAR[ cchAlloc ]; if ( pszFormat == NULL ) { THR( E_OUTOFMEMORY ); goto Cleanup; } hr = THR( StringCchPrintfW( pszFormat, cchAlloc, L"%ws %ws", pszPrefixIn, s_szFormat ) ); if ( FAILED( hr ) ) { goto Cleanup; } // if: StringCchPrintfW failed. (ClusResLogEvent)( m_hResource, LOG_ERROR, pszFormat, hrIn ); Cleanup: delete [] pszFormat; HRETURN( hr ); } //*** CScriptResource::LogError ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::LogScriptError // //-- ////////////////////////////////////////////////////////////////////////////// STDMETHODIMP CScriptResource::LogScriptError( EXCEPINFO ei ) { TraceFunc( "" ); HRESULT hr; if ( ei.pfnDeferredFillIn != NULL ) { hr = THR( ei.pfnDeferredFillIn( &ei ) ); } TraceMsg( mtfCALLS, "%ws failed.\nError: %u\nSource: %ws\nDescription: %ws\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! (0x%1!08.8x!) - Description: %2!ws! (Source: %3!ws!)\n", ( ei.wCode == 0 ? ei.scode : ei.wCode ), ( ei.bstrDescription == NULL ? L"" : ei.bstrDescription ), ( ei.bstrSource == NULL ? L"" : ei.bstrSource ) ); HRETURN( S_OK ); } //*** CScriptResource::LogScriptError ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::HrGetDispIDs // // Description: // Get the DISPIDs for the entry points in the script. // // Arguments: // None. // // Return Values: // S_OK Operation succeeded. // Other HRESULTs. //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::HrGetDispIDs( void ) { TraceFunc( "" ); HRESULT hr; LPWSTR pszCommand; Assert( m_pidm != NULL ); // // Get DISPIDs for each method we will call. // pszCommand = L"Open"; hr = THR( m_pidm->GetIDsOfNames( IID_NULL, &pszCommand, 1, LOCALE_USER_DEFAULT, &m_dispidOpen ) ); if ( hr == DISP_E_UNKNOWNNAME ) { m_dispidOpen = DISPID_UNKNOWN; } else if ( FAILED( hr ) ) { goto Cleanup; } 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 Cleanup; } 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 Cleanup; } 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 Cleanup; } 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 Cleanup; } pszCommand = L"LooksAlive"; hr = THR( m_pidm->GetIDsOfNames( IID_NULL, &pszCommand, 1, LOCALE_USER_DEFAULT, &m_dispidLooksAlive ) ); if ( FAILED( hr ) ) { // // If there's no LooksAlive entry point in the script. // if ( hr == DISP_E_UNKNOWNNAME ) { m_dispidLooksAlive = DISPID_UNKNOWN; hr = DISP_E_MEMBERNOTFOUND; (ClusResLogEvent)( m_hResource , LOG_ERROR , L"%1 did not implement LooksAlive() script entry point. This is a required script entry point.\n" , m_pszName ); } goto Cleanup; } pszCommand = L"IsAlive"; hr = THR( m_pidm->GetIDsOfNames( IID_NULL, &pszCommand, 1, LOCALE_USER_DEFAULT, &m_dispidIsAlive ) ); if ( FAILED( hr ) ) { // // If there's no IsAlive entry point in the script. // if ( hr == DISP_E_UNKNOWNNAME ) { m_dispidIsAlive = DISPID_UNKNOWN; hr = DISP_E_MEMBERNOTFOUND; (ClusResLogEvent)( m_hResource , LOG_ERROR , L"%1 did not implement IsAlive() script entry point. This is a required script entry point.\n" , m_pszName ); } goto Cleanup; } // // Don't return DISP_E_UNKNOWNNAME to caller. // hr = S_OK; Cleanup: HRETURN( hr ); } //*** CScriptResource::HrGetDispIDs ////////////////////////////////////////////////////////////////////////////// // // CScriptResource::HrLoadScriptFile // ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::HrLoadScriptFile( void ) { TraceFunc( "" ); HRESULT hr; DWORD scErr; DWORD dwLow; DWORD dwRead; VARIANT varResult; EXCEPINFO ei; BOOL fSuccess; HANDLE hFile = INVALID_HANDLE_VALUE; LPSTR paszText = NULL; LPWSTR pszScriptText = NULL; Assert( m_hScriptFile == INVALID_HANDLE_VALUE ); // // Open the script file. // hFile = CreateFile( m_pszScriptFilePath , GENERIC_READ , FILE_SHARE_READ , NULL , OPEN_EXISTING , 0 , NULL ); if ( hFile == INVALID_HANDLE_VALUE ) { scErr = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // if: failed to open // // Figure out its size. // dwLow = GetFileSize( hFile, NULL ); if ( dwLow == -1 ) { scErr = TW32( GetLastError() ); hr = THR( HRESULT_FROM_WIN32( scErr ) ); goto Error; } // if: failed to figure out size else if ( dwLow == -2 ) { hr = THR( E_OUTOFMEMORY ); goto Error; } // // Make a buffer big enough to hold it. // dwLow++; // add one for trailing NULL. paszText = reinterpret_cast( TraceAlloc( LMEM_FIXED, dwLow ) ); if ( paszText == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Error; } // // Read the script into memory. // fSuccess = ReadFile( hFile, paszText, dwLow - 1, &dwRead, NULL ); if ( fSuccess == FALSE ) { scErr = TW32( GetLastError() ); hr = THR( HRESULT_FROM_WIN32( scErr ) ); goto Error; } // if: failed if ( dwRead == - 1 ) { hr = THR( E_OUTOFMEMORY ); goto Error; } if ( dwLow - 1 != dwRead ) { hr = THR( E_OUTOFMEMORY ); // TODO: figure out a better error code. goto Error; } // // Make sure it is terminated. // paszText[ dwRead ] = '\0'; // // Make a buffer to convert the text into UNICODE. // dwRead++; pszScriptText = reinterpret_cast( TraceAlloc( LMEM_FIXED, dwRead * sizeof(WCHAR) ) ); if ( pszScriptText == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Error; } // // Convert it to UNICODE. // Assert( lstrlenA( paszText ) + 1 == (signed)dwRead ); int cchWideFormat = MultiByteToWideChar( CP_ACP , 0 , paszText , -1 , pszScriptText , dwRead ); if ( cchWideFormat == 0 ) { scErr = TW32( GetLastError() ); hr = THR( HRESULT_FROM_WIN32( scErr ) ); goto Error; } // // 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 ) { LogScriptError( ei ); goto Error; } else if ( FAILED( hr ) ) { goto Error; } // // Get DISPIDs for each method in the script that we will call. // hr = THR( HrGetDispIDs() ); if ( FAILED( hr ) ) { goto Error; } // // Save the file handle to keep it open while we are using it. // Set hFile so that the file won't be closed below. // m_hScriptFile = hFile; hFile = INVALID_HANDLE_VALUE; (ClusResLogEvent)( m_hResource, LOG_INFORMATION, L"Loaded script '%1!ws!' successfully.\n", m_pszScriptFilePath ); Cleanup: VariantClear( &varResult ); 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: (ClusResLogEvent)( m_hResource, LOG_ERROR, L"Error loading script '%1!ws!'. HRESULT: 0x%2!08x!\n", m_pszScriptFilePath, hr ); goto Cleanup; } //*** CScriptResource::HrLoadScriptFile ///////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::UnLoadScriptFile // // Description: // Unload the script file and close the file. // // Arguments: // None. // // Return Values: // None. // //-- ///////////////////////////////////////////////////////////////////////////// void CScriptResource::UnloadScriptFile( void ) { TraceFunc( "" ); 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; if ( m_hScriptFile != INVALID_HANDLE_VALUE ) { CloseHandle( m_hScriptFile ); m_hScriptFile = INVALID_HANDLE_VALUE; (ClusResLogEvent)( m_hResource, LOG_INFORMATION, L"Unloaded script '%1!ws!' successfully.\n", m_pszScriptFilePath ); } // if: file is open TraceFuncExit(); } //*** CScriptResource::UnloadScriptFile ///////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::S_ThreadProc // //-- ///////////////////////////////////////////////////////////////////////////// DWORD WINAPI CScriptResource::S_ThreadProc( LPVOID pParam ) { TraceFunc( "" ); HRESULT hr; DWORD dw; DWORD scErr; BOOL fSuccess; 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. // fSuccess = SetEvent( pscript->m_hEventDone ); if ( fSuccess == FALSE ) { scErr = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // // 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. // fSuccess = ResetEvent( pscript->m_hEventWait ); if ( fSuccess == FALSE ) { scErr = TW32( GetLastError() ); hr = HRESULT_FROM_WIN32( scErr ); goto Error; } // // 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 msgSETPRIVATEPROPERTIES: pscript->m_hr = STHR( pscript->OnSetPrivateProperties( pscript->m_pProps ) ); break; case msgDIE: // // This means the resource is being released. // goto Cleanup; } // switch: on message } // spin forever Cleanup: CoUninitialize(); HRETURN( hr ); Error: pscript->LogError( hr, L"S_ThreadProc() failed." ); goto Cleanup; } //*** CScriptResource::S_ThreadProc ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::HrInvoke // // Description: // Invoke a script method. // // Arguments: // dispidIn - ID of the method to call. // msgIn - Used in figuring out which entry point is being executed. // pvarInout - Variant in which to return the results of the call. // fRequiredIn - TRUE = method must exist, FALSE = method doesn't have to exist. // // Return Values: // S_OK - Operation completed successfully. // DISP_E_MEMBERNOTFOUND -Method not implemented by the script. // Other HRESULTs. // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::HrInvoke( DISPID dispidIn , EMESSAGE msgIn , VARIANT * pvarInout // = NULL , BOOL fRequiredIn // = FALSE ) { TraceFunc( "" ); HRESULT hr = S_OK; EXCEPINFO ei; VARIANT varResult; VARIANT * pvarResult = pvarInout; DISPPARAMS dispparamsNoArgs = { NULL, NULL, 0, 0 }; Assert( m_pidm != NULL ); VariantInit( &varResult ); if ( pvarInout == NULL ) { pvarResult = &varResult; } if ( dispidIn != DISPID_UNKNOWN ) { m_msgLastExecuted = msgIn; hr = m_pidm->Invoke( dispidIn , IID_NULL , LOCALE_USER_DEFAULT , DISPATCH_METHOD , &dispparamsNoArgs , pvarResult , &ei , NULL ); if ( hr == DISP_E_EXCEPTION ) { THR( hr ); LogScriptError( ei ); } else if ( FAILED( hr ) ) { LogError( hr, L"Failed to invoke a method in the script." ); } } // if: entry point is known else { // // If this is a required method in the script. // if ( fRequiredIn == TRUE ) { (ClusResLogEvent)( m_hResource , LOG_ERROR , L"%1 entry point is not implemented in the script. This is a required entry point.\n" , g_rgpszScriptEntryPointNames[ msgIn ] ); hr = DISP_E_MEMBERNOTFOUND; } // Log an error message if this is a required entry point, and fail. else { (ClusResLogEvent)( m_hResource , LOG_INFORMATION , L"%1 entry point is not implemented in the script. It is not required but recommended to have this entry point.\n" , g_rgpszScriptEntryPointNames[ msgIn ] ); hr = S_OK; } // Log an information message if the method is not required but missing in the script. } // if: method does not exist in the script VariantClear( &varResult ); HRETURN( hr ); } //*** CScriptResource::HrInvoke ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::ScTranslateVariantReturnValue // // Description: // Translates a numeric variant value to a status code. // // Arguments: // varResultIn - Variant that holds the return value of a script entry point. // vTypeIn - Type of the variant. // // Return Values: // DWORD value of the variant. // //-- ////////////////////////////////////////////////////////////////////////////// DWORD CScriptResource::ScTranslateVariantReturnValue( VARIANT varResultIn , VARTYPE vTypeIn ) { DWORD sc = ERROR_SUCCESS; switch ( vTypeIn ) { case VT_I1 : sc = (DWORD) V_I1( &varResultIn ); break; case VT_I2 : sc = (DWORD) V_I2( &varResultIn ); break; case VT_I4 : sc = (DWORD) V_I4( &varResultIn ); break; case VT_I8 : sc = (DWORD) V_I8( &varResultIn ); break; case VT_UI1 : sc = (DWORD) V_UI1( &varResultIn ); break; case VT_UI2 : sc = (DWORD) V_UI2( &varResultIn ); break; case VT_UI4 : sc = (DWORD) V_UI4( &varResultIn ); break; case VT_UI8 : sc = (DWORD) V_UI8( &varResultIn ); break; case VT_INT : sc = (DWORD) V_INT( &varResultIn ); break; case VT_UINT : sc = (DWORD) V_UINT( &varResultIn ); break; case VT_R4 : sc = (DWORD) V_R4( &varResultIn ); break; case VT_R8 : sc = (DWORD) V_R8( &varResultIn ); break; } // switch( vTypeIn ) return sc; } //*** CScriptResource::ScTranslateVariantReturnValue ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::HrProcessResult // // Description: // Processes and logs the return value that is stored in varResultIn and // generates an HRESULT from the return value. // // Arguments: // varResultIn - Variant that holds the return value of a script entry point. // msgIn - Used in figuring out which entry point that was executed. // // Return Values: // S_OK - Script entry point (i.e. Online) was executed successfully. // S_FALSE - Script entry point returned an error. // Other HRESULTs - Script entry point returned an error. // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::HrProcessResult( VARIANT varResultIn, EMESSAGE msgIn ) { TraceFunc( "" ); HRESULT hr = S_OK; DWORD dwReturnValue = 0; VARTYPE vType = V_VT( &varResultIn ); // // Get the return value from varResultIn. // switch ( vType ) { case VT_BOOL : if ( V_BOOL( &varResultIn ) == VARIANT_FALSE ) // FALSE was returned { // // Are we processing an IsAlive/LooksAlive return value? // if ( ( msgIn == msgISALIVE ) || ( msgIn == msgLOOKSALIVE ) ) { hr = S_FALSE; } // if: processing IsAlive/LooksAlive else { hr = HRESULT_FROM_WIN32( TW32( ERROR_RESOURCE_FAILED ) ); } // else: processing IsAlive/LooksAlive // // Log the FALSE return value. // (ClusResLogEvent)( m_hResource , LOG_ERROR , L"'%1!ws!' script entry point returned FALSE.'\n" , g_rgpszScriptEntryPointNames[ msgIn ] ); } // if: Return value is FALSE break; case VT_I1 : case VT_I2 : case VT_I4 : case VT_I8 : case VT_UI1 : case VT_UI2 : case VT_UI4 : case VT_UI8 : case VT_INT : case VT_UINT : case VT_R4 : case VT_R8 : dwReturnValue = TW32( ScTranslateVariantReturnValue( varResultIn, vType ) ); // // Log the return value on failure. // if ( dwReturnValue != 0 ) { (ClusResLogEvent)( m_hResource , LOG_ERROR , L"'%1!ws!' script entry point returned '%2!d!'.\n" , g_rgpszScriptEntryPointNames[ msgIn ] , dwReturnValue ); } hr = HRESULT_FROM_WIN32( dwReturnValue ); break; case VT_BSTR : // A string was returned, so let's just log it. (ClusResLogEvent)( m_hResource , LOG_INFORMATION , L"'%1!ws!' script entry point returned '%2!ws!'.\n" , g_rgpszScriptEntryPointNames[ msgIn ] , V_BSTR( &varResultIn ) ); break; case VT_NULL : // NULL was returned, will not treat this as an error (ClusResLogEvent)( m_hResource , LOG_INFORMATION , L"'%1!ws!' script entry point returned NULL.'\n" , g_rgpszScriptEntryPointNames[ msgIn ] ); break; case VT_EMPTY : // No return value. (ClusResLogEvent)( m_hResource , LOG_INFORMATION , L"'%1!ws!' script entry point did not return a value.'\n" , g_rgpszScriptEntryPointNames[ msgIn ] ); break; default: // Unsupported return type. (ClusResLogEvent)( m_hResource , LOG_INFORMATION , L"'%1!ws!' script entry point returned a value type is not supported. The return value will be ignored.'\n" , g_rgpszScriptEntryPointNames[ msgIn ] ); break; } // switch ( V_VT( &varResultIn ) ) if ( FAILED( hr ) ) { (ClusResLogEvent)( m_hResource , LOG_ERROR , L"Return value of '%1!ws!' script entry point caused HRESULT to be set to 0x%2!08x!.\n" , g_rgpszScriptEntryPointNames[ msgIn ] , hr ); } // if: ( FAILED( hr ) ) else if ( hr != S_OK ) { (ClusResLogEvent)( m_hResource , LOG_INFORMATION , L"Return value of '%1!ws!' script entry point caused HRESULT to be set to 0x%2!08x!.\n" , g_rgpszScriptEntryPointNames[ msgIn ] , hr ); } // else: ( FAILED( hr ) ) HRETURN( hr ); } //*** CScriptResource::HrProcessResult ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::OnOpen // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::OnOpen( void ) { TraceFunc( "" ); HRESULT hr = S_OK; HRESULT hrOpen = S_OK; HRESULT hrClose = S_OK; VARIANT varResultOpen; VARIANT varResultClose; VariantInit( &varResultOpen ); VariantInit( &varResultClose ); // // Get the script file path from the cluster database if we don't already have it. // if ( m_pszScriptFilePath == NULL ) { hr = HrGetScriptFilePath(); if ( FAILED( hr ) ) { if ( ( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) ) || ( hr == HRESULT_FROM_WIN32( ERROR_KEY_DELETED ) ) ) { // This can happen when the resource is first created since the // ScriptFilePath property has not been specified yet. hr = S_OK; } THR( hr ); goto Cleanup; } } // if: no script file path // // If the script file path is set. // if ( m_pszScriptFilePath != NULL ) { // // Load the script engine for the specified script. // hr = THR( HrLoadScriptEngine() ); if ( FAILED( hr ) ) { goto Cleanup; } // // Open the script file and parse it // hr = THR( HrLoadScriptFile() ); if ( FAILED( hr ) ) { goto Cleanup; } // // Call the Open routine of the script if there's one. // Call the Close routine as well since we are going to be unloading the script. // hrOpen = THR( HrInvoke( m_dispidOpen, msgOPEN, &varResultOpen, FALSE /* fRequiredIn */ ) ); hrClose = THR( HrInvoke( m_dispidClose, msgCLOSE, &varResultClose, FALSE /* fRequiredIn */ ) ); if ( FAILED( hrOpen ) ) { hr = hrOpen; goto Cleanup; } // if: ( FAILED( hrOpen ) ) else if ( FAILED( hrClose ) ) { hr = hrClose; goto Cleanup; } // elseif: ( FAILED( hrClose ) ) // // We only care about the return value of Open. // We don't care about the return value of Close, // however processing the return value of Close // might log an entry to the log file. // hr = HrProcessResult( varResultOpen, msgOPEN ); hrClose = HrProcessResult( varResultClose, msgCLOSE ); if ( FAILED( hr ) ) { goto Cleanup; } // if: FAILED( hr ) } // if: script file path is set Cleanup: // // Unload the script and the script engine. Note they may not be loaded // but these routines are safe to call either way. // UnloadScriptFile(); UnloadScriptEngine(); VariantClear( &varResultOpen ); VariantClear( &varResultClose ); HRETURN( hr ); } //*** CScriptResource::OnOpen ////////////////////////////////////////////////////////////////////////////// // // HRESULT // CScriptResource::OnClose // ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::OnClose( void ) { TraceFunc( "" ); HRESULT hr = S_OK; HRESULT hrOpen = S_OK; HRESULT hrClose = S_OK; BOOL fCallOpen = FALSE; VARIANT varResultOpen; VARIANT varResultClose; VariantInit( &varResultOpen ); VariantInit( &varResultClose ); // // If m_pidm is NULL call HrLoadScriptEngine to have it set. // if ( m_pidm == NULL ) { // // Get the script file path if we don't already have it. // if ( m_pszScriptFilePath == NULL ) { hr = HrGetScriptFilePath(); if ( FAILED( hr ) ) { if ( ( hr == HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ) ) || ( hr == HRESULT_FROM_WIN32( ERROR_KEY_DELETED ) ) ) { // This can happen when the resource is cancelled before the // ScriptFilePath property has not been specified. hr = S_OK; } THR( hr ); goto Cleanup; } } // if: no script file path // // Load the script engine based on the script file path. // hr = HrLoadScriptEngine(); if ( FAILED( hr ) ) { goto Cleanup; } // // We need to call open since we loaded the script. // fCallOpen = TRUE; } // if: script and script engine is not loaded if ( m_dispidClose == DISPID_UNKNOWN ) { // // Open the script file and parse it // hr = THR( HrLoadScriptFile() ); if ( FAILED( hr ) ) { goto Cleanup; } } // if: DISPID for Close not loaded // // If we loaded the script, then we need to call the script's Open method. // if ( fCallOpen ) { hrOpen = THR( HrInvoke( m_dispidOpen, msgOPEN, &varResultOpen, FALSE /* fRequiredIn */ ) ); } // // Call the script's Close method. // hrClose = THR( HrInvoke( m_dispidClose, msgCLOSE, &varResultClose, FALSE /* fRequiredIn */ ) ); if ( FAILED( hrClose ) ) { hr = hrClose; goto Cleanup; } // if: ( FAILED( hrClose ) ) else if ( FAILED( hrOpen ) ) { hr = hrOpen; goto Cleanup; } // else if: ( FAILED( hrOpen ) ) // // We don't care about the return values of Open // and Close script entry points in here, however processing the // return values below might log an entry to the log file. // hr = HrProcessResult( varResultOpen, msgOPEN ); hr = HrProcessResult( varResultClose, msgCLOSE ); hr = S_OK; Cleanup: // // Unload the script and the script engine. Note they may not be loaded // but these routines are safe to call either way. // UnloadScriptFile(); UnloadScriptEngine(); VariantClear( &varResultOpen ); VariantClear( &varResultClose ); HRETURN( hr ); } //*** CScriptResource::OnClose ////////////////////////////////////////////////////////////////////////////// //++ // // HRESULT // CScriptResource::OnOnline // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::OnOnline( void ) { TraceFunc( "" ); HRESULT hr = S_OK; HRESULT hrOpen = S_OK; HRESULT hrOnline = S_OK; VARIANT varResultOpen; VARIANT varResultOnline; VariantInit( &varResultOpen ); VariantInit( &varResultOnline ); // // Get the ScriptFilePath property. // hr = HrGetScriptFilePath(); if ( FAILED( hr ) ) { goto Cleanup; } // // Load the script engine based on the script file path. // hr = HrLoadScriptEngine(); if ( FAILED( hr ) ) { goto Cleanup; } // // Load the script file. // hr = THR( HrLoadScriptFile() ); if ( FAILED( hr ) ) { goto Cleanup; } // // Call the script's Open method since we just loaded the script. // hrOpen = THR( HrInvoke( m_dispidOpen, msgOPEN, &varResultOpen, FALSE /* fRequiredIn */ ) ); if ( FAILED( hrOpen ) ) { hr = hrOpen; goto Cleanup; } // if: FAILED( hrOpen ) // // Call the script's Online method. // hrOnline = THR( HrInvoke( m_dispidOnline, msgONLINE, &varResultOnline, FALSE /* fRequiredIn */ ) ); if ( FAILED( hrOnline ) ) { hr = hrOnline; goto Cleanup; } // if: FAILED( hrOnline ) // // We only care about the return value of Online. // We don't care about the return value of Open, // however processing the return value of Open // might log an entry to the log file. // hr = HrProcessResult( varResultOpen, msgOPEN ); hr = HrProcessResult( varResultOnline, msgONLINE ); if ( FAILED( hr ) ) { goto Cleanup; } // if: FAILED( hr ) // // Assume the resource LooksAlive... // m_fLastLooksAlive = TRUE; Cleanup: VariantClear( &varResultOpen ); VariantClear( &varResultOnline ); HRETURN( hr ); } //*** CScriptResource::OnOnline ////////////////////////////////////////////////////////////////////////////// //++ // // HRESULT // CScriptResource::OnOffline // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::OnOffline( void ) { TraceFunc( "" ); HRESULT hr = S_OK; HRESULT hrOffline = S_OK; HRESULT hrClose = S_OK; VARIANT varResultOffline; VARIANT varResultClose; VariantInit( &varResultOffline ); VariantInit( &varResultClose ); // // Call the script's Offline method. // hrOffline = THR( HrInvoke( m_dispidOffline, msgOFFLINE, &varResultOffline, FALSE /* fRequiredIn */ ) ); // // Call the script's Close method since we are going to unload the script. // hrClose = THR( HrInvoke( m_dispidClose, msgCLOSE, &varResultClose, FALSE /* fRequiredIn */ ) ); if ( FAILED( hrOffline ) ) { hr = hrOffline; goto Cleanup; } // if: ( FAILED( hrOffline ) ) else if ( FAILED( hrClose ) ) { hr = hrClose; goto Cleanup; } // else if: ( FAILED( hrClose ) ) // // We only care about the return value of Offline. // We don't care about the return value of Close, // however processing the return value of Close // might log an entry to the log file. // hr = HrProcessResult( varResultOffline, msgOFFLINE ); hrClose = HrProcessResult( varResultClose, msgCLOSE ); if ( FAILED( hr ) ) { goto Cleanup; } //if: FAILED( hr ) Cleanup: // // Unload the script and the script engine. // UnloadScriptFile(); UnloadScriptEngine(); VariantClear( &varResultOffline ); VariantClear( &varResultClose ); HRETURN( hr ); } //*** CScriptResource::OnOffline ////////////////////////////////////////////////////////////////////////////// // // HRESULT // CScriptResource::OnTerminate // ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::OnTerminate( void ) { TraceFunc( "" ); HRESULT hr = S_OK; HRESULT hrOpen = S_OK; HRESULT hrTerminate = S_OK; HRESULT hrClose = S_OK; VARIANT varResultOpen; VARIANT varResultTerminate; VARIANT varResultClose; VariantInit( &varResultOpen ); VariantInit( &varResultTerminate ); VariantInit( &varResultClose ); // // If the script engine is not loaded yet, load it now. // if ( m_pidm == NULL ) { // // Get the script file path if we don't already have it. // if ( m_pszScriptFilePath == NULL ) { hr = THR( HrGetScriptFilePath() ); if ( FAILED( hr ) ) { goto Cleanup; } } // if: no script file path // // Load the script engine based on the script file path. // hr = HrLoadScriptEngine(); if ( FAILED( hr ) ) { goto Cleanup; } // // Open the script file and parse it // hr = THR( HrLoadScriptFile() ); if ( FAILED( hr ) ) { goto Cleanup; } // // We need to call open since we loaded the script. // hrOpen = THR( HrInvoke( m_dispidOpen, msgOPEN, &varResultOpen, FALSE /* fRequiredIn */ ) ); if ( FAILED( hrOpen ) ) { hr = hrOpen; goto Cleanup; } // if: FAILED( hrOpen ) // // We don't care about the return value of Open // however processing the return value below might // log an entry to the log file. // hrOpen = HrProcessResult( varResultOpen, msgOPEN ); } // if: script and script engine is not loaded // // Call the script's Terminate method. // hrTerminate = THR( HrInvoke( m_dispidTerminate, msgTERMINATE, &varResultTerminate, FALSE /* fRequiredIn */ ) ); if ( FAILED( hrTerminate ) ) { hr = hrTerminate; goto Cleanup; } // if: ( FAILED( hrTerminate ) ) // // Call the script's Close method since we are unloading the script. // hrClose = THR( HrInvoke( m_dispidClose, msgCLOSE, &varResultClose, FALSE /* fRequiredIn */ ) ); if ( FAILED( hrClose ) ) { hr = hrClose; goto Cleanup; } // if: ( FAILED( hrClose ) ) // // We don't care about the return values of Terminate // and Close script entry points in here, however processing the // return values below might log an entry to the log file. // hrTerminate = HrProcessResult( varResultTerminate, msgTERMINATE ); hrClose = HrProcessResult( varResultClose, msgCLOSE ); hr = S_OK; Cleanup: // // Unload the script and the script engine. // UnloadScriptFile(); UnloadScriptEngine(); VariantClear( &varResultOpen ); VariantClear( &varResultTerminate ); VariantClear( &varResultClose ); HRETURN( hr ); } //*** CScriptResource::OnTerminate ////////////////////////////////////////////////////////////////////////////// // // HRESULT // CScriptResource::OnLooksAlive // ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::OnLooksAlive( void ) { TraceFunc( "" ); HRESULT hr = S_OK; VARIANT varResult; VariantInit( &varResult ); // // Call the script's LooksAlive method. // hr = THR( HrInvoke( m_dispidLooksAlive, msgLOOKSALIVE, &varResult, TRUE /* fRequiredIn */ ) ); if ( FAILED( hr ) ) { goto Cleanup; } // // Get the result of the LooksAlive call // and process it. // hr = HrProcessResult( varResult, msgLOOKSALIVE ); if ( FAILED( hr ) ) { goto Cleanup; } //if: FAILED( hr ) Cleanup: VariantClear( &varResult ); // // Only if the result of this script entry point 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 ); } //*** CScriptResource::OnLooksAlive ////////////////////////////////////////////////////////////////////////////// // // HRESULT // CScriptResource::OnIsAlive // ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::OnIsAlive( void ) { TraceFunc( "" ); HRESULT hr = S_OK; VARIANT varResult; VariantInit( &varResult ); // // Call the script's IsAlive method. // hr = THR( HrInvoke( m_dispidIsAlive, msgISALIVE, &varResult, TRUE /* fRequiredIn */ ) ); if ( FAILED( hr ) ) { goto Cleanup; } // // Get the result of the IsAlive call // and process it. // hr = HrProcessResult( varResult, msgISALIVE ); if ( FAILED( hr ) ) { goto Cleanup; } //if: FAILED( hr ) Cleanup: VariantClear( &varResult ); HRETURN( hr ); } //*** CScriptResource::OnIsAlive ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::OnSetPrivateProperties // // Description: // Handle the CLUSCTL_RESOURCE_SET_PRIVATE_PROPERTIES control code in // the script thread. // // Arguments: // // Return Values: // //-- ////////////////////////////////////////////////////////////////////////////// DWORD CScriptResource::OnSetPrivateProperties( PGENSCRIPT_PROPS pProps ) { TraceFunc( "" ); DWORD sc = ERROR_SUCCESS; HRESULT hr = S_OK; LPWSTR pszFilePath = NULL; // // Search the property list for the properties we know about. // if ( pProps != NULL ) { // // If the resource is online, we can't allow the user to set the ScriptFilePath // out from under us, so return an error. // if ( m_pidm != NULL ) { sc = ERROR_RESOURCE_ONLINE; goto Cleanup; } // if: script engine is loaded // // Expand the new script file path. // pszFilePath = ClRtlExpandEnvironmentStrings( pProps->pszScriptFilePath ); if ( pszFilePath == NULL ) { sc = TW32( ERROR_OUTOFMEMORY ); goto Cleanup; } // if: ( pszFilePath == NULL ) LocalFree( m_pszScriptFilePath ); m_pszScriptFilePath = pszFilePath; if ( m_pszScriptFilePath == NULL ) { sc = TW32( GetLastError() ); goto Cleanup; } // // Since the script is being set, we need to load it again and call Open and Close on it. // hr = THR( OnOpen() ); if ( FAILED( hr ) ) { sc = STATUS_TO_RETURN( hr ); goto Cleanup; } } // if: a property list was specified Cleanup: if ( sc == ERROR_SUCCESS ) { // // To allow the Resource Monitor to save the properties in the property list // (especially unknown properties) to the cluster database, we will return // ERROR_INVALID_FUNCTION. // sc = ERROR_INVALID_FUNCTION; } W32RETURN( sc ); } //*** CScriptResource::OnSetPrivateProperties ////////////////////////////////////////////////////////////////////////////// // // CScriptResource::HrMakeScriptEngineAssociation // // Description: // Using the filename, this method splits off the extension then // queries the registry to obtain the association and finally queries // the ScriptingEngine key under that association and allocates a // buffer containing the engine name. This engine name is suitable // for input into CLSIDFromProgID. // // Return Values: // S_OK - Success // Other HRESULTs // ////////////////////////////////////////////////////////////////////////////// #define SCRIPTENGINE_KEY_STRING L"\\ScriptEngine" HRESULT CScriptResource::HrMakeScriptEngineAssociation( void ) { TraceFunc( "" ); LPWSTR pszAssociation = NULL; LPWSTR pszEngineName = NULL; HKEY hKey = NULL; WCHAR szExtension[ _MAX_EXT ]; DWORD scErr = ERROR_SUCCESS; DWORD dwType; DWORD cbAssociationSize; DWORD cbEngineNameSize; DWORD dwNumChars; size_t cchBufSize; HRESULT hr = S_OK; TraceFree( m_pszScriptEngine ); m_pszScriptEngine = NULL; // // First split the path to get the extension. // _wsplitpath( m_pszScriptFilePath, NULL, NULL, NULL, szExtension ); if ( szExtension[ 0 ] == L'\0' ) { hr = HRESULT_FROM_WIN32( TW32( ERROR_FILE_NOT_FOUND ) ); goto Cleanup; } // // Go to the HKEY_CLASSES_ROOT\szExtenstion registry key. // scErr = TW32( RegOpenKeyExW( HKEY_CLASSES_ROOT // handle to open key , szExtension // subkey name , 0 // reserved , KEY_READ // security access desired. , &hKey // key handle returned ) ); if ( scErr == ERROR_FILE_NOT_FOUND ) // The fix for bug 737013 in windows database. { hr = THR( MK_E_INVALIDEXTENSION ); goto Cleanup; } // if: ( scErr == ERROR_FILE_NOT_FOUND ) else if ( scErr != ERROR_SUCCESS ) { goto MakeHr; } // // Query the value to get the size of the buffer to allocate. // NB cbSize contains the size including the '\0' // scErr = TW32( RegQueryValueExW( hKey // handle to key , NULL // value name , 0 // reserved , &dwType // type buffer , NULL // data buffer , &cbAssociationSize // size of data buffer ) ); if ( scErr == ERROR_FILE_NOT_FOUND ) // The fix for bug 737013 in windows database. { hr = THR( MK_E_INVALIDEXTENSION ); goto Cleanup; } // if: ( scErr == ERROR_FILE_NOT_FOUND ) else if ( scErr != ERROR_SUCCESS ) { goto MakeHr; } if ( dwType != REG_SZ ) { hr = HRESULT_FROM_WIN32( TW32( ERROR_FILE_NOT_FOUND ) ); goto Cleanup; } dwNumChars = cbAssociationSize / sizeof( WCHAR ); cchBufSize = static_cast ( cbAssociationSize ) + sizeof( SCRIPTENGINE_KEY_STRING ); pszAssociation = (LPWSTR) TraceAlloc( GPTR, static_cast ( cchBufSize ) ); if ( pszAssociation == NULL ) { hr = HRESULT_FROM_WIN32( TW32( ERROR_NOT_ENOUGH_MEMORY ) ); goto Cleanup; } // // Get the value for real. // scErr = TW32( RegQueryValueExW( hKey // handle to key , NULL // value name , 0 // reserved , &dwType // type buffer , (LPBYTE) pszAssociation // data buffer , &cbAssociationSize // size of data buffer ) ); if ( scErr == ERROR_FILE_NOT_FOUND ) // The fix for bug 737013 in windows database. { hr = THR( MK_E_INVALIDEXTENSION ); goto Cleanup; } // if: ( scErr == ERROR_FILE_NOT_FOUND ) else if ( scErr != ERROR_SUCCESS ) { goto MakeHr; } if ( dwType != REG_SZ ) { hr = HRESULT_FROM_WIN32( TW32( ERROR_FILE_NOT_FOUND ) ); goto Cleanup; } scErr = TW32( RegCloseKey( hKey ) ); if ( scErr != ERROR_SUCCESS ) { goto MakeHr; } hKey = NULL; // // Take the data and make a key with \ScriptEngine on the end. If // we find this then we can use the file. // hr = THR( StringCchPrintfW( &pszAssociation[ dwNumChars - 1 ], cchBufSize - ( dwNumChars - 1 ), SCRIPTENGINE_KEY_STRING ) ); if ( FAILED( hr ) ) { goto Cleanup; } // if: FAILED( hr ) scErr = TW32( RegOpenKeyExW( HKEY_CLASSES_ROOT // handle to open key , pszAssociation // subkey name , 0 // reserved , KEY_READ // security access , &hKey // key handle ) ); if ( scErr == ERROR_FILE_NOT_FOUND ) // The fix for bug 737013 in windows database. { hr = THR( MK_E_INVALIDEXTENSION ); goto Cleanup; } // if: ( scErr == ERROR_FILE_NOT_FOUND ) else if ( scErr != ERROR_SUCCESS ) { goto MakeHr; } // else if: ( scErr != ERROR_SUCCESS ) scErr = TW32( RegQueryValueExW( hKey // handle to key , NULL // value name , 0 // reserved , &dwType // type buffer , NULL // data buffer , &cbEngineNameSize // size of data buffer ) ); if ( scErr == ERROR_FILE_NOT_FOUND ) // The fix for bug 737013 in windows database. { hr = THR( MK_E_INVALIDEXTENSION ); goto Cleanup; } // if: ( scErr == ERROR_FILE_NOT_FOUND ) else if ( scErr != ERROR_SUCCESS ) { goto MakeHr; } if ( dwType != REG_SZ ) { hr = HRESULT_FROM_WIN32( TW32( ERROR_FILE_NOT_FOUND ) ); goto Cleanup; } dwNumChars = cbEngineNameSize / sizeof( WCHAR ); pszEngineName = (LPWSTR) TraceAlloc( GPTR, cbEngineNameSize ); if ( NULL == pszEngineName ) { hr = HRESULT_FROM_WIN32( TW32( ERROR_NOT_ENOUGH_MEMORY ) ); goto Cleanup; } pszEngineName[ dwNumChars - 1 ] = L'\0'; // // Get the value for real. // scErr = TW32( RegQueryValueExW( hKey // handle to key , NULL // value name , 0 // reserved , &dwType // type buffer , (LPBYTE) pszEngineName // data buffer , &cbEngineNameSize // size of data buffer ) ); if ( scErr == ERROR_FILE_NOT_FOUND ) // The fix for bug 737013 in windows database. { hr = THR( MK_E_INVALIDEXTENSION ); goto Cleanup; } // if: ( scErr == ERROR_FILE_NOT_FOUND ) else if ( scErr != ERROR_SUCCESS ) { goto MakeHr; } if ( dwType != REG_SZ ) { hr = HRESULT_FROM_WIN32( TW32( ERROR_FILE_NOT_FOUND ) ); goto Cleanup; } scErr = RegCloseKey( hKey ); if ( scErr != ERROR_SUCCESS ) { goto MakeHr; } hKey = NULL; goto Cleanup; MakeHr: hr = HRESULT_FROM_WIN32( TW32( scErr ) ); goto Cleanup; Cleanup: if ( FAILED( hr ) ) { TraceFree( pszEngineName ); pszEngineName = NULL; } else { m_pszScriptEngine = pszEngineName; } if ( hKey != NULL ) { (void) RegCloseKey( hKey ); } TraceFree( pszAssociation ); HRETURN( hr ); } //*** CScriptResource::MakeScriptEngineAssociation #undef SCRIPTENGINE_KEY_STRING ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::HrGetScriptFilePath // // Description: // Reads the registry, extracts the script file path and sets m_pszScriptFilePath. // // Arguments: // None // // Return Values: // S_OK - Script file path retrieved successfully. // ERROR_FILE_NOT_FOUND - Script file path not set yet. // Other HRESULTs. // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::HrGetScriptFilePath( void ) { TraceFunc( "" ); HRESULT hr = S_OK; DWORD scErr; DWORD cbSize; DWORD dwType; LPWSTR pszScriptFilePathTmp = NULL; // // Figure out how big the filepath is. // scErr = ClusterRegQueryValue( m_hkeyParams, CLUSREG_NAME_GENSCRIPT_SCRIPT_FILEPATH, NULL, NULL, &cbSize ); if ( scErr != ERROR_SUCCESS ) { hr = HRESULT_FROM_WIN32( scErr ); if ( ( scErr == ERROR_FILE_NOT_FOUND ) || ( scErr == ERROR_KEY_DELETED ) ) { goto Cleanup; // We don't want to log this error, goto Cleanup. } else { TW32( scErr ); goto Error; } } // if: failed // // Make a buffer big enough. // cbSize += sizeof( L'\0' ); pszScriptFilePathTmp = reinterpret_cast( TraceAlloc( LMEM_FIXED, cbSize ) ); if ( pszScriptFilePathTmp == NULL ) { hr = THR( E_OUTOFMEMORY ); goto Error; } // // Grab it for real this time, // scErr = TW32( ClusterRegQueryValue( m_hkeyParams , CLUSREG_NAME_GENSCRIPT_SCRIPT_FILEPATH , &dwType , reinterpret_cast( pszScriptFilePathTmp ) , &cbSize ) ); if ( scErr != ERROR_SUCCESS ) { hr = HRESULT_FROM_WIN32( scErr ); goto Error; } Assert( ( dwType == REG_SZ ) || ( dwType == REG_EXPAND_SZ ) ); // // If we have some old data from before then free this first. // LocalFree( m_pszScriptFilePath ); m_pszScriptFilePath = ClRtlExpandEnvironmentStrings( pszScriptFilePathTmp ); if ( m_pszScriptFilePath == NULL ) { hr = HRESULT_FROM_WIN32( TW32( GetLastError() ) ); goto Error; } Cleanup: if ( pszScriptFilePathTmp != NULL ) { TraceFree( pszScriptFilePathTmp ); } // if: pszScriptFilePathTmp HRETURN( hr ); Error: LogError( hr, L"Error getting the script file path property from the cluster database." ); goto Cleanup; } //*** CScriptResource::HrGetScriptFilePath ////////////////////////////////////////////////////////////////////////////// //++ // // CScriptResource::HrLoadScriptEngine // // Description: // Connects to the script engine associated with the script passed in. // // Arguments: // None. // // Return Values: // S_OK - connected OK. // Failure status - local cleanup performed. // //-- ////////////////////////////////////////////////////////////////////////////// HRESULT CScriptResource::HrLoadScriptEngine( void ) { TraceFunc( "" ); HRESULT hr = S_OK; CLSID clsidScriptEngine; CActiveScriptSite * psite = NULL; Assert( m_pszScriptFilePath != NULL ); Assert( m_pass == NULL ); Assert( m_pasp == NULL ); Assert( m_pas == NULL ); Assert( m_pidm == NULL ); // // Create the scripting site. // psite = new CActiveScriptSite( m_hResource, ClusResLogEvent, m_hkeyParams, m_pszName ); if ( psite == NULL ) { hr = THR( E_OUTOFMEMORY ); LogError( hr, L"Error allocating memory for the active script site object instance." ); goto Cleanup; } hr = THR( psite->QueryInterface( IID_IActiveScriptSite, reinterpret_cast< void ** >( &m_pass ) ) ); if ( FAILED( hr ) ) { LogError( hr, L"Error getting the active script site interface." ); goto Cleanup; } // // Find the Active Engine. // if ( m_pszScriptFilePath == NULL ) { (ClusResLogEvent)( m_hResource, LOG_ERROR, L"HrLoadScriptEngine: No script file path set\n" ); hr = HRESULT_FROM_WIN32( TW32( ERROR_FILE_NOT_FOUND ) ); goto Cleanup; } // if: no script file path specified else { // // Find the program associated with the extension. // hr = HrMakeScriptEngineAssociation(); if ( FAILED( hr ) ) { LogError( hr, L"Error getting script engine." ); goto Cleanup; } hr = THR( CLSIDFromProgID( m_pszScriptEngine, &clsidScriptEngine ) ); if ( FAILED( hr ) ) { LogError( hr, L"Error getting the ProgID for the script engine." ); goto Cleanup; } } // else: script file path specified // // Create an instance of it. // TraceDo( hr = THR( CoCreateInstance( clsidScriptEngine , NULL , ( CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER ) , IID_IActiveScriptParse , reinterpret_cast< void ** >( &m_pasp ) ) ) ); if ( FAILED( hr ) ) { LogError( hr, L"Error creating the script engine object instance." ); goto Cleanup; } m_pasp = TraceInterface( L"Active Script Engine", IActiveScriptParse, m_pasp, 1 ); TraceDo( hr = THR( m_pasp->QueryInterface( IID_IActiveScript, reinterpret_cast ( &m_pas ) ) ) ); if ( FAILED( hr ) ) { LogError( hr, L"Error getting the active script interface from the script parse object." ); goto Cleanup; } m_pas = TraceInterface( L"Active Script Engine", IActiveScript, m_pas, 1 ); // // Initialize it. // TraceDo( hr = THR( m_pasp->InitNew() ) ); if ( FAILED( hr ) ) { LogError( hr, L"Error initializing the script site parser object." ); goto Cleanup; } #if defined(DEBUG) // // Set our site. We'll give out a new tracking interface to track this separately. // { IActiveScriptSite * psiteDbg; hr = THR( m_pass->TypeSafeQI( IActiveScriptSite, &psiteDbg ) ); Assert( hr == S_OK ); TraceDo( hr = THR( m_pas->SetScriptSite( psiteDbg ) ) ); psiteDbg->Release(); // release promptly psiteDbg = NULL; if ( FAILED( hr ) ) { LogError( hr, L"Error setting the script site on the script engine." ); goto Cleanup; } } #else TraceDo( hr = THR( m_pas->SetScriptSite( m_pass ) ) ); if ( FAILED( hr ) ) { LogError( hr, L"Error setting the script site on the script engine." ); goto Cleanup; } #endif // // Add Document to the global members. // TraceDo( hr = THR( m_pas->AddNamedItem( L"Resource", SCRIPTITEM_ISVISIBLE ) ) ); if ( FAILED( hr ) ) { LogError( hr, L"Error adding the 'Resource' named item to the script object." ); goto Cleanup; } // // Connect the script. // TraceDo( hr = THR( m_pas->SetScriptState( SCRIPTSTATE_CONNECTED ) ) ); if ( FAILED( hr ) ) { LogError( hr, L"Error setting the script state on the script engine." ); goto Cleanup; } // // Get the dispatch inteface to the script. // TraceDo( hr = THR( m_pas->GetScriptDispatch( NULL, &m_pidm ) ) ); if ( FAILED( hr) ) { LogError( hr, L"Error getting the script dispatch table." ); goto Cleanup; } m_pidm = TraceInterface( L"Active Script", IDispatch, m_pidm, 1 ); hr = S_OK; (ClusResLogEvent)( m_hResource, LOG_INFORMATION, L"Loaded script engine '%1!ws!' successfully.\n", m_pszScriptEngine ); Cleanup: if ( psite != NULL ) { psite->Release(); psite = NULL; } HRETURN( hr ); } //*** CScriptResource::HrLoadScriptEngine ////////////////////////////////////////////////////////////////////////////// // // CScriptResource::UnloadScriptEngine // // Description: // Disconnects from any currently connected script engine. // // Arguments: // None. // // Return Values: // None. // ////////////////////////////////////////////////////////////////////////////// void CScriptResource::UnloadScriptEngine( void ) { TraceFunc( "" ); // // Cleanup the scripting engine. // if ( m_pszScriptEngine != NULL ) { (ClusResLogEvent)( m_hResource, LOG_INFORMATION, L"Unloaded script engine '%1!ws!' successfully.\n", m_pszScriptEngine ); TraceFree( m_pszScriptEngine ); m_pszScriptEngine = NULL; } 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 TraceFuncExit(); } //*** CScriptResource::UnloadScriptEngine