You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3420 lines
93 KiB
3420 lines
93 KiB
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// 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"<unknown>" : 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"<unknown>" : 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"<null>" : ei.bstrSource ),
|
|
( ei.bstrDescription == NULL ? L"<null>" : 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"<null>" : ei.bstrDescription ),
|
|
( ei.bstrSource == NULL ? L"<null>" : 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<LPSTR>( 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<LPWSTR>( 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<size_t> ( cbAssociationSize ) + sizeof( SCRIPTENGINE_KEY_STRING );
|
|
pszAssociation = (LPWSTR) TraceAlloc( GPTR, static_cast<DWORD> ( 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<LPWSTR>( 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<LPBYTE>( 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<void**> ( &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
|