1094 lines
29 KiB
C++
1094 lines
29 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1995
|
|
//
|
|
// File: bscript.cxx
|
|
//
|
|
// Contents: Implementation of CBServerScript
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "headers.hxx"
|
|
CScriptHost::CScriptHost(CMTScript * pMT,
|
|
BOOL fPrimary,
|
|
BOOL fDispatchOnly)
|
|
: _pMT(pMT),
|
|
_fIsPrimaryScript(fPrimary)
|
|
{
|
|
_ulRefs = 1;
|
|
|
|
VariantInit(&_vPubCache);
|
|
VariantInit(&_vPrivCache);
|
|
|
|
Assert(_dwPublicSN == 0);
|
|
Assert(_dwPrivateSN == 0);
|
|
}
|
|
|
|
CScriptHost::~CScriptHost()
|
|
{
|
|
int i;
|
|
|
|
// Any thread can call the dtor.
|
|
WHEN_DBG(_dwThreadId = GetCurrentThreadId());
|
|
AssertSz(PopScript() == S_FALSE,
|
|
"Script object not closed properly!");
|
|
|
|
VariantClear(&_vPubCache);
|
|
VariantClear(&_vPrivCache);
|
|
|
|
for (i = 0; i < _aryEvtSinks.Size(); i++)
|
|
{
|
|
_aryEvtSinks[i]->Disconnect();
|
|
}
|
|
|
|
_aryEvtSinks.ReleaseAll();
|
|
|
|
ReleaseInterface(_pTypeInfoIGlobalMTScript);
|
|
ReleaseInterface(_pTypeInfoCMTScript);
|
|
}
|
|
|
|
HRESULT
|
|
CScriptHost::QueryInterface(REFIID iid, void **ppvObj)
|
|
{
|
|
if (iid == IID_IGlobalMTScript || iid == IID_IUnknown || iid == IID_IDispatch)
|
|
{
|
|
*ppvObj = (IGlobalMTScript *)this;
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
((IUnknown *)*ppvObj)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
DWORD
|
|
CScriptHost::ThreadMain()
|
|
{
|
|
HRESULT hr;
|
|
CStr cstrScript;
|
|
VARIANT varParam;
|
|
SCRIPT_PARAMS *pscrParams;
|
|
|
|
VariantInit(&varParam);
|
|
|
|
VERIFY_THREAD();
|
|
|
|
pscrParams = (SCRIPT_PARAMS*)_pvParams;
|
|
|
|
|
|
cstrScript.Set(pscrParams->pszPath);
|
|
|
|
#if DBG == 1
|
|
char achBuf[10];
|
|
cstrScript.GetMultiByte(achBuf, 10);
|
|
SetName(achBuf);
|
|
#endif
|
|
|
|
hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED |
|
|
COINIT_DISABLE_OLE1DDE |
|
|
COINIT_SPEED_OVER_MEMORY);
|
|
if (!SUCCEEDED(hr))
|
|
{
|
|
ThreadStarted(hr); // Free our creating thread
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (pscrParams->pvarParams)
|
|
{
|
|
if (V_VT(pscrParams->pvarParams) == VT_DISPATCH)
|
|
{
|
|
// Unmarshal the IDispatch pointer being handed to us from the
|
|
// other thread.
|
|
|
|
IDispatch *pDisp;
|
|
DWORD dwCookie = V_I4(pscrParams->pvarParams);
|
|
|
|
hr = _pMT->_pGIT->GetInterfaceFromGlobal(dwCookie,
|
|
IID_IDispatch,
|
|
(LPVOID*)&pDisp);
|
|
if (!hr)
|
|
{
|
|
V_VT(&varParam) = VT_DISPATCH;
|
|
V_DISPATCH(&varParam) = pDisp;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
VariantCopy(&varParam, pscrParams->pvarParams);
|
|
}
|
|
}
|
|
|
|
// Hold a reference on ourself while the script is running
|
|
AddRef();
|
|
|
|
if (_fIsPrimaryScript)
|
|
{
|
|
|
|
hr = THR(LoadTypeLibrary());
|
|
|
|
// Ensure that ScriptMain() completes before we fire any other events.
|
|
_fDontHandleEvents = TRUE;
|
|
}
|
|
|
|
if (hr)
|
|
{
|
|
ThreadStarted(hr);
|
|
goto Cleanup;
|
|
}
|
|
hr = ExecuteTopLevelScript(cstrScript, &varParam);
|
|
if (hr)
|
|
{
|
|
ThreadStarted(hr);
|
|
TraceTag((tagError, "Failed to execute script: %x", hr));
|
|
AssertSz(!_fIsPrimaryScript, "Failed to execute script");
|
|
|
|
PostQuitMessage(0);
|
|
goto Cleanup;
|
|
}
|
|
ThreadStarted(hr);
|
|
FireEvent(DISPID_MTScript_ScriptMain, 0, NULL);
|
|
//
|
|
// Secondary scripts go away as soon as they're done.
|
|
//
|
|
if (_fIsPrimaryScript)
|
|
{
|
|
DWORD dwRet;
|
|
|
|
_fDontHandleEvents = FALSE;
|
|
|
|
dwRet = MessageEventPump(TRUE);
|
|
|
|
AssertSz(dwRet == MEP_EXIT, "NONFATAL: Invalid return value from MessageEventPump!");
|
|
}
|
|
else
|
|
{
|
|
CScriptHost *pThis = this;
|
|
|
|
PostToThread(_pMT,
|
|
MD_SECONDARYSCRIPTTERMINATE,
|
|
(LPVOID)&pThis,
|
|
sizeof(CScriptHost*));
|
|
}
|
|
|
|
Cleanup:
|
|
CloseScripts();
|
|
|
|
VariantClear(&varParam);
|
|
|
|
if (_fIsPrimaryScript)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < s_arySyncEvents.Size(); i++)
|
|
{
|
|
CloseHandle(s_arySyncEvents[i]._hEvent);
|
|
s_arySyncEvents[i]._cstrName.Free();
|
|
}
|
|
|
|
s_arySyncEvents.DeleteAll();
|
|
|
|
for (i = 0; i < (int)s_cThreadLocks; i++)
|
|
{
|
|
DeleteCriticalSection(&s_aThreadLocks[i]._csLock);
|
|
s_aThreadLocks[i]._cstrName.Free();
|
|
}
|
|
|
|
memset(&s_aThreadLocks, 0, sizeof(s_aThreadLocks));
|
|
s_cThreadLocks = 0;
|
|
}
|
|
|
|
Release();
|
|
CoUninitialize();
|
|
return 0;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScriptHost::MessageEventPump, public
|
|
//
|
|
// Synopsis: Empties our message queues (both windows' and our private
|
|
// threadcomm queue)
|
|
//
|
|
// Arguments: [fWait] -- If TRUE, will not return until an event occurs
|
|
// [cEvents] -- Count of events to monitor
|
|
// [pEvents] -- Pointer to list of event handles
|
|
// [fAll] -- If TRUE, don't return until all handles in
|
|
// pEvents are signaled.
|
|
// [dwTimeout] -- Timeout after this many ms if nothing signals
|
|
// [fNoEvents] -- If TRUE, don't fire events while waiting
|
|
//
|
|
// Returns: MEP_TIMEOUT: The given timeout period expired without any
|
|
// event objects becoming signaled. Returned only
|
|
// if dwTimeout != INFINITE
|
|
// MEP_EXIT: An event occurred which is causing this thread to
|
|
// terminate. The caller should clean up and finish
|
|
// what it's doing.
|
|
// MEP_FALLTHROUGH: Indicates that no objects signaled.
|
|
// Returned only if fWait==FALSE.
|
|
// MEP_EVENT_0: If one (or all if fAll==TRUE) of the passed-in
|
|
// event handles became signaled. The index of the
|
|
// signaled handle is added to MEP_EVENT_0. Returned
|
|
// only if one or more event handles were passed in.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DWORD
|
|
CScriptHost::MessageEventPump(BOOL fWait,
|
|
UINT cEvents /* = 0 */,
|
|
HANDLE *pEvents /* = NULL */,
|
|
BOOL fAll /* = FALSE */,
|
|
DWORD dwTimeout /* = INFINITE */,
|
|
BOOL fNoEvents /* = FALSE */)
|
|
{
|
|
CStackPtrAry<HANDLE, 5> aryHandles;
|
|
|
|
MSG msg;
|
|
DWORD dwRet;
|
|
DWORD mepReturn = MEP_FALLTHROUGH;
|
|
|
|
_int64 i64Freq = 0;
|
|
_int64 i64Time;
|
|
_int64 i64Goal = 0;
|
|
long lTimeout = dwTimeout;
|
|
BOOL fTimeout = dwTimeout != INFINITE;
|
|
|
|
if (cEvents)
|
|
{
|
|
aryHandles.CopyIndirect(cEvents, pEvents, FALSE);
|
|
}
|
|
|
|
if (_fMustExitThread)
|
|
{
|
|
return MEP_EXIT;
|
|
}
|
|
|
|
// WARNING! aryHandles will get rebuilt under certain conditions below.
|
|
// If you add code which adds handles to the array, you must update the
|
|
// code below as well!
|
|
|
|
if (!fNoEvents && !_fDontHandleEvents)
|
|
{
|
|
aryHandles.Insert(0, _hCommEvent);
|
|
}
|
|
else if (fNoEvents)
|
|
{
|
|
_fDontHandleEvents = TRUE;
|
|
}
|
|
|
|
if (fTimeout)
|
|
{
|
|
QueryPerformanceFrequency((LARGE_INTEGER*)&i64Freq);
|
|
QueryPerformanceCounter((LARGE_INTEGER*)&i64Time);
|
|
|
|
// Resolution must be at least milliseconds
|
|
Assert(i64Freq >= 1000);
|
|
|
|
// Compute the time when the timer will be complete, converted to ms
|
|
i64Goal = ((i64Time * 1000) / i64Freq) + lTimeout;
|
|
}
|
|
|
|
do
|
|
{
|
|
//
|
|
// Purge out all window messages (primarily for OLE's benefit).
|
|
//
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
if (msg.message == WM_QUIT)
|
|
{
|
|
_fMustExitThread = TRUE;
|
|
return MEP_EXIT;
|
|
}
|
|
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
if (_fMustExitThread)
|
|
{
|
|
AbortScripts();
|
|
return MEP_EXIT;
|
|
}
|
|
|
|
dwRet = MsgWaitForMultipleObjects(aryHandles.Size(),
|
|
aryHandles,
|
|
FALSE,
|
|
(fWait) ? (DWORD)lTimeout : 0,
|
|
QS_ALLINPUT);
|
|
|
|
if (dwRet == WAIT_OBJECT_0 && !_fDontHandleEvents)
|
|
{
|
|
//
|
|
// Another thread is sending us a message.
|
|
//
|
|
HandleThreadMessage();
|
|
}
|
|
else if (dwRet < WAIT_OBJECT_0 + aryHandles.Size())
|
|
{
|
|
Assert(cEvents);
|
|
|
|
int iEvent = dwRet - WAIT_OBJECT_0;
|
|
|
|
//
|
|
// One of the events the script is waiting for has been signaled.
|
|
//
|
|
if (fAll)
|
|
{
|
|
// They want to wait for all the events. Remove the signaled
|
|
// event from the array and if it's the last one then we're
|
|
// there!
|
|
|
|
aryHandles.Delete(iEvent);
|
|
|
|
if (aryHandles.Size() == ((_fDontHandleEvents) ? 0 : 1))
|
|
{
|
|
// All the events have come back signaled. Check that none
|
|
// have become unsignaled.
|
|
if (WaitForMultipleObjects(cEvents, pEvents, TRUE, 0) == WAIT_TIMEOUT)
|
|
{
|
|
// Something became unsignaled. Start over! Rebuild
|
|
// the array of handles.
|
|
|
|
aryHandles.CopyIndirect(cEvents, pEvents, FALSE);
|
|
|
|
if (!_fDontHandleEvents)
|
|
{
|
|
aryHandles.Insert(0, _hCommEvent);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mepReturn = MEP_EVENT_0;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
mepReturn = MEP_EVENT_0 + iEvent;
|
|
|
|
if (!_fDontHandleEvents)
|
|
{
|
|
mepReturn--;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
else if (dwRet == WAIT_OBJECT_0 + aryHandles.Size())
|
|
{
|
|
//
|
|
// A windows message came through. It will be handled at the
|
|
// top of the loop.
|
|
//
|
|
}
|
|
else if (dwRet == WAIT_FAILED)
|
|
{
|
|
TraceTag((tagError, "WaitForMultipleObjects failure (%d)", GetLastError()));
|
|
|
|
AssertSz(FALSE, "WaitForMultipleObjects failure");
|
|
|
|
_fMustExitThread = TRUE;
|
|
|
|
mepReturn = MEP_EXIT;
|
|
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
Assert(dwRet == WAIT_TIMEOUT);
|
|
|
|
mepReturn = MEP_TIMEOUT;
|
|
|
|
break;
|
|
}
|
|
|
|
// Since any number of things could have brought us out of MWFMO,
|
|
// we need to compute the remaining timeout for the next time around.
|
|
|
|
if (fTimeout)
|
|
{
|
|
QueryPerformanceCounter((LARGE_INTEGER*)&i64Time);
|
|
|
|
// Convert current time to milliseconds.
|
|
i64Time = ((i64Time * 1000) / i64Freq);
|
|
|
|
// Compute the delta between the current time and our goal
|
|
lTimeout = (DWORD)(i64Goal - i64Time);
|
|
|
|
// Are we timed out?
|
|
if (lTimeout <= 0)
|
|
{
|
|
mepReturn = MEP_TIMEOUT;
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
while (fWait); // Only do the loop once if fWait == FALSE
|
|
|
|
if (fNoEvents)
|
|
{
|
|
_fDontHandleEvents = FALSE;
|
|
}
|
|
|
|
// MEP_FALLTHROUGH is not a valid return if fWait == TRUE
|
|
Assert(!fWait || mepReturn != MEP_FALLTHROUGH);
|
|
|
|
return mepReturn;
|
|
}
|
|
|
|
void
|
|
CScriptHost::HandleThreadMessage()
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
THREADMSG tm;
|
|
BYTE bData[MSGDATABUFSIZE];
|
|
DWORD cbData;
|
|
|
|
if (_fDontHandleEvents)
|
|
return;
|
|
|
|
//
|
|
//$ FUTURE: Add a way to filter messages so we can check for MD_PLEASEEXIT
|
|
// without pulling off the event messages
|
|
//
|
|
while (GetNextMsg(&tm, (void **)bData, &cbData))
|
|
{
|
|
switch (tm)
|
|
{
|
|
case MD_PLEASEEXIT:
|
|
//
|
|
// We're being asked to terminate.
|
|
//
|
|
AbortScripts();
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
case MD_MACHINECONNECT:
|
|
AssertSz(_fIsPrimaryScript, "Non-primary script got machine event!");
|
|
|
|
_fDontHandleEvents = TRUE;
|
|
|
|
FireEvent(DISPID_MTScript_OnMachineConnect, 0, NULL);
|
|
|
|
_fDontHandleEvents = FALSE;
|
|
break;
|
|
|
|
case MD_MACHEVENTCALL:
|
|
AssertSz(_fIsPrimaryScript, "Non-primary script got machine event!");
|
|
Assert(cbData == sizeof(MACHPROC_EVENT_DATA*));
|
|
|
|
_fDontHandleEvents = TRUE;
|
|
|
|
// This call will set the event object in the
|
|
// MACHPROC_EVENT_DATA struct when everything completes.
|
|
FireMachineEvent(*(MACHPROC_EVENT_DATA**)bData, TRUE);
|
|
|
|
_fDontHandleEvents = FALSE;
|
|
break;
|
|
|
|
case MD_PROCESSDATA:
|
|
Assert(cbData == sizeof(MACHPROC_EVENT_DATA*));
|
|
|
|
_fDontHandleEvents = TRUE;
|
|
|
|
// This call will set the event object in the
|
|
// MACHPROC_EVENT_DATA struct when everything completes.
|
|
FireMachineEvent(*(MACHPROC_EVENT_DATA**)bData, FALSE);
|
|
|
|
_fDontHandleEvents = FALSE;
|
|
break;
|
|
|
|
case MD_PROCESSEXITED:
|
|
case MD_PROCESSTERMINATED:
|
|
case MD_PROCESSCONNECTED:
|
|
case MD_PROCESSCRASHED:
|
|
Assert(cbData == sizeof(CProcessThread*));
|
|
|
|
_fDontHandleEvents = TRUE;
|
|
|
|
FireProcessEvent(tm, *(CProcessThread**)bData);
|
|
|
|
_fDontHandleEvents = FALSE;
|
|
|
|
break;
|
|
|
|
default:
|
|
AssertSz(FALSE, "CScriptHost got a message it couldn't handle!");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScriptHost::PushScript
|
|
//
|
|
// Create a new script site/engine and push it on the script stack
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CScriptHost::PushScript(TCHAR *pchName)
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
HRESULT hr;
|
|
CScriptSite * pScriptSite;
|
|
TCHAR * pchFile;
|
|
|
|
hr = LoadTypeLibrary();
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
pScriptSite = new CScriptSite(this);
|
|
if(!pScriptSite)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pchFile = _tcsrchr(pchName, _T('\\'));
|
|
if (!pchFile)
|
|
{
|
|
pchFile = pchName;
|
|
}
|
|
else
|
|
pchFile++;
|
|
|
|
hr = pScriptSite->Init(pchFile);
|
|
if (hr)
|
|
{
|
|
delete pScriptSite;
|
|
pScriptSite = NULL;
|
|
goto Cleanup;
|
|
}
|
|
|
|
pScriptSite->_pScriptSitePrev = _pScriptSite;
|
|
_pScriptSite = pScriptSite;
|
|
|
|
Cleanup:
|
|
RRETURN(hr);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScriptHost::PopScript
|
|
//
|
|
// Pop last script site/engine off the script stack
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CScriptHost::PopScript()
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
CScriptSite * pScriptSite = _pScriptSite;
|
|
|
|
if(!_pScriptSite)
|
|
return S_FALSE;
|
|
|
|
_pScriptSite = _pScriptSite->_pScriptSitePrev;
|
|
|
|
pScriptSite->Close();
|
|
pScriptSite->Release();
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScriptHost::CloseScripts
|
|
//
|
|
// Clear the stack of script engines
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CScriptHost::CloseScripts()
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
while(PopScript() == S_OK)
|
|
;
|
|
|
|
AssertSz(_pScriptSite == NULL, "Should have released script site");
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScriptHost::AbortScripts
|
|
//
|
|
// Clear the stack of script engines
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CScriptHost::AbortScripts()
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
// Make sure we're not stuck on MsgWaitForMultipleObjects and that we
|
|
// never will be again.
|
|
|
|
_fMustExitThread = TRUE;
|
|
SetEvent(_hCommEvent);
|
|
|
|
CScriptSite * pScriptSite;
|
|
|
|
pScriptSite = _pScriptSite;
|
|
|
|
while (pScriptSite)
|
|
{
|
|
pScriptSite->Abort();
|
|
|
|
pScriptSite = pScriptSite->_pScriptSitePrev;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScriptHost::ExecuteTopLevelScript
|
|
//
|
|
// Close previous top level script engine then load and execute script
|
|
// in a new top level script engine
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CScriptHost::ExecuteTopLevelScript(TCHAR * pchPath, VARIANT *pvarParams)
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
HRESULT hr;
|
|
|
|
// Stablize reference count during script execution.
|
|
// Script can hide window which decrements reference count.
|
|
|
|
AddRef();
|
|
|
|
// Getting read to close the scripts fire unload event.
|
|
CloseScripts();
|
|
|
|
hr = THR(PushScript(pchPath));
|
|
if(hr)
|
|
goto Cleanup;
|
|
|
|
hr = THR(VariantCopy(&_pScriptSite->_varParam, pvarParams));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = THR(_pScriptSite->ExecuteScriptFile(pchPath));
|
|
if(hr)
|
|
goto Cleanup;
|
|
|
|
hr = THR(_pScriptSite->SetScriptState(SCRIPTSTATE_CONNECTED));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
Cleanup:
|
|
Release();
|
|
RRETURN(hr);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScriptHost::ExecuteScriptlet
|
|
//
|
|
// Add a scriptlet to the current top level script engine and execute it
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CScriptHost::ExecuteTopLevelScriptlet(TCHAR * pchScript)
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
HRESULT hr;
|
|
|
|
// Stablize reference count during script execution.
|
|
// Script can hide window which decrements reference count.
|
|
|
|
AddRef();
|
|
|
|
if(!_pScriptSite)
|
|
{
|
|
hr = THR(PushScript(_T("Scriptlet")));
|
|
if(hr)
|
|
goto Cleanup;
|
|
}
|
|
else
|
|
{
|
|
Assert(_pScriptSite->_pScriptSitePrev == NULL);
|
|
}
|
|
|
|
hr = THR(_pScriptSite->ExecuteScriptStr(pchScript));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = THR(_pScriptSite->SetScriptState(SCRIPTSTATE_CONNECTED));
|
|
|
|
Cleanup:
|
|
Release();
|
|
RRETURN(hr);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScriptHost::FireProcessEvent, public
|
|
//
|
|
// Synopsis: Fires an OnProcessEvent event into the script
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
CScriptHost::FireProcessEvent(THREADMSG tm, CProcessThread *pProc)
|
|
{
|
|
VARIANTARG varg[3];
|
|
TCHAR *pszMsg;
|
|
DISPID dispid = DISPID_MTScript_OnProcessEvent;
|
|
|
|
VERIFY_THREAD();
|
|
|
|
VariantInit(&varg[0]);
|
|
VariantInit(&varg[1]);
|
|
VariantInit(&varg[2]);
|
|
|
|
// Parameters are in order from last to first
|
|
|
|
V_VT(&varg[2]) = VT_I4;
|
|
V_I4(&varg[2]) = pProc->ProcId();
|
|
|
|
switch (tm)
|
|
{
|
|
case MD_PROCESSEXITED:
|
|
pszMsg = _T("exited");
|
|
|
|
V_VT(&varg[0]) = VT_I4;
|
|
V_I4(&varg[0]) = pProc->GetExitCode();
|
|
break;
|
|
|
|
case MD_PROCESSCRASHED:
|
|
pszMsg = _T("crashed");
|
|
|
|
// 3rd parameter is empty
|
|
break;
|
|
|
|
case MD_PROCESSTERMINATED:
|
|
pszMsg = _T("terminated");
|
|
|
|
// 3rd parameter is empty
|
|
break;
|
|
|
|
case MD_PROCESSCONNECTED:
|
|
pszMsg = _T("connected");
|
|
|
|
// 3rd parameter is empty
|
|
break;
|
|
|
|
default:
|
|
AssertSz(FALSE, "NONFATAL: Invalid THREADMSG value");
|
|
return;
|
|
break;
|
|
}
|
|
|
|
V_VT(&varg[1]) = VT_BSTR;
|
|
V_BSTR(&varg[1]) = SysAllocString(pszMsg); // NULL is a valid value for BSTR
|
|
|
|
FireEvent(dispid, 3, varg);
|
|
|
|
VariantClear(&varg[0]);
|
|
VariantClear(&varg[1]);
|
|
VariantClear(&varg[2]);
|
|
|
|
return;
|
|
}
|
|
|
|
long CScriptHost::FireScriptErrorEvent(
|
|
TCHAR *bstrFile,
|
|
long nLine,
|
|
long nChar,
|
|
TCHAR *bstrText,
|
|
long sCode,
|
|
TCHAR *bstrSource,
|
|
TCHAR *bstrDescription)
|
|
{
|
|
long cSucceeded = 0;
|
|
|
|
VERIFY_THREAD();
|
|
|
|
// Parameters are in order from last to first
|
|
AutoVariant varg[7];
|
|
cSucceeded += varg[6].Set(bstrFile);
|
|
cSucceeded += varg[5].Set(nLine);
|
|
cSucceeded += varg[4].Set(nChar);
|
|
cSucceeded += varg[3].Set(bstrText);
|
|
cSucceeded += varg[2].Set(sCode);
|
|
cSucceeded += varg[1].Set(bstrSource);
|
|
cSucceeded += varg[0].Set(bstrDescription);
|
|
|
|
if (cSucceeded != ARRAY_SIZE(varg))
|
|
return 0; // Default return value
|
|
|
|
AutoVariant varResult;
|
|
FireEvent(DISPID_MTScript_OnScriptError, ARRAY_SIZE(varg), varg, &varResult);
|
|
AutoVariant varResultInt;
|
|
if (VariantChangeType(&varResultInt, &varResult, 0, VT_I4) == S_OK)
|
|
return V_I4(&varResultInt);
|
|
|
|
return 0;
|
|
}
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScriptHost::FireMachineEvent
|
|
//
|
|
// Notes: Fires the OnRemoteExec event when a machine connected to us
|
|
// remotely calls the Exec() method.
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void
|
|
CScriptHost::FireMachineEvent(MACHPROC_EVENT_DATA *pmed, BOOL fExec)
|
|
{
|
|
|
|
VERIFY_THREAD();
|
|
|
|
DISPID dispid = (fExec)
|
|
? DISPID_MTScript_OnRemoteExec
|
|
: DISPID_MTScript_OnProcessEvent;
|
|
DISPPARAMS dp;
|
|
EXCEPINFO ei;
|
|
UINT uArgErr = 0;
|
|
VARIANTARG vararg[3];
|
|
VARIANTARG varResult;
|
|
HRESULT hr = S_OK;
|
|
|
|
pmed->hrReturn = S_OK;
|
|
|
|
if (GetSite() && GetSite()->_pDispSink)
|
|
{
|
|
VariantInit(&vararg[0]);
|
|
VariantInit(&vararg[1]);
|
|
VariantInit(&vararg[2]);
|
|
VariantInit(&varResult);
|
|
|
|
// Params are in order from last to first in the array
|
|
V_VT(&vararg[0]) = VT_BSTR;
|
|
V_BSTR(&vararg[0]) = pmed->bstrParams;
|
|
|
|
V_VT(&vararg[1]) = VT_BSTR;
|
|
V_BSTR(&vararg[1]) = pmed->bstrCmd;
|
|
|
|
if (!fExec)
|
|
{
|
|
V_VT(&vararg[2]) = VT_I4;
|
|
V_I4(&vararg[2]) = pmed->dwProcId;
|
|
}
|
|
|
|
dp.rgvarg = vararg;
|
|
dp.rgdispidNamedArgs = NULL;
|
|
dp.cArgs = (fExec) ? 2 : 3;
|
|
dp.cNamedArgs = 0;
|
|
|
|
hr = GetSite()->_pDispSink->Invoke(dispid,
|
|
IID_NULL,
|
|
0,
|
|
DISPATCH_METHOD,
|
|
&dp,
|
|
&varResult,
|
|
&ei,
|
|
&uArgErr);
|
|
pmed->hrReturn = hr;
|
|
|
|
if (hr)
|
|
{
|
|
// If an error occurred, do nothing except return the error code.
|
|
}
|
|
// Check for data types which we don't support.
|
|
else if ( V_ISBYREF(&varResult)
|
|
|| V_ISARRAY(&varResult)
|
|
|| V_ISVECTOR(&varResult)
|
|
|| V_VT(&varResult) == VT_UNKNOWN)
|
|
{
|
|
// Do nothing. Return an empty result
|
|
AssertSz(FALSE, "NONFATAL: Unsupported data type returned from OnRemoteExec event");
|
|
}
|
|
else if (V_VT(&varResult) == VT_DISPATCH)
|
|
{
|
|
if (fExec)
|
|
{
|
|
// Note that the return value is an IDispatch, but don't set the
|
|
// pointer because it will need to be retrieved out of the GIT
|
|
V_VT(pmed->pvReturn) = VT_DISPATCH;
|
|
V_DISPATCH(pmed->pvReturn) = NULL;
|
|
|
|
hr =_pMT->_pGIT->RegisterInterfaceInGlobal(V_DISPATCH(&varResult),
|
|
IID_IDispatch,
|
|
&pmed->dwGITCookie);
|
|
if (hr)
|
|
{
|
|
pmed->hrReturn = hr;
|
|
}
|
|
}
|
|
|
|
// Leave the result empty if they returned an IDispatch from an
|
|
// OnProcessEvent call.
|
|
}
|
|
else
|
|
{
|
|
VariantCopy(pmed->pvReturn, &varResult);
|
|
}
|
|
|
|
VariantClear(&varResult);
|
|
}
|
|
|
|
// Tell the calling thread we're done with the call and it can continue.
|
|
SetEvent(pmed->hEvent);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScriptHost::FireEvent
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void
|
|
CScriptHost::FireEvent(DISPID dispid, UINT cArg, VARIANTARG *pvararg, VARIANTARG *pvarResult)
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
DISPPARAMS dp;
|
|
EXCEPINFO ei;
|
|
UINT uArgErr = 0;
|
|
|
|
if (GetSite() && GetSite()->_pDispSink)
|
|
{
|
|
dp.rgvarg = pvararg;
|
|
dp.rgdispidNamedArgs = NULL;
|
|
dp.cArgs = cArg;
|
|
dp.cNamedArgs = 0;
|
|
|
|
GetSite()->_pDispSink->Invoke(
|
|
dispid,
|
|
IID_NULL,
|
|
0,
|
|
DISPATCH_METHOD,
|
|
&dp,
|
|
pvarResult,
|
|
&ei,
|
|
&uArgErr);
|
|
}
|
|
}
|
|
|
|
void
|
|
CScriptHost::FireEvent(DISPID dispid, UINT carg, VARIANTARG *pvararg)
|
|
{
|
|
FireEvent(dispid, carg, pvararg, NULL);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScriptHost::FireEvent
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void
|
|
CScriptHost::FireEvent(DISPID dispid, LPCTSTR pch)
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
VARIANT var;
|
|
|
|
V_BSTR(&var) = SysAllocString(pch);
|
|
V_VT(&var) = VT_BSTR;
|
|
FireEvent(dispid, 1, &var);
|
|
SysFreeString(V_BSTR(&var));
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScriptHost::FireEvent
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void
|
|
CScriptHost::FireEvent(DISPID dispid, BOOL fArg)
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
VARIANT var;
|
|
|
|
V_BOOL(&var) = fArg ? VARIANT_TRUE : VARIANT_FALSE;
|
|
V_VT(&var) = VT_BOOL;
|
|
FireEvent(dispid, 1, &var);
|
|
}
|
|
|
|
//---------------------------------------------------------------------------
|
|
//
|
|
// Member: CScriptHost::FireEvent
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
void
|
|
CScriptHost::FireEvent(DISPID dispid, IDispatch *pDisp)
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
VARIANT var;
|
|
|
|
V_DISPATCH(&var) = pDisp;
|
|
V_VT(&var) = VT_DISPATCH;
|
|
FireEvent(dispid, 1, &var);
|
|
}
|
|
|
|
HRESULT
|
|
CScriptHost::LoadTypeLibrary()
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!_pTypeLibEXE)
|
|
{
|
|
// BUGBUG -- Is this valid, or does this need to be marshalled?
|
|
|
|
_pTypeLibEXE = _pMT->_pTypeLibEXE;
|
|
}
|
|
|
|
if (!_pTypeInfoCMTScript)
|
|
{
|
|
hr = THR(_pTypeLibEXE->GetTypeInfoOfGuid(CLSID_LocalMTScript, &_pTypeInfoCMTScript));
|
|
if (hr)
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!_pTypeInfoIGlobalMTScript)
|
|
{
|
|
hr = THR(_pTypeLibEXE->GetTypeInfoOfGuid(IID_IGlobalMTScript, &_pTypeInfoIGlobalMTScript));
|
|
if (hr)
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
return hr;
|
|
}
|
|
|
|
void
|
|
CScriptHost::GetScriptPath(CStr *pcstrPath)
|
|
{
|
|
_pMT->_options.GetScriptPath(pcstrPath);
|
|
}
|