|
|
//+------------------------------------------------------------------------
//
// Microsoft Forms
// Copyright (C) Microsoft Corporation, 1996
//
// File: bsauto.cxx
//
//-------------------------------------------------------------------------
#include "headers.hxx"
#include <mapi.h>
#undef ASSERT
DeclareTag(tagSync, "MTScript", "Trace Thread Sync events"); DeclareTag(tagLock, "MTScript", "Trace Thread Lock events"); ExternTag(tagProcess);
AutoCriticalSection CScriptHost::s_csSync; CStackDataAry<CScriptHost::SYNCEVENT, 5> CScriptHost::s_arySyncEvents;
CScriptHost::THREADLOCK CScriptHost::s_aThreadLocks[MAX_LOCKS]; UINT CScriptHost::s_cThreadLocks = 0;
static const wchar_t *g_pszListDeliminator = L";,"; static wchar_t *g_pszAtomicSyncLock = L"g_pszAtomicSyncLock"; //---------------------------------------------------------------------------
//
// Member: CScriptHost::GetTypeInfo, IDispatch
//
//---------------------------------------------------------------------------
HRESULT CScriptHost::GetTypeInfo(UINT itinfo, ULONG lcid, ITypeInfo ** pptinfo) { VERIFY_THREAD();
HRESULT hr;
hr = LoadTypeLibrary(); if (hr) goto Cleanup;
*pptinfo = _pTypeInfoIGlobalMTScript; (*pptinfo)->AddRef();
Cleanup: return hr; }
//---------------------------------------------------------------------------
//
// Member: CScriptHost::GetTypeInfoCount, IDispatch
//
//---------------------------------------------------------------------------
HRESULT CScriptHost::GetTypeInfoCount(UINT * pctinfo) { VERIFY_THREAD();
*pctinfo = 1; return S_OK; }
//---------------------------------------------------------------------------
//
// Member: CScriptHost::GetIDsOfNames, IDispatch
//
//---------------------------------------------------------------------------
HRESULT CScriptHost::GetIDsOfNames(REFIID riid, LPOLESTR * rgszNames, UINT cNames, LCID lcid, DISPID * rgdispid) { VERIFY_THREAD();
HRESULT hr;
hr = THR(LoadTypeLibrary()); if (hr) goto Cleanup;
hr = _pTypeInfoIGlobalMTScript->GetIDsOfNames(rgszNames, cNames, rgdispid);
Cleanup: return hr; }
//---------------------------------------------------------------------------
//
// Member: CScriptHost::Invoke, IDispatch
//
//---------------------------------------------------------------------------
HRESULT CScriptHost::Invoke(DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags,DISPPARAMS * pdispparams, VARIANT * pvarResult,EXCEPINFO * pexcepinfo, UINT * puArgErr) { VERIFY_THREAD();
HRESULT hr;
hr = LoadTypeLibrary(); if (hr) goto Cleanup;
hr = _pTypeInfoIGlobalMTScript->Invoke((IGlobalMTScript *)this, dispidMember, wFlags, pdispparams, pvarResult, pexcepinfo, puArgErr);
Cleanup: return hr; }
//***************************************************************************
//
// IGlobalMTScript implementation
//
//***************************************************************************
HRESULT CScriptHost::get_PublicData(VARIANT * pvData) { VERIFY_THREAD();
HRESULT hr = S_OK;
// NOTE: We assume that the output parameter pvData is an empty or
// uninitialized VARIANT. This should be safe because it is defined as
// the return value for this method to the scripting engines and is
// a pure [out] parameter.
VariantInit(pvData);
// Check to see if the data has changed since we last got it.
// _dwPublicSerialNum is a DWORD so we are guaranteed an atomic read.
if (_pMT->_dwPublicSerialNum != _dwPublicSN) { LOCK_LOCALS(_pMT);
VariantClear(&_vPubCache);
// If the data is an IDispatch pointer (how the scripting engines
// implement most objects) we must get a marshalled copy to this thread.
// Otherwise we can just copy the data into the return value.
if (V_VT(&_pMT->_vPublicData) == VT_DISPATCH) { IDispatch *pDisp;
Assert(_pMT->_dwPublicDataCookie != 0);
hr = _pMT->_pGIT->GetInterfaceFromGlobal(_pMT->_dwPublicDataCookie, IID_IDispatch, (LPVOID*)&pDisp); if (!hr) { V_VT(&_vPubCache) = VT_DISPATCH; V_DISPATCH(&_vPubCache) = pDisp; } } else { hr = VariantCopy(&_vPubCache, &_pMT->_vPublicData); }
_dwPublicSN = _pMT->_dwPublicSerialNum; }
if (!hr) { hr = VariantCopy(pvData, &_vPubCache); }
return hr; }
HRESULT CScriptHost::put_PublicData(VARIANT vData) { VERIFY_THREAD();
HRESULT hr = S_OK;
// Check for data types which we don't support.
if ( V_ISBYREF(&vData) || V_ISARRAY(&vData) || V_ISVECTOR(&vData) || V_VT(&vData) == VT_UNKNOWN) { return E_INVALIDARG; }
LOCK_LOCALS(_pMT);
// If the previous data is an IDispatch pointer revoke it from the
// GlobalInterfaceTable.
if (V_VT(&_pMT->_vPublicData) == VT_DISPATCH) { Assert(_pMT->_dwPublicDataCookie != 0);
hr = _pMT->_pGIT->RevokeInterfaceFromGlobal(_pMT->_dwPublicDataCookie);
AssertSz(!hr, "Unexpected failure revoking itf from GIT");
_pMT->_dwPublicDataCookie = 0; }
// If the new data is an IDispatch pointer then we must register it.
if (V_VT(&vData) == VT_DISPATCH) { Assert(_pMT->_dwPublicDataCookie == 0);
hr = _pMT->_pGIT->RegisterInterfaceInGlobal(V_DISPATCH(&vData), IID_IDispatch, &_pMT->_dwPublicDataCookie);
AssertSz(!hr, "Unexpected failure registering itf in GIT"); }
// Update the global copy of the data.
//$ FUTURE: This can be optimized to reduce memory usage (by not making
// a copy of a string in every thread, for example).
_pMT->_dwPublicSerialNum++; hr = VariantCopy(&_pMT->_vPublicData, &vData);
if (!hr) { // Even if it's an IDispatch, we don't need to marshal it for
// ourselves because we're running in the same thread as the script
// engine that gave it to us.
hr = VariantCopy(&_vPubCache, &vData);
_dwPublicSN = _pMT->_dwPublicSerialNum; }
return S_OK; }
HRESULT CScriptHost::get_PrivateData(VARIANT * pvData) { VERIFY_THREAD();
HRESULT hr = S_OK;
// NOTE: We assume that the output parameter pvData is an empty or
// uninitialized VARIANT. This should be safe because it is defined as
// the return value for this method to the scripting engines and is
// a pure [out] parameter.
VariantInit(pvData);
// Check to see if the data has changed since we last got it.
// _dwPrivateSerialNum is a DWORD so we are guaranteed an atomic read.
if (_pMT->_dwPrivateSerialNum != _dwPrivateSN) { LOCK_LOCALS(_pMT);
VariantClear(&_vPrivCache);
// If the data is an IDispatch pointer (how the scripting engines
// implement most objects) we must get a marshalled copy to this thread.
// Otherwise we can just copy the data into the return value.
if (V_VT(&_pMT->_vPrivateData) == VT_DISPATCH) { IDispatch *pDisp;
Assert(_pMT->_dwPrivateDataCookie != 0);
hr = _pMT->_pGIT->GetInterfaceFromGlobal(_pMT->_dwPrivateDataCookie, IID_IDispatch, (LPVOID*)&pDisp); if (!hr) { V_VT(&_vPrivCache) = VT_DISPATCH; V_DISPATCH(&_vPrivCache) = pDisp; } } else { hr = VariantCopy(&_vPrivCache, &_pMT->_vPrivateData); }
_dwPrivateSN = _pMT->_dwPrivateSerialNum; }
if (!hr) { hr = VariantCopy(pvData, &_vPrivCache); }
return hr; }
HRESULT CScriptHost::put_PrivateData(VARIANT vData) { VERIFY_THREAD();
HRESULT hr = S_OK;
// Check for data types which we don't support.
if ( V_ISBYREF(&vData) || V_ISARRAY(&vData) || V_ISVECTOR(&vData) || V_VT(&vData) == VT_UNKNOWN) { return E_INVALIDARG; }
LOCK_LOCALS(_pMT);
// If the previous data is an IDispatch pointer revoke it from the
// GlobalInterfaceTable.
if (V_VT(&_pMT->_vPrivateData) == VT_DISPATCH) { Assert(_pMT->_dwPrivateDataCookie != 0);
hr = _pMT->_pGIT->RevokeInterfaceFromGlobal(_pMT->_dwPrivateDataCookie);
AssertSz(!hr, "Unexpected failure revoking itf from GIT");
_pMT->_dwPrivateDataCookie = 0; }
// If the new data is an IDispatch pointer then we must register it.
if (V_VT(&vData) == VT_DISPATCH) { Assert(_pMT->_dwPrivateDataCookie == 0);
hr = _pMT->_pGIT->RegisterInterfaceInGlobal(V_DISPATCH(&vData), IID_IDispatch, &_pMT->_dwPrivateDataCookie);
AssertSz(!hr, "Unexpected failure registering itf in GIT"); }
// Update the global copy of the data.
//$ FUTURE: This can be optimized to reduce memory usage (by not making
// a copy of a string in every thread, for example).
_pMT->_dwPrivateSerialNum++; hr = VariantCopy(&_pMT->_vPrivateData, &vData);
if (!hr) { // Even if it's an IDispatch, we don't need to marshal it for
// ourselves because we're running in the same thread as the script
// engine that gave it to us.
hr = VariantCopy(&_vPrivCache, &vData);
_dwPrivateSN = _pMT->_dwPrivateSerialNum; }
return S_OK; }
HRESULT CScriptHost::ExitProcess() { VERIFY_THREAD();
PostToThread(_pMT, MD_PLEASEEXIT);
return S_OK; }
HRESULT CScriptHost::Restart() { VERIFY_THREAD();
PostToThread(_pMT, MD_RESTART);
return S_OK; }
HRESULT CScriptHost::get_LocalMachine(BSTR *pbstrName) { TCHAR achCompName[MAX_COMPUTERNAME_LENGTH+1]; DWORD dwLen = MAX_COMPUTERNAME_LENGTH+1;
VERIFY_THREAD();
if (!pbstrName) return E_POINTER;
GetComputerName(achCompName, &dwLen);
achCompName[dwLen] = '\0';
*pbstrName = SysAllocString(achCompName); if (!*pbstrName) return E_OUTOFMEMORY;
return S_OK; }
HRESULT CScriptHost::Include(BSTR bstrPath) { VERIFY_THREAD();
HRESULT hr;
if(!bstrPath) return E_INVALIDARG;
if (!_fIsPrimaryScript) MessageEventPump(FALSE);
//$ TODO: Define a new named item context for the included file for better
// debugging.
hr = THR(GetSite()->ExecuteScriptFile(bstrPath));
return hr; }
HRESULT CScriptHost::CallScript(BSTR bstrPath, VARIANT *pvarScriptParam) { VERIFY_THREAD();
HRESULT hr;
if(!bstrPath) return E_INVALIDARG;
if (!_fIsPrimaryScript) MessageEventPump(FALSE);
hr = THR(PushScript(_tcsrchr(bstrPath, _T('.')))); if(hr) goto Cleanup;
if (pvarScriptParam && pvarScriptParam->vt != VT_ERROR) { hr = THR(VariantCopy(&GetSite()->_varParam, pvarScriptParam)); if (hr) goto Cleanup; }
hr = THR(GetSite()->ExecuteScriptFile(bstrPath));
hr = THR(GetSite()->SetScriptState(SCRIPTSTATE_CONNECTED)); if (hr) goto Cleanup;
FireEvent(DISPID_MTScript_ScriptMain, 0, NULL);
PopScript();
Cleanup: return hr; }
HRESULT CScriptHost::SpawnScript(BSTR bstrPath, VARIANT *pvarScriptParam) { VERIFY_THREAD();
HRESULT hr = S_OK; VARIANT *pvarParam = NULL; DWORD dwCookie = 0; BOOL fRegistered = false;
// Check for data types which we don't support.
if ( !bstrPath || SysStringLen(bstrPath) == 0 || V_ISBYREF(pvarScriptParam) || V_ISARRAY(pvarScriptParam) || V_ISVECTOR(pvarScriptParam) || V_VT(pvarScriptParam) == VT_UNKNOWN) { return E_INVALIDARG; }
if (!_fIsPrimaryScript) MessageEventPump(FALSE);
if (pvarScriptParam && pvarScriptParam->vt != VT_ERROR) { pvarParam = new VARIANT;
if (!pvarParam) return E_OUTOFMEMORY;
VariantInit(pvarParam);
if (V_VT(pvarScriptParam) == VT_DISPATCH) { // Stick the pointer in the GlobalInterfaceTable, so the other
// thread can pull it out safely.
hr = _pMT->_pGIT->RegisterInterfaceInGlobal(V_DISPATCH(pvarScriptParam), IID_IDispatch, &dwCookie); if (hr) goto Cleanup;
// Stick the cookie in the variant we hand to the other thread.
V_VT(pvarParam) = VT_DISPATCH; V_I4(pvarParam) = dwCookie; fRegistered = true; } else { hr = THR(VariantCopy(pvarParam, pvarScriptParam)); if (hr) goto Cleanup; } } hr = _pMT->RunScript(bstrPath, pvarParam);
Cleanup: if (pvarParam) { if (fRegistered) { Verify(_pMT->_pGIT->RevokeInterfaceFromGlobal(dwCookie) == S_OK); } if (V_VT(pvarParam) != VT_DISPATCH) VariantClear(pvarParam); delete pvarParam; } return hr; }
HRESULT CScriptHost::get_ScriptParam(VARIANT *pvarScriptParam) { VERIFY_THREAD();
HRESULT hr;
if (!_fIsPrimaryScript) MessageEventPump(FALSE);
if (GetSite()) { hr = THR(VariantCopy(pvarScriptParam, &GetSite()->_varParam)); } else { hr = E_FAIL; }
return hr; }
HRESULT CScriptHost::get_ScriptPath(BSTR *pbstrPath) { CStr cstrPath;
VERIFY_THREAD();
if (!_fIsPrimaryScript) MessageEventPump(FALSE);
_pMT->_options.GetScriptPath(&cstrPath);
return cstrPath.AllocBSTR(pbstrPath); }
typedef HRESULT (TestExternal_Func)(VARIANT *pParam, long *plRetVal);
HRESULT CScriptHost::CallExternal(BSTR bstrDLLName, BSTR bstrFunctionName, VARIANT *pParam, long * plRetVal) { VERIFY_THREAD();
HRESULT hr = S_OK; HINSTANCE hInstDLL = NULL; TestExternal_Func *pfn = NULL;
if (!_fIsPrimaryScript) MessageEventPump(FALSE);
if (!plRetVal || !bstrDLLName || !bstrFunctionName) return E_POINTER;
*plRetVal = -1;
hInstDLL = LoadLibrary(bstrDLLName);
if (NULL == hInstDLL) { return S_FALSE; // Can't return error codes or the script will abort
}
int cchLen = SysStringLen(bstrFunctionName); char *pchBuf = new char[cchLen+1];
if (pchBuf) { WideCharToMultiByte(CP_ACP, 0, bstrFunctionName, cchLen, pchBuf, cchLen+1, NULL, NULL); pchBuf[cchLen] = '\0';
pfn = (TestExternal_Func *)GetProcAddress(hInstDLL, pchBuf);
delete pchBuf; }
if (NULL == pfn) { hr = S_FALSE; } else { *plRetVal = 0; hr = (*pfn)(pParam, plRetVal); }
FreeLibrary(hInstDLL);
return hr; }
HRESULT CScriptHost::GetSyncEventName(int nEvent, CStr *pCStr, HANDLE *phEvent) { HRESULT hr = S_OK;
*phEvent = NULL;
if (nEvent < 0) return E_INVALIDARG;
EnterCriticalSection(&s_csSync);
if (nEvent >= s_arySyncEvents.Size()) { hr = E_INVALIDARG; goto Cleanup; }
pCStr->Set(s_arySyncEvents[nEvent]._cstrName); *phEvent = s_arySyncEvents[nEvent]._hEvent;
Cleanup: LeaveCriticalSection(&s_csSync);
RRETURN(hr); }
HRESULT CScriptHost::GetSyncEvent(LPCTSTR pszName, HANDLE *phEvent) { int i; SYNCEVENT *pse; HRESULT hr = S_OK;
*phEvent = NULL;
if (_tcslen(pszName) < 1) return E_INVALIDARG;
EnterCriticalSection(&s_csSync);
for (i = s_arySyncEvents.Size(), pse = s_arySyncEvents; i > 0; i--, pse++) { if (_tcsicmp(pszName, pse->_cstrName) == 0) { *phEvent = pse->_hEvent; break; } }
if (i == 0) { //
// The event doesn't exist yet. Create one. The primary script thread
// owns cleaning all this stuff up.
//
SYNCEVENT se;
se._hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!se._hEvent) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; } else { se._cstrName.Set(pszName);
s_arySyncEvents.AppendIndirect(&se);
//
// The cstr in 'se' will destroy its memory unless we take it away.
// It's now owned by the cstrName member of the array.
//
(void)se._cstrName.TakePch();
*phEvent = se._hEvent; } }
Cleanup: LeaveCriticalSection(&s_csSync);
RRETURN(hr); }
HRESULT CScriptHost::StringToEventArray(const wchar_t *pszNameList, CStackPtrAry<HANDLE, 5> *pAryEvents) { HRESULT hr = S_OK; if (wcspbrk(pszNameList, g_pszListDeliminator)) { CStr cstrNameList; HRESULT hr = cstrNameList.Set(pszNameList); wchar_t *pch = NULL;
if (hr == S_OK) pch = wcstok(cstrNameList, g_pszListDeliminator);
while (hr == S_OK && pch) { HANDLE hEvent; hr = THR(GetSyncEvent(pch, &hEvent)); if (hr != S_OK) break;
// Don't allow duplicates. MsgWaitForMultipleObjects will barf.
if (pAryEvents->Find(hEvent) != -1) { hr = E_INVALIDARG; break; }
hr = pAryEvents->Append(hEvent);
pch = wcstok(NULL, g_pszListDeliminator); } } else { HANDLE hEvent; hr = THR(GetSyncEvent(pszNameList, &hEvent)); if (hr == S_OK) hr = pAryEvents->Append(hEvent); } RRETURN(hr); }
HRESULT CScriptHost::ResetSync(const BSTR bstrName) { VERIFY_THREAD();
HRESULT hr;
if (!_fIsPrimaryScript) MessageEventPump(FALSE);
CStackPtrAry<HANDLE, 5> aryEvents; hr = StringToEventArray(bstrName, &aryEvents); if (hr == S_OK) { for(int i = aryEvents.Size() - 1; i>= 0; --i) ResetEvent(aryEvents[i]); } return hr; }
HRESULT CScriptHost::WaitForSync(BSTR bstrName, long nTimeout, VARIANT_BOOL *pfSignaled) { VERIFY_THREAD();
HANDLE hEvent; HRESULT hr;
if (!pfSignaled) return E_POINTER;
*pfSignaled = VB_TRUE;
hr = THR(GetSyncEvent(bstrName, &hEvent)); if (hr) RRETURN(hr);
TraceTag((tagSync, "Thread 0x%x is starting a wait for sync %ls", _dwThreadId, bstrName));
if (MessageEventPump(TRUE, 1, &hEvent, FALSE, (nTimeout > 0) ? nTimeout : INFINITE) != MEP_EVENT_0) { *pfSignaled = VB_FALSE; }
// Now make sure that SignalThreadSync() and ResetSync()
// are atomic when manipulating multiple syncs.
TakeThreadLock(g_pszAtomicSyncLock); ReleaseThreadLock(g_pszAtomicSyncLock); TraceTag((tagSync, "Thread 0x%x has returned from a wait for sync %ls (signaled=%s)", _dwThreadId, bstrName, (*pfSignaled==VB_FALSE) ? "false" : "true"));
return S_OK; }
HRESULT CScriptHost::WaitForMultipleSyncs(const BSTR bstrNameList, VARIANT_BOOL fWaitForAll, long nTimeout, long *plSignal) { VERIFY_THREAD();
HRESULT hr; DWORD dwRet;
*plSignal = 0;
CStackPtrAry<HANDLE, 5> aryEvents; hr = StringToEventArray(bstrNameList, &aryEvents);
if (hr == S_OK) { TraceTag((tagSync, "Thread 0x%x is starting a multiwait for sync %ls", _dwThreadId, bstrNameList)); dwRet = MessageEventPump(TRUE, aryEvents.Size(), aryEvents, (fWaitForAll == VB_TRUE) ? TRUE : FALSE, (nTimeout > 0) ? nTimeout : INFINITE); if (dwRet >= MEP_EVENT_0) { *plSignal = dwRet - MEP_EVENT_0 + 1; // result is 1-based, not zero-based
// Now make sure that SignalThreadSync() and ResetSync()
// are atomic when manipulating multiple syncs.
TakeThreadLock(g_pszAtomicSyncLock); ReleaseThreadLock(g_pszAtomicSyncLock); } TraceTag((tagSync, "Thread 0x%x has returned from a multiwait for sync %ls (signaled=%d)", _dwThreadId, bstrNameList, *plSignal)); } return hr; }
HRESULT CScriptHost::SignalThreadSync(BSTR bstrName) { HRESULT hr;
if (!_fIsPrimaryScript) MessageEventPump(FALSE);
TraceTag((tagSync, "Thread 0x%x is signalling sync %ls", _dwThreadId, bstrName));
CStackPtrAry<HANDLE, 5> aryEvents; hr = StringToEventArray(bstrName, &aryEvents); if (hr == S_OK) { if (aryEvents.Size() > 1) TakeThreadLock(g_pszAtomicSyncLock);
for(int i = aryEvents.Size() - 1; i>= 0; --i) SetEvent(aryEvents[i]);
if (aryEvents.Size() > 1) ReleaseThreadLock(g_pszAtomicSyncLock); }
return S_OK; }
HRESULT CScriptHost::GetLockCritSec(LPTSTR pszName, CRITICAL_SECTION **ppcs, DWORD **ppdwOwner) { int i; THREADLOCK *ptl; HRESULT hr = S_OK;
if (_tcslen(pszName) < 1) return E_INVALIDARG;
*ppcs = NULL;
EnterCriticalSection(&s_csSync);
for (i = s_cThreadLocks, ptl = s_aThreadLocks; i > 0; i--, ptl++) { if (_tcsicmp(pszName, ptl->_cstrName) == 0) { *ppcs = &ptl->_csLock; *ppdwOwner = &ptl->_dwOwner;
break; } }
if (i == 0) { //
// The critical section doesn't exist yet. Create one. The primary
// script thread owns cleaning all this stuff up.
//
if (s_cThreadLocks == MAX_LOCKS) { // BUGBUG -- SetErrorInfo
hr = E_OUTOFMEMORY; goto Cleanup; }
ptl = &s_aThreadLocks[s_cThreadLocks++];
InitializeCriticalSection(&ptl->_csLock);
ptl->_cstrName.Set(pszName); ptl->_dwOwner = 0;
*ppcs = &ptl->_csLock; *ppdwOwner = &ptl->_dwOwner; }
Cleanup: LeaveCriticalSection(&s_csSync);
RRETURN(hr); }
HRESULT CScriptHost::TakeThreadLock(BSTR bstrName) { HRESULT hr; CRITICAL_SECTION *pcs; DWORD *pdwOwner;
VERIFY_THREAD();
if (!_fIsPrimaryScript) MessageEventPump(FALSE);
hr = THR(GetLockCritSec(bstrName, &pcs, &pdwOwner)); if (hr) RRETURN(hr);
TraceTag((tagLock, "Thread 0x%x is trying to obtain lock %ls", _dwThreadId, bstrName));
while (!TryEnterCriticalSection(pcs)) { // Make sure we don't get hung here if the thread's trying to exit
if (MessageEventPump(TRUE, 0, NULL, FALSE, 100, TRUE) == MEP_EXIT) return E_FAIL; }
TraceTag((tagLock, "Thread 0x%x has obtained lock %ls", _dwThreadId, bstrName));
*pdwOwner = GetCurrentThreadId();
return S_OK; }
HRESULT CScriptHost::ReleaseThreadLock(BSTR bstrName) { HRESULT hr; CRITICAL_SECTION *pcs; DWORD *pdwOwner;
VERIFY_THREAD();
if (!_fIsPrimaryScript) MessageEventPump(FALSE);
hr = THR(GetLockCritSec(bstrName, &pcs, &pdwOwner)); if (hr) RRETURN(hr);
// LeaveCriticalSection can cause other threads to lock indefinitely on
// the critical section if we don't actually own it.
if (*pdwOwner != GetCurrentThreadId()) { return HRESULT_FROM_WIN32(ERROR_NOT_OWNER); }
LeaveCriticalSection(pcs);
TraceTag((tagLock, "Thread 0x%x has released lock %ls", _dwThreadId, bstrName));
return S_OK; }
HRESULT CScriptHost::DoEvents() { VERIFY_THREAD();
MessageEventPump(FALSE);
return S_OK; }
HRESULT CScriptHost::MessageBoxTimeout(BSTR bstrMessage, // Message Text
long cButtons, // Number of buttons (max 5)
BSTR bstrButtonText, // Comma separated list of button text. Number must match cButtons
long lTimeout, // Timeout in minutes. If zero then no timeout.
long lEventInterval, // Fire a OnMessageBoxInterval event every lEventInterval minutes
VARIANT_BOOL fCanCancel, // If TRUE then timeout can be canceled.
VARIANT_BOOL fConfirm, // If TRUE then confirm the button pushed before returning.
long *plSelected) // Returns button pushed. 0=timeout, 1=Button1, 2=Button2, etc.
{ VERIFY_THREAD();
HANDLE hEvent; MBTIMEOUT mbt = { 0 }; BOOL fExit = FALSE; HRESULT hr = S_OK;
if (!plSelected) return E_POINTER;
*plSelected = -1;
if (cButtons < 1 || cButtons > 5) return E_INVALIDARG;
hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (!hEvent) return HRESULT_FROM_WIN32(GetLastError());
mbt.bstrMessage = bstrMessage; mbt.cButtons = cButtons; mbt.bstrButtonText = bstrButtonText; mbt.lTimeout = lTimeout; mbt.lEventInterval = lEventInterval; mbt.fCanCancel = (fCanCancel == VB_TRUE) ? TRUE : FALSE; mbt.fConfirm = (fConfirm == VB_TRUE) ? TRUE : FALSE; mbt.hEvent = hEvent;
CMessageBoxTimeout *pmbt = new CMessageBoxTimeout(&mbt); if (!pmbt) return E_OUTOFMEMORY;
pmbt->StartThread(NULL);
while (!fExit) { // Make sure it was our event being signaled that caused the loop to end
if (MessageEventPump(TRUE, 1, &hEvent) != MEP_EVENT_0) { hr = S_FALSE; fExit = TRUE; break; }
switch (mbt.mbts) { case MBTS_BUTTON1: case MBTS_BUTTON2: case MBTS_BUTTON3: case MBTS_BUTTON4: case MBTS_BUTTON5: case MBTS_TIMEOUT: *plSelected = (long)mbt.mbts; fExit = TRUE; break;
case MBTS_INTERVAL: FireEvent(DISPID_MTScript_OnMessageBoxInterval, 0, NULL); ResetEvent(hEvent); break;
case MBTS_ERROR: hr = E_FAIL; fExit = TRUE; break;
default: AssertSz(FALSE, "FATAL: Invalid value for mbts!"); fExit = TRUE; break; } }
if (pmbt->_hwnd != NULL) { EndDialog(pmbt->_hwnd, 0); }
pmbt->Release();
CloseHandle(hEvent);
return hr; }
HRESULT CScriptHost::RunLocalCommand(BSTR bstrCommand, BSTR bstrDir, BSTR bstrTitle, VARIANT_BOOL fMinimize, VARIANT_BOOL fGetOutput, VARIANT_BOOL fWait, VARIANT_BOOL fNoCrashPopup, VARIANT_BOOL fNoEnviron, long * plErrorCode) { VERIFY_THREAD();
CProcessThread *pProc; PROCESS_PARAMS pp;
if (!_fIsPrimaryScript) MessageEventPump(FALSE);
if (!plErrorCode) return E_POINTER;
pp.pszCommand = bstrCommand; pp.pszDir = bstrDir; pp.pszTitle = bstrTitle; pp.fMinimize = (fMinimize == VB_TRUE) ? TRUE : FALSE; pp.fGetOutput = (fGetOutput == VB_TRUE) ? TRUE : FALSE; pp.fNoCrashPopup = (fNoCrashPopup == VB_TRUE) ? TRUE : FALSE; pp.fNoEnviron = (fNoEnviron == VB_TRUE) ? TRUE : FALSE;
_hrLastRunLocalError = S_OK;
pProc = new CProcessThread(this); if (!pProc) { _hrLastRunLocalError = E_OUTOFMEMORY;
*plErrorCode = 0;
return S_FALSE; } _hrLastRunLocalError = pProc->StartThread(&pp); if (FAILED(_hrLastRunLocalError)) { *plErrorCode = 0;
pProc->Release();
// Don't cause a script error by returning a failure code.
return S_FALSE; }
_pMT->AddProcess(pProc);
if (fWait == VB_TRUE) { HANDLE hEvent = pProc->hThread();
// The actual return code here doesn't matter. We'll do the same thing
// no matter what causes MessageEventPump to exit.
MessageEventPump(TRUE, 1, &hEvent); }
TraceTag((tagProcess, "RunLocalCommand PID=%d, %s", pProc->ProcId(), bstrCommand)); *plErrorCode = pProc->ProcId();
return S_OK; }
HRESULT CScriptHost::GetLastRunLocalError(long *plErrorCode) { VERIFY_THREAD();
*plErrorCode = _hrLastRunLocalError;
return S_OK; }
HRESULT CScriptHost::GetProcessOutput(long lProcessID, BSTR *pbstrData) { VERIFY_THREAD();
CProcessThread *pProc;
pProc = _pMT->FindProcess((DWORD)lProcessID);
if (!pProc) { return E_INVALIDARG; }
return pProc->GetProcessOutput(pbstrData); }
HRESULT CScriptHost::GetProcessExitCode(long lProcessID, long *plExitCode) { VERIFY_THREAD();
CProcessThread *pProc;
pProc = _pMT->FindProcess((DWORD)lProcessID);
if (!pProc) { return E_INVALIDARG; }
*plExitCode = pProc->GetExitCode();
return S_OK; }
HRESULT CScriptHost::TerminateProcess(long lProcessID) { VERIFY_THREAD();
CProcessThread *pProc;
pProc = _pMT->FindProcess((DWORD)lProcessID);
if (!pProc) { return E_INVALIDARG; }
PostToThread(pProc, MD_PLEASEEXIT);
return S_OK; }
HRESULT CScriptHost::SendToProcess(long lProcessID, BSTR bstrType, BSTR bstrData, long *plReturn) { VERIFY_THREAD(); MACHPROC_EVENT_DATA med; MACHPROC_EVENT_DATA *pmed; VARIANT vRet; HRESULT hr = S_OK;
CProcessThread *pProc;
pProc = _pMT->FindProcess((DWORD)lProcessID);
//$ TODO -- Fix these error return codes to not cause script errors.
if (!pProc || !plReturn) { return E_INVALIDARG; } else if (pProc->GetExitCode() != STILL_ACTIVE) { *plReturn = -1; return S_FALSE; }
VariantInit(&vRet);
med.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL); if (med.hEvent == NULL) { return HRESULT_FROM_WIN32(GetLastError()); }
med.bstrCmd = bstrType; med.bstrParams = bstrData; med.dwProcId = (DWORD)lProcessID; med.pvReturn = &vRet; med.dwGITCookie = 0; med.hrReturn = S_OK;
pmed = &med;
PostToThread(_pMT, MD_SENDTOPROCESS, &pmed, sizeof(MACHPROC_EVENT_DATA*));
// BUGBUG - we could get a crash if something causes us to exit before
// the CMTScript thread handles the MD_SENDTOPROCESS message.
MessageEventPump(TRUE, 1, &med.hEvent);
hr = med.hrReturn;
*plReturn = V_I4(&vRet);
VariantClear(&vRet);
CloseHandle(med.hEvent);
return hr; }
#define USERPROFILESTRING_SZ (256 * sizeof(TCHAR))
TCHAR UserProfileString[USERPROFILESTRING_SZ];
HRESULT CScriptHost::SendMail(BSTR bstrTo, BSTR bstrCC, BSTR bstrBCC, BSTR bstrSubject, BSTR bstrMessage, BSTR bstrAttachmentPath, BSTR bstrUsername, BSTR bstrPassword, long * plErrorCode) { // This implementation was stolen from the execmail.exe source code.
// Handles to MAPI32.DLL library, host name registry key, email session.
//$ FUTURE -- Cache this stuff instead of reloading the library every
// time.
HINSTANCE hLibrary; LHANDLE hSession;
// Function pointers for MAPI calls we use.
LPMAPILOGON fnMAPILogon; LPMAPISENDMAIL fnMAPISendMail; LPMAPILOGOFF fnMAPILogoff;
// MAPI structures and counters.
MapiRecipDesc rgRecipDescStruct[30]; MapiMessage MessageStruct = {0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, NULL, 0, NULL}; MapiFileDesc MAPIFileDesc = {0, 0, 0, NULL, NULL, NULL}; FLAGS MAPIFlags = MAPI_NEW_SESSION;
// Pointers to email parameter strings.
char *pszToList = NULL; char *pszCCList = NULL; char *pszBCCList = NULL;
ULONG ulErrorCode;
if (!plErrorCode) return E_POINTER;
///////////////////////////////////////////////////////////////////////////
// No point going any farther if MAPI32.DLL isn't available.
hLibrary = LoadLibrary(L"MAPI32.DLL"); if (hLibrary == NULL) { DWORD dwError = GetLastError();
TraceTag((tagError, "Error: MAPI32.DLL not found on this machine!"));
*plErrorCode = HRESULT_FROM_WIN32(dwError);
return S_FALSE; }
// Must convert all parameters to ANSI
ANSIString szTo(bstrTo); ANSIString szCC(bstrCC); ANSIString szBCC(bstrBCC); ANSIString szSubject(bstrSubject); ANSIString szMessage(bstrMessage); ANSIString szAttachment(bstrAttachmentPath); ANSIString szUsername(bstrUsername); ANSIString szPassword(bstrPassword);
// Set up MAPI function pointers.
fnMAPILogon = (LPMAPILOGON)GetProcAddress(hLibrary, "MAPILogon"); fnMAPISendMail = (LPMAPISENDMAIL)GetProcAddress(hLibrary, "MAPISendMail"); fnMAPILogoff = (LPMAPILOGOFF)GetProcAddress(hLibrary, "MAPILogoff");
// Hook the recipient structure array into the message structure.
MessageStruct.lpRecips = rgRecipDescStruct;
// Get the default user parameters if none were specified.
if (SysStringLen(bstrUsername) == 0) { HKEY hkey; WCHAR KeyPath[] = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\Windows Messaging Subsystem\\Profiles"; WCHAR Value[] = L"DefaultProfile"; DWORD buf_sz = USERPROFILESTRING_SZ; DWORD val_type;
if( RegOpenKeyEx( HKEY_CURRENT_USER, KeyPath, 0, KEY_READ, &hkey ) == ERROR_SUCCESS ) { if ( RegQueryValueEx( hkey, Value, NULL, &val_type, (BYTE*)UserProfileString, &buf_sz ) == ERROR_SUCCESS ) { if ( val_type == REG_SZ ) { szUsername.Set(UserProfileString); } }
RegCloseKey( hkey ); } }
pszToList = szTo;
// Parse ToList into rgRecipDescStruct.
while (*pszToList && (MessageStruct.nRecipCount < 30)) { // Strip leading spaces from recipient name and terminate preceding
// name string.
if (isspace(*pszToList)) { *pszToList=0; pszToList++; } // Add a name to the array and increment the number of recipients.
else { rgRecipDescStruct[MessageStruct.nRecipCount].ulReserved = 0; rgRecipDescStruct[MessageStruct.nRecipCount].ulRecipClass = MAPI_TO; rgRecipDescStruct[MessageStruct.nRecipCount].lpszName = pszToList; rgRecipDescStruct[MessageStruct.nRecipCount].lpszAddress = NULL; rgRecipDescStruct[MessageStruct.nRecipCount].ulEIDSize = 0; rgRecipDescStruct[MessageStruct.nRecipCount].lpEntryID = NULL; MessageStruct.nRecipCount++; // Move beginning of string to next name in ToList.
do { pszToList++; } while (isgraph(*pszToList)); } }
pszCCList = szCC;
// Parse CCList into rgRecipDescStruct.
while (*pszCCList && (MessageStruct.nRecipCount < 30)) { // Strip leading spaces from recipient name and terminate preceding
// name string.
if (isspace(*pszCCList)) { *pszCCList=0; pszCCList++; } // Add a name to the array and increment the number of recipients.
else { rgRecipDescStruct[MessageStruct.nRecipCount].ulReserved = 0; rgRecipDescStruct[MessageStruct.nRecipCount].ulRecipClass = MAPI_CC; rgRecipDescStruct[MessageStruct.nRecipCount].lpszName = pszCCList; rgRecipDescStruct[MessageStruct.nRecipCount].lpszAddress = NULL; rgRecipDescStruct[MessageStruct.nRecipCount].ulEIDSize = 0; rgRecipDescStruct[MessageStruct.nRecipCount].lpEntryID = NULL; MessageStruct.nRecipCount++; // Move beginning of string to next name in CCList.
do { pszCCList++; } while (isgraph(*pszCCList)); } }
pszBCCList = szBCC;
// Parse BCCList into rgRecipDescStruct.
while (*pszBCCList && (MessageStruct.nRecipCount < 30)) { // Strip leading spaces from recipient name and terminate preceding
// name string.
if (isspace(*pszBCCList)) { *pszBCCList=0; pszBCCList++; } // Add a name to the array and increment the number of recipients.
else { rgRecipDescStruct[MessageStruct.nRecipCount].ulReserved = 0; rgRecipDescStruct[MessageStruct.nRecipCount].ulRecipClass = MAPI_BCC; rgRecipDescStruct[MessageStruct.nRecipCount].lpszName = pszBCCList; rgRecipDescStruct[MessageStruct.nRecipCount].lpszAddress = NULL; rgRecipDescStruct[MessageStruct.nRecipCount].ulEIDSize = 0; rgRecipDescStruct[MessageStruct.nRecipCount].lpEntryID = NULL; MessageStruct.nRecipCount++; // Move beginning of string to next name in BCCList.
do { pszBCCList++; } while (isgraph(*pszBCCList)); } }
if (strlen(szAttachment) > 0) { MAPIFileDesc.ulReserved = 0; MAPIFileDesc.flFlags = 0; MAPIFileDesc.nPosition = 0; MAPIFileDesc.lpszPathName = szAttachment; MAPIFileDesc.lpszFileName = NULL; MAPIFileDesc.lpFileType = NULL;
MessageStruct.nFileCount = 1; MessageStruct.lpFiles = &MAPIFileDesc;
// muck around with the message text (The attachment
// will be attached at the beginning of the mail message
// but it replaces the character at that position)
// BUGBUG -- Do we need to do this? (lylec)
//strcpy(szMessageText," \n");
}
MessageStruct.lpszSubject = szSubject; MessageStruct.lpszNoteText = szMessage;
*plErrorCode = 0;
// Send the message!
ulErrorCode = fnMAPILogon(0L, szUsername, szPassword, MAPIFlags, 0L, &hSession);
if (ulErrorCode != SUCCESS_SUCCESS) { *plErrorCode = (long)ulErrorCode; } else { ulErrorCode = fnMAPISendMail(hSession, 0L, &MessageStruct, 0L, 0L);
if (ulErrorCode != SUCCESS_SUCCESS) { *plErrorCode = (long)ulErrorCode; }
fnMAPILogoff(hSession, 0L, 0L, 0L); }
FreeLibrary(hLibrary);
return S_OK; }
HRESULT CScriptHost::OUTPUTDEBUGSTRING(BSTR bstrMessage) { VERIFY_THREAD();
if (!_fIsPrimaryScript) MessageEventPump(FALSE);
TCHAR szText[ (MSGDATABUFSIZE-1) / sizeof(TCHAR) ];
CScriptSite *site = GetSite(); const TCHAR *pszScriptName = L""; if (site) { pszScriptName = _tcsrchr((LPTSTR)site->_cstrName, _T('\\')); if (!pszScriptName) pszScriptName = (LPTSTR)site->_cstrName; } int cChars = _snwprintf(szText, ARRAY_SIZE(szText), L"%.12s \t%s", pszScriptName, bstrMessage);
szText[ARRAY_SIZE(szText) - 1] = 0;
if (cChars < 0) cChars = ARRAY_SIZE(szText); else cChars++;
PostToThread(_pMT, MD_OUTPUTDEBUGSTRING, szText, cChars * sizeof(TCHAR)); return S_OK; }
HRESULT CScriptHost::UnevalString(BSTR bstrInput, BSTR *pbstrOutput) { int nInputLength = SysStringLen(bstrInput); OLECHAR tmpBuf[512]; OLECHAR *pTmp = 0; OLECHAR *pOutputBuffer;
*pbstrOutput = 0; if (sizeof(OLECHAR) * (nInputLength * 2 + 2) > sizeof(tmpBuf)) { pTmp = (OLECHAR *)MemAlloc(sizeof(OLECHAR) * (nInputLength * 2 + 2)); if (!pTmp) return E_OUTOFMEMORY; pOutputBuffer = pTmp; } else { pOutputBuffer = tmpBuf; } int j = 0; pOutputBuffer[j++] = L'"'; for(OLECHAR *pInputEnd = bstrInput + nInputLength; bstrInput < pInputEnd; ++bstrInput) { switch(*bstrInput) { case L'\\': case L'"': case L'\'': pOutputBuffer[j] = L'\\'; pOutputBuffer[j+1] = *bstrInput; j += 2; break; case L'\n': pOutputBuffer[j] = L'\\'; pOutputBuffer[j+1] = L'n'; j += 2; break; case L'\r': pOutputBuffer[j] = L'\\'; pOutputBuffer[j+1] = L'r'; j += 2; break; case L'\t': pOutputBuffer[j] = L'\\'; pOutputBuffer[j+1] = L't'; j += 2; break; default: pOutputBuffer[j++] = *bstrInput; break; } } pOutputBuffer[j++] = L'"'; *pbstrOutput = SysAllocStringLen(pOutputBuffer, j);
if (pTmp) MemFree(pTmp);
if (!*pbstrOutput) return E_OUTOFMEMORY;
return S_OK; }
HRESULT CScriptHost::CopyOrAppendFile(BSTR bstrSrc, BSTR bstrDst, long nSrcOffset, long nSrcLength, VARIANT_BOOL fAppend, long *pnSrcFilePosition) { HRESULT hr = S_OK; HANDLE hDst = INVALID_HANDLE_VALUE; HANDLE hSrcFile = INVALID_HANDLE_VALUE; BY_HANDLE_FILE_INFORMATION fi = {0}; long nEndPos; long nLen; DWORD nBytesRead; DWORD nBytesWritten; char rgBuffer[4096];
hDst = CreateFile( bstrDst, GENERIC_WRITE, FILE_SHARE_READ, 0, (fAppend ? OPEN_ALWAYS : CREATE_ALWAYS), FILE_ATTRIBUTE_NORMAL, 0);
if (hDst == INVALID_HANDLE_VALUE) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; }
hSrcFile = CreateFile(bstrSrc, GENERIC_READ, FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if (hSrcFile == INVALID_HANDLE_VALUE) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; }
GetFileInformationByHandle(hSrcFile, &fi); if (fi.nFileSizeHigh != 0 || (fi.nFileSizeLow & 0x80000000) != 0) { hr = E_FAIL;//HRESULT_FROM_WIN32(?????);
goto Cleanup; } if (nSrcLength == -1) nEndPos = (long)fi.nFileSizeLow; else { if ( ((_int64)nSrcOffset + nSrcLength) > 0x7f000000) { hr = E_INVALIDARG; goto Cleanup; } nEndPos = nSrcOffset + nSrcLength; } if (nEndPos > (long)fi.nFileSizeLow) nEndPos = (long)fi.nFileSizeLow;
if (SetFilePointer(hSrcFile, nSrcOffset, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; } if (SetFilePointer(hDst, 0, 0, FILE_END) == INVALID_SET_FILE_POINTER) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; } while (nSrcOffset < nEndPos) { nLen = nEndPos - nSrcOffset; if (nLen > sizeof(rgBuffer)) nLen = sizeof(rgBuffer);
if (!ReadFile(hSrcFile, rgBuffer, nLen, &nBytesRead, 0)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; } if (!WriteFile(hDst, rgBuffer, nBytesRead, &nBytesWritten, 0)) { hr = HRESULT_FROM_WIN32(GetLastError()); goto Cleanup; } nSrcOffset += nBytesRead; } if (pnSrcFilePosition) *pnSrcFilePosition = nSrcOffset; Cleanup: if (hDst != INVALID_HANDLE_VALUE) CloseHandle(hDst); if (hSrcFile != INVALID_HANDLE_VALUE) CloseHandle(hSrcFile); return hr; }
HRESULT CScriptHost::ASSERT(VARIANT_BOOL fAssert, BSTR bstrMessage) { VERIFY_THREAD();
if (!_fIsPrimaryScript) MessageEventPump(FALSE);
if (!fAssert) { CHAR ach[1024];
// Add name of currently executing script to the assert message.
if (!GetSite() || !GetSite()->_achPath[0]) { ach[0] = 0; } else { // Try to chop off directory name.
TCHAR * pchName = wcsrchr(GetSite()->_achPath, _T('\\')); if (pchName) pchName += 1; else pchName = GetSite()->_achPath;
WideCharToMultiByte( CP_ACP, 0, pchName, -1, ach, MAX_PATH, NULL, NULL);
strcat(ach, ": "); }
// Add message to the assert.
if (!bstrMessage || !*bstrMessage) { strcat(ach, "MTScript Script Assert"); } else { WideCharToMultiByte( CP_ACP, 0, bstrMessage, -1, &ach[strlen(ach)], ARRAY_SIZE(ach) - MAX_PATH - 3, NULL, NULL); }
#if DBG == 1
AssertSz(FALSE, ach); #else
if (MessageBoxA(NULL, ach, "MTScript Script Assert", MB_OKCANCEL | MB_SETFOREGROUND) == IDCANCEL) return E_FAIL; #endif
}
return S_OK; }
HRESULT CScriptHost::Sleep (int nTimeout) { VERIFY_THREAD();
MessageEventPump(TRUE, 0, NULL, FALSE, (DWORD)nTimeout);
return S_OK; }
HRESULT CScriptHost::Reboot() { VERIFY_THREAD();
PostToThread(_pMT, MD_REBOOT);
return S_OK; }
HRESULT CScriptHost::NotifyScript(BSTR bstrEvent, VARIANT vData) { HRESULT hr = S_OK; VARIANT *pvar;
VERIFY_THREAD();
// Check for data types which we don't support.
if ( V_ISBYREF(&vData) || V_ISARRAY(&vData) || V_ISVECTOR(&vData) || V_VT(&vData) == VT_DISPATCH //$ FUTURE: Support this later
|| V_VT(&vData) == VT_UNKNOWN) { return E_INVALIDARG; }
if (!_pMT->_pMachine) { return S_OK; }
pvar = new VARIANT[2];
VariantInit(&pvar[0]); VariantInit(&pvar[1]);
V_VT(&pvar[0]) = VT_BSTR; V_BSTR(&pvar[0]) = SysAllocString(bstrEvent);
VariantCopy(&pvar[1], &vData);
PostToThread(_pMT->_pMachine, MD_NOTIFYSCRIPT, &pvar, sizeof(VARIANT*));
return hr; }
HRESULT CScriptHost::RegisterEventSource(IDispatch *pDisp, BSTR bstrProgID) { HRESULT hr; CScriptEventSink *pSink = NULL;
pSink = new CScriptEventSink(this); if (!pSink) return E_OUTOFMEMORY;
hr = pSink->Connect(pDisp, bstrProgID); if (!hr) { _aryEvtSinks.Append(pSink); } else pSink->Release();
return hr; }
HRESULT CScriptHost::UnregisterEventSource(IDispatch *pDisp) { int i; BOOL fFound = FALSE;
for (i = 0; i < _aryEvtSinks.Size(); i++) { if (_aryEvtSinks[i]->IsThisYourSource(pDisp)) { _aryEvtSinks[i]->Disconnect();
_aryEvtSinks.ReleaseAndDelete(i);
fFound = TRUE; break; } }
return (fFound) ? S_OK : E_INVALIDARG; }
HRESULT CScriptHost::get_HostMajorVer(long *plMajorVer) { if (!plMajorVer) return E_POINTER;
*plMajorVer = IConnectedMachine_lVersionMajor;
return S_OK; }
HRESULT CScriptHost::get_HostMinorVer(long *plMinorVer) { if (!plMinorVer) return E_POINTER;
*plMinorVer = IConnectedMachine_lVersionMinor;
return S_OK; }
HRESULT CScriptHost::get_StatusValue(long nIndex, long *pnStatus) { return _pMT->get_StatusValue(nIndex, pnStatus); }
HRESULT CScriptHost::put_StatusValue(long nIndex, long nStatus) { return _pMT->put_StatusValue(nIndex, nStatus); }
|