1920 lines
48 KiB
C++
1920 lines
48 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1998
|
|
//
|
|
// File: mtscript.cxx
|
|
//
|
|
// Contents: Implementation of the MTScript class
|
|
//
|
|
// Written by Lyle Corbin
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "headers.hxx"
|
|
|
|
#include <shellapi.h>
|
|
#include <advpub.h> // for RegInstall
|
|
|
|
#include "StatusDialog.h"
|
|
#include "RegSettingsIO.h"
|
|
|
|
HINSTANCE g_hInstDll;
|
|
HINSTANCE g_hinstAdvPack = NULL;
|
|
REGINSTALL g_pfnRegInstall = NULL;
|
|
|
|
const TCHAR *g_szWindowName = _T("MTScript");
|
|
|
|
LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
|
|
|
|
//
|
|
// Global variables
|
|
//
|
|
|
|
HINSTANCE g_hInstance = NULL;
|
|
|
|
EXTERN_C HANDLE g_hProcessHeap = NULL;
|
|
DWORD g_dwFALSE = 0;
|
|
|
|
DeclareTagOther(tagDebugger, "MTScript", "Register with script debugger (MUST RESTART)");
|
|
DeclareTagOther(tagIMSpy, "!Memory", "Register IMallocSpy (MUST RESTART)");
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ErrorPopup
|
|
//
|
|
// Synopsis: Displays a message to the user.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#define ERRPOPUP_BUFSIZE 300
|
|
|
|
void
|
|
ErrorPopup(LPWSTR szMessage)
|
|
{
|
|
WCHAR achBuf[ERRPOPUP_BUFSIZE];
|
|
|
|
_snwprintf(achBuf, ERRPOPUP_BUFSIZE, L"%s: (%d)", szMessage, GetLastError());
|
|
|
|
achBuf[ERRPOPUP_BUFSIZE - 1] = L'\0';
|
|
|
|
MessageBox(NULL, achBuf, L"MTScript", MB_OK | MB_SETFOREGROUND);
|
|
}
|
|
|
|
int PrintfMessageBox(HWND hwnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType, ...)
|
|
{
|
|
TCHAR chBuffer[256];
|
|
va_list args;
|
|
va_start(args, uType);
|
|
_vsnwprintf(chBuffer, 256, lpText, args);
|
|
va_end(args);
|
|
return MessageBox(hwnd, chBuffer, lpCaption, uType);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: LoadAdvPack
|
|
//
|
|
// Synopsis: Loads AdvPack.dll for DLL registration.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
LoadAdvPack()
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
g_hinstAdvPack = LoadLibrary(_T("ADVPACK.DLL"));
|
|
|
|
if (!g_hinstAdvPack)
|
|
goto Error;
|
|
|
|
g_pfnRegInstall = (REGINSTALL)GetProcAddress(g_hinstAdvPack, achREGINSTALL);
|
|
|
|
if (!g_pfnRegInstall)
|
|
goto Error;
|
|
|
|
Cleanup:
|
|
return hr;
|
|
|
|
Error:
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
if (g_hinstAdvPack)
|
|
{
|
|
FreeLibrary(g_hinstAdvPack);
|
|
}
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: Register
|
|
//
|
|
// Synopsis: Register the various important information needed by this
|
|
// executable.
|
|
//
|
|
// Notes: Uses AdvPack.dll and an INF file to do the registration
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
Register()
|
|
{
|
|
HRESULT hr;
|
|
OLECHAR strClsid[40];
|
|
TCHAR keyName [256];
|
|
STRTABLE stReg = { 0, NULL };
|
|
|
|
if (!g_hinstAdvPack)
|
|
{
|
|
hr = LoadAdvPack();
|
|
if (hr)
|
|
goto Cleanup;
|
|
}
|
|
|
|
hr = g_pfnRegInstall(GetModuleHandle(NULL), "Register", &stReg);
|
|
|
|
if (!hr)
|
|
{
|
|
DWORD dwRet;
|
|
HKEY hKey;
|
|
BOOL fSetACL = FALSE;
|
|
|
|
// If the access key already exists, then don't modify it in case
|
|
// someone changed it from the defaults.
|
|
|
|
StringFromGUID2(CLSID_RemoteMTScript, strClsid, 40);
|
|
|
|
wsprintf (keyName, TEXT("APPID\\%s"), strClsid);
|
|
|
|
dwRet = RegOpenKeyEx (HKEY_CLASSES_ROOT,
|
|
keyName,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hKey);
|
|
|
|
if (dwRet == ERROR_SUCCESS)
|
|
{
|
|
dwRet = RegQueryValueEx (hKey,
|
|
TEXT("AccessPermission"),
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (dwRet != ERROR_SUCCESS)
|
|
{
|
|
fSetACL = TRUE;
|
|
}
|
|
|
|
RegCloseKey (hKey);
|
|
}
|
|
else
|
|
{
|
|
fSetACL = TRUE;
|
|
}
|
|
|
|
if (fSetACL)
|
|
{
|
|
// Give everyone access rights
|
|
hr = ChangeAppIDACL(CLSID_RemoteMTScript, _T("EVERYONE"), TRUE, TRUE, TRUE);
|
|
|
|
if (!hr)
|
|
{
|
|
// Deny everyone launch rights
|
|
hr = ChangeAppIDACL(CLSID_RemoteMTScript, _T("EVERYONE"), FALSE, TRUE, TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
Cleanup:
|
|
RegFlushKey(HKEY_CLASSES_ROOT);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+------------------------------------------------------------------------
|
|
//
|
|
// Function: Unregister
|
|
//
|
|
// Synopsis: Undo the actions of Register.
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
Unregister()
|
|
{
|
|
HRESULT hr;
|
|
HKEY hKey;
|
|
DWORD dwRet;
|
|
OLECHAR strClsid[40];
|
|
TCHAR keyName [256];
|
|
|
|
STRTABLE stReg = { 0, NULL };
|
|
|
|
if (!g_hinstAdvPack)
|
|
{
|
|
hr = LoadAdvPack();
|
|
if (hr)
|
|
goto Cleanup;
|
|
}
|
|
|
|
//
|
|
// Remove the security keys that we created while registering
|
|
//
|
|
|
|
StringFromGUID2(CLSID_RemoteMTScript, strClsid, 40);
|
|
|
|
wsprintf (keyName, TEXT("APPID\\%s"), strClsid);
|
|
|
|
dwRet = RegOpenKeyEx (HKEY_CLASSES_ROOT,
|
|
keyName,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hKey);
|
|
|
|
if (dwRet == ERROR_SUCCESS)
|
|
{
|
|
RegDeleteValue (hKey, TEXT("AccessPermission"));
|
|
RegDeleteValue (hKey, TEXT("LaunchPermission"));
|
|
|
|
RegCloseKey (hKey);
|
|
}
|
|
|
|
hr = g_pfnRegInstall(GetModuleHandle(NULL), "Unregister", &stReg);
|
|
|
|
Cleanup:
|
|
RegFlushKey(HKEY_CLASSES_ROOT);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+------------------------------------------------------------------------
|
|
//
|
|
// Function: IAmTheOnlyMTScript, private
|
|
//
|
|
// Synopsis: Guarantees that only 1 MTScript gets to run
|
|
//
|
|
// Arguments:
|
|
//
|
|
// Returns: True is there is not already a running MTScript.
|
|
//
|
|
// Note: This function "leaks" a Mutex handle intentionally.
|
|
// The system frees this handle on exit - so we know
|
|
// for sure that we have finished all other cleanup
|
|
// before it OK for another instance of MTScript to run.
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
|
|
static bool IAmTheOnlyMTScript()
|
|
{
|
|
HANDLE hMutex = CreateMutex(0, FALSE, g_szWindowName);
|
|
if (!hMutex)
|
|
{
|
|
ErrorPopup(_T("Cannot create MTScript mutex!"));
|
|
return false;
|
|
}
|
|
if( GetLastError() == ERROR_ALREADY_EXISTS)
|
|
{
|
|
ErrorPopup(_T("Cannot run more than one mtscript.exe!"));
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
//+------------------------------------------------------------------------
|
|
//
|
|
// Function: WinMain, public
|
|
//
|
|
// Synopsis: Entry routine called by Windows upon startup.
|
|
//
|
|
// Arguments: [hInstance] -- handle to the program's instance
|
|
// [hPrevInstance] -- Always NULL
|
|
// [lpCmdLine] -- Command line arguments
|
|
// [nCmdShow] -- Value to be passed to ShowWindow when the
|
|
// main window is initialized.
|
|
//
|
|
// Returns: FALSE on error or the value passed from PostQuitMessage on exit
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
|
|
EXTERN_C int PASCAL
|
|
WinMain(
|
|
HINSTANCE hInstance,
|
|
HINSTANCE hPrevInstance,
|
|
LPSTR lpCmdLine,
|
|
int nCmdShow)
|
|
{
|
|
OSVERSIONINFO ovi;
|
|
CMTScript * pMT = NULL;
|
|
int iRet = 0;
|
|
#if DBG == 1
|
|
IMallocSpy * pSpy = NULL;
|
|
#endif
|
|
|
|
#ifdef USE_STACK_SPEW
|
|
InitChkStk(0xCCCCCCCC);
|
|
#endif
|
|
|
|
//
|
|
// Initialize data structures.
|
|
//
|
|
g_hProcessHeap = GetProcessHeap();
|
|
g_hInstance = hInstance;
|
|
|
|
#if DBG == 1
|
|
DbgExRestoreDefaultDebugState();
|
|
#endif
|
|
|
|
//
|
|
// Get system information
|
|
//
|
|
ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
|
|
|
|
GetVersionEx(&ovi);
|
|
|
|
if (ovi.dwPlatformId != VER_PLATFORM_WIN32_NT)
|
|
{
|
|
// Win95 doesn't implement MessageBoxW
|
|
MessageBoxA(NULL,
|
|
"MTScript",
|
|
"This program can only be run on Windows NT",
|
|
MB_OK | MB_SETFOREGROUND);
|
|
|
|
goto Error;
|
|
}
|
|
|
|
if (lpCmdLine && _stricmp(&lpCmdLine[1], "register") == 0)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = Register();
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
|
|
return 0;
|
|
}
|
|
else if (lpCmdLine && _stricmp(&lpCmdLine[1], "unregister") == 0)
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = Unregister();
|
|
if (FAILED(hr))
|
|
goto Error;
|
|
|
|
return 0;
|
|
}
|
|
|
|
if (!IAmTheOnlyMTScript() )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
#if DBG == 1
|
|
if (DbgExIsTagEnabled(tagIMSpy))
|
|
{
|
|
pSpy = (IMallocSpy *)DbgExGetMallocSpy();
|
|
|
|
if (pSpy)
|
|
{
|
|
CoRegisterMallocSpy(pSpy);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// Can't put it on the stack because it depends on having zero'd memory
|
|
//
|
|
pMT = new CMTScript();
|
|
if (!pMT)
|
|
goto Error;
|
|
|
|
if (!pMT->Init())
|
|
goto Error;
|
|
|
|
//
|
|
// Start doing real stuff
|
|
//
|
|
iRet = pMT->ThreadMain();
|
|
|
|
Cleanup:
|
|
if (pMT)
|
|
pMT->Release();
|
|
|
|
#if DBG == 1
|
|
if (pSpy)
|
|
{
|
|
// Note, due to the fact that we do not have control over DLL unload
|
|
// ordering, the IMallocSpy implementation may report false leaks.
|
|
// (lylec) The only way to fix this is to explicitely load all
|
|
// dependent DLLs and unload them in their proper order (mshtmdbg.dll
|
|
// last).
|
|
CoRevokeMallocSpy();
|
|
}
|
|
#endif
|
|
|
|
return iRet;
|
|
|
|
Error:
|
|
iRet = 1;
|
|
goto Cleanup;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// CMTScript::OPTIONSETTINGS class
|
|
//
|
|
// Handles user-configurable options
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CMTScript::OPTIONSETTINGS::OPTIONSETTINGS()
|
|
: cstrScriptPath(CSTR_NOINIT),
|
|
cstrInitScript(CSTR_NOINIT)
|
|
{
|
|
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::OPTIONSETTINGS::GetModulePath, public
|
|
//
|
|
// Synopsis: Returns the path of this executable. Used for finding other
|
|
// related files.
|
|
//
|
|
// Arguments: [pstr] -- Place to put path.
|
|
//
|
|
// Notes: Any existing string in pstr will be cleared.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
CMTScript::OPTIONSETTINGS::GetModulePath(CStr *pstr)
|
|
{
|
|
WCHAR *pch;
|
|
WCHAR achBuf[MAX_PATH];
|
|
|
|
pstr->Free();
|
|
|
|
if (!GetModuleFileName(NULL, achBuf, sizeof(achBuf)))
|
|
return;
|
|
|
|
pch = wcsrchr(achBuf, L'\\');
|
|
if (!pch)
|
|
return;
|
|
|
|
*pch = L'\0';
|
|
|
|
pstr->Set(achBuf);
|
|
}
|
|
|
|
void
|
|
CMTScript::OPTIONSETTINGS::GetScriptPath(CStr *cstrScript)
|
|
{
|
|
LOCK_LOCALS(this);
|
|
if (cstrScriptPath.Length() == 0)
|
|
{
|
|
// TCHAR achBuf[MAX_PATH];
|
|
// TCHAR *psz;
|
|
|
|
GetModulePath(cstrScript);
|
|
|
|
/*
|
|
cstrScript->Append(L"\\..\\..\\scripts");
|
|
|
|
GetFullPathName(*cstrScript, MAX_PATH, achBuf, &psz);
|
|
|
|
cstrScript->Set(achBuf);
|
|
*/
|
|
}
|
|
else
|
|
{
|
|
cstrScript->Set(cstrScriptPath);
|
|
}
|
|
}
|
|
|
|
void
|
|
CMTScript::OPTIONSETTINGS::GetInitScript(CStr *cstr)
|
|
{
|
|
static WCHAR * pszInitScript = L"mtscript.js";
|
|
|
|
LOCK_LOCALS(this);
|
|
if (cstrInitScript.Length() == 0)
|
|
{
|
|
cstr->Set(pszInitScript);
|
|
}
|
|
else
|
|
{
|
|
cstr->Set(cstrInitScript);
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// CMTScript class
|
|
//
|
|
// Handles the main UI thread
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CMTScript::CMTScript()
|
|
{
|
|
Assert(_fInDestructor == FALSE);
|
|
Assert(_pGIT == NULL);
|
|
Assert(_dwPublicDataCookie == 0);
|
|
Assert(_dwPrivateDataCookie == 0);
|
|
Assert(_dwPublicSerialNum == 0);
|
|
Assert(_dwPrivateSerialNum == 0);
|
|
|
|
VariantInit(&_vPublicData);
|
|
VariantInit(&_vPrivateData);
|
|
|
|
_ulRefs = 1;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::~CMTScript, public
|
|
//
|
|
// Synopsis: destructor
|
|
//
|
|
// Notes: Anything done in the Init() call must be undone here. This
|
|
// method must also be able to handle a partial initialization,
|
|
// in case something inside Init() failed halfway through.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CMTScript::~CMTScript()
|
|
{
|
|
int i;
|
|
|
|
VERIFY_THREAD();
|
|
|
|
// Anything done in the Init() call must be undone here
|
|
|
|
UnregisterClassObjects();
|
|
|
|
_fInDestructor = TRUE;
|
|
|
|
//
|
|
// Process any pending messages such as PROCESSTHREADTERMINATE now.
|
|
//
|
|
HandleThreadMessage();
|
|
|
|
// Cleanup any running processes.
|
|
|
|
for (i = 0; i < _aryProcesses.Size(); i++)
|
|
{
|
|
Shutdown(_aryProcesses[i]);
|
|
}
|
|
_aryProcesses.ReleaseAll();
|
|
|
|
//
|
|
// This must be done in reverse order because the primary script (element 0)
|
|
// must be shutdown last.
|
|
//
|
|
for (i = _aryScripts.Size() - 1; i >= 0; i--)
|
|
{
|
|
Shutdown(_aryScripts[i]);
|
|
}
|
|
_aryScripts.ReleaseAll();
|
|
|
|
if (_pMachine)
|
|
{
|
|
Shutdown(_pMachine);
|
|
ReleaseInterface(_pMachine);
|
|
}
|
|
|
|
if (_dwPublicDataCookie != 0)
|
|
{
|
|
_pGIT->RevokeInterfaceFromGlobal(_dwPublicDataCookie);
|
|
}
|
|
|
|
if (_dwPrivateDataCookie != 0)
|
|
{
|
|
_pGIT->RevokeInterfaceFromGlobal(_dwPrivateDataCookie);
|
|
}
|
|
|
|
ReleaseInterface(_pTIMachine);
|
|
ReleaseInterface(_pTypeLibEXE);
|
|
ReleaseInterface(_pGIT);
|
|
|
|
ReleaseInterface(_pJScriptFactory);
|
|
|
|
VariantClear(&_vPublicData);
|
|
VariantClear(&_vPrivateData);
|
|
|
|
DeInitScriptDebugger();
|
|
|
|
CoUninitialize();
|
|
|
|
CleanupUI();
|
|
|
|
// This delete call must be done after we've destroyed our window, which
|
|
// in turn will destroy the status window.
|
|
delete _pStatusDialog;
|
|
_pStatusDialog = NULL;
|
|
}
|
|
|
|
HRESULT
|
|
CMTScript::QueryInterface(REFIID iid, void **ppvObj)
|
|
{
|
|
if (iid == IID_IUnknown)
|
|
{
|
|
*ppvObj = (IUnknown *)this;
|
|
}
|
|
else
|
|
{
|
|
*ppvObj = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
((IUnknown *)*ppvObj)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::Init, public
|
|
//
|
|
// Synopsis: Initialization method that can fail.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CMTScript::Init()
|
|
{
|
|
HRESULT hr;
|
|
|
|
_dwThreadId = GetCurrentThreadId();
|
|
|
|
if (!CThreadComm::Init())
|
|
return FALSE;
|
|
|
|
//
|
|
// Load our default configuration
|
|
//
|
|
UpdateOptionSettings(FALSE);
|
|
|
|
if (FAILED(CoInitializeEx(NULL,
|
|
COINIT_MULTITHREADED |
|
|
COINIT_DISABLE_OLE1DDE |
|
|
COINIT_SPEED_OVER_MEMORY)))
|
|
{
|
|
goto Error;
|
|
}
|
|
|
|
// This code may be needed if we want to do our own custom security.
|
|
// However, it is easier to let DCOM do it for us.
|
|
|
|
// The following code removes all security always. It cannot be overridden
|
|
// using the registry.
|
|
|
|
if (FAILED(CoInitializeSecurity(NULL,
|
|
-1,
|
|
NULL,
|
|
NULL,
|
|
RPC_C_AUTHN_LEVEL_NONE,
|
|
RPC_C_IMP_LEVEL_IMPERSONATE,
|
|
NULL,
|
|
EOAC_NONE,
|
|
NULL)))
|
|
{
|
|
goto Error;
|
|
}
|
|
|
|
hr = LoadTypeLibraries();
|
|
if (hr)
|
|
goto Error;
|
|
|
|
InitScriptDebugger();
|
|
|
|
if (FAILED(CoCreateInstance(CLSID_StdGlobalInterfaceTable,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER,
|
|
IID_IGlobalInterfaceTable,
|
|
(void **)&_pGIT)))
|
|
{
|
|
goto Error;
|
|
}
|
|
|
|
//
|
|
// Create our hidden window and put an icon on the taskbar
|
|
//
|
|
if (!ConfigureUI())
|
|
goto Error;
|
|
|
|
//
|
|
// Run the initial script
|
|
//
|
|
if (FAILED(RunScript(NULL, NULL)))
|
|
goto Error;
|
|
|
|
if (RegisterClassObjects(this) != S_OK)
|
|
goto Error;
|
|
|
|
#if DBG == 1
|
|
OpenStatusDialog();
|
|
#endif
|
|
return TRUE;
|
|
|
|
Error:
|
|
return FALSE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::ThreadMain, public
|
|
//
|
|
// Synopsis: Main UI message loop.
|
|
//
|
|
// Arguments: [hWnd] -- Modeless Dialog HWND
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
DWORD
|
|
CMTScript::ThreadMain()
|
|
{
|
|
DWORD dwRet;
|
|
HANDLE ahEvents[3];
|
|
int cEvents = 2;
|
|
DWORD dwReturn = 0;
|
|
|
|
VERIFY_THREAD();
|
|
|
|
SetName("CMTScript");
|
|
|
|
// Don't need to call ThreadStarted() because StartThread() was not used
|
|
// to start the main thread!
|
|
|
|
ahEvents[0] = _hCommEvent;
|
|
ahEvents[1] = GetPrimaryScript()->hThread();
|
|
|
|
while (TRUE)
|
|
{
|
|
MSG msg;
|
|
|
|
//
|
|
// Purge out all window messages.
|
|
//
|
|
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
|
|
{
|
|
if (msg.message == WM_QUIT)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (_pStatusDialog)
|
|
{
|
|
if (_pStatusDialog->IsDialogMessage(&msg))
|
|
continue;
|
|
}
|
|
TranslateMessage(&msg);
|
|
DispatchMessage(&msg);
|
|
}
|
|
|
|
//
|
|
// Wait for anything we need to deal with.
|
|
//
|
|
dwRet = MsgWaitForMultipleObjects(cEvents,
|
|
ahEvents,
|
|
FALSE,
|
|
INFINITE,
|
|
QS_ALLINPUT);
|
|
|
|
if (dwRet == WAIT_OBJECT_0)
|
|
{
|
|
//
|
|
// Another thread is sending us a message.
|
|
//
|
|
HandleThreadMessage();
|
|
}
|
|
else if (dwRet == WAIT_OBJECT_0 + 1)
|
|
{
|
|
// The Primary Script Thread terminated due to a problem loading
|
|
// the initial script. Bring up the configuration dialog.
|
|
|
|
{
|
|
LOCK_LOCALS(this);
|
|
_aryScripts.ReleaseAll();
|
|
}
|
|
|
|
if (!_fRestarting)
|
|
{
|
|
int iRet = MessageBox(_hwnd, _T("An error occurred loading the default script.\n\nDo you wish to edit the default configuration?"),
|
|
_T("MTScript Error"),
|
|
MB_YESNO | MB_SETFOREGROUND | MB_ICONERROR);
|
|
|
|
if (iRet == IDNO)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
_fRestarting = TRUE; // Preventthe config dialog from doing a restart in this case.
|
|
CConfig * pConfig = new CConfig(this);
|
|
|
|
if (pConfig)
|
|
{
|
|
pConfig->StartThread(NULL);
|
|
|
|
WaitForSingleObject(pConfig->hThread(), INFINITE);
|
|
|
|
pConfig->Release();
|
|
}
|
|
_fRestarting = FALSE;
|
|
}
|
|
else
|
|
{
|
|
_fRestarting = FALSE;
|
|
}
|
|
|
|
//
|
|
// Try re-loading the initial script
|
|
//
|
|
if (FAILED(RunScript(NULL, NULL)))
|
|
goto Error;
|
|
|
|
ahEvents[1] = GetPrimaryScript()->hThread();
|
|
|
|
if (_pStatusDialog)
|
|
_pStatusDialog->Restart();
|
|
|
|
}
|
|
else if (dwRet == WAIT_TIMEOUT)
|
|
{
|
|
// Make sure our message queue is empty first.
|
|
HandleThreadMessage();
|
|
|
|
// Right now we never fall in this loop.
|
|
}
|
|
else if (dwRet == WAIT_FAILED)
|
|
{
|
|
TraceTag((tagError, "MsgWaitForMultipleObjects failure (%d)", GetLastError()));
|
|
|
|
AssertSz(FALSE, "MsgWaitForMultipleObjects failure");
|
|
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
|
|
Cleanup:
|
|
return dwReturn;
|
|
|
|
Error:
|
|
dwReturn = 1;
|
|
goto Cleanup;
|
|
}
|
|
|
|
void
|
|
CMTScript::InitScriptDebugger()
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (!IsTagEnabled(tagDebugger))
|
|
{
|
|
return;
|
|
}
|
|
|
|
hr = CoCreateInstance(CLSID_ProcessDebugManager,
|
|
NULL,
|
|
CLSCTX_INPROC_SERVER |
|
|
CLSCTX_INPROC_HANDLER |
|
|
CLSCTX_LOCAL_SERVER,
|
|
IID_IProcessDebugManager,
|
|
(LPVOID*)&_pPDM);
|
|
if (hr)
|
|
{
|
|
TraceTag((tagError, "Could not create ProcessDebugManager: %x", hr));
|
|
return;
|
|
}
|
|
|
|
hr = THR(_pPDM->CreateApplication(&_pDA));
|
|
if (hr)
|
|
goto Error;
|
|
|
|
_pDA->SetName(L"MTScript");
|
|
|
|
hr = THR(_pPDM->AddApplication(_pDA, &_dwAppCookie));
|
|
if (hr)
|
|
goto Error;
|
|
|
|
return;
|
|
|
|
Error:
|
|
ClearInterface(&_pDA);
|
|
ClearInterface(&_pPDM);
|
|
|
|
return;
|
|
}
|
|
|
|
void
|
|
CMTScript::DeInitScriptDebugger()
|
|
{
|
|
_try
|
|
{
|
|
if (_pPDM)
|
|
{
|
|
_pPDM->RemoveApplication(_dwAppCookie);
|
|
|
|
_pDA->Close();
|
|
|
|
ReleaseInterface(_pPDM);
|
|
ReleaseInterface(_pDA);
|
|
}
|
|
}
|
|
_except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
//$ BUGBUG -- Figure out what's wrong here!
|
|
|
|
// Ignore the crash caused by the Script Debugger
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::HackCreateInstance, public
|
|
//
|
|
// Synopsis: Loads a private jscript.dll since the one that shipped with
|
|
// Win2K is broken for what we need it for.
|
|
//
|
|
// Arguments: [clsid] -- Same parameters as CoCreateInstance.
|
|
// [pUnk] --
|
|
// [clsctx] --
|
|
// [riid] --
|
|
// [ppv] --
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CMTScript::HackCreateInstance(REFCLSID clsid,
|
|
IUnknown *pUnk,
|
|
DWORD clsctx,
|
|
REFIID riid,
|
|
LPVOID *ppv)
|
|
{
|
|
TCHAR achDllPath[MAX_PATH];
|
|
HINSTANCE hInstDll;
|
|
DWORD iRet;
|
|
TCHAR *pch;
|
|
LPFNGETCLASSOBJECT pfnGCO = NULL;
|
|
HRESULT hr;
|
|
DWORD dwJunk;
|
|
BYTE *pBuf = NULL;
|
|
VS_FIXEDFILEINFO *pFI = NULL;
|
|
UINT iLen;
|
|
|
|
if (!_pJScriptFactory && _fHackVersionChecked)
|
|
{
|
|
return S_FALSE;
|
|
}
|
|
|
|
if (!_pJScriptFactory)
|
|
{
|
|
LOCK_LOCALS(this);
|
|
|
|
// Make sure another thread didn't take care of this while we were
|
|
// waiting for the lock.
|
|
|
|
if (!_fHackVersionChecked)
|
|
{
|
|
// Remember we've done this check so we won't do it again.
|
|
_fHackVersionChecked = TRUE;
|
|
|
|
// First, check the version number on the system DLL
|
|
|
|
iRet = GetSystemDirectory(achDllPath, MAX_PATH);
|
|
if (iRet == 0)
|
|
goto Win32Error;
|
|
|
|
_tcscat(achDllPath, _T("\\jscript.dll"));
|
|
|
|
iRet = GetFileVersionInfoSize(achDllPath, &dwJunk);
|
|
if (iRet == 0)
|
|
goto Win32Error;
|
|
|
|
pBuf = new BYTE[iRet];
|
|
|
|
iRet = GetFileVersionInfo(achDllPath, NULL, iRet, pBuf);
|
|
if (iRet == 0)
|
|
goto Win32Error;
|
|
|
|
if (!VerQueryValue(pBuf, _T("\\"), (LPVOID*)&pFI, &iLen))
|
|
goto Win32Error;
|
|
|
|
//
|
|
// Is the system DLL a version that has our needed fix?
|
|
//
|
|
// Version 5.1.0.4702 has the fix but isn't approved for Win2K.
|
|
// The first official version that has the fix is 5.5.0.4703.
|
|
//
|
|
if ( (pFI->dwProductVersionMS == 0x00050001 && pFI->dwProductVersionLS >= 4702)
|
|
|| (pFI->dwProductVersionMS >= 0x00050005 && pFI->dwProductVersionLS >= 4703))
|
|
{
|
|
hr = S_FALSE;
|
|
|
|
goto Cleanup;
|
|
}
|
|
|
|
iRet = GetModuleFileName(NULL, achDllPath, MAX_PATH);
|
|
if (iRet == 0)
|
|
goto Win32Error;
|
|
|
|
pch = _tcsrchr(achDllPath, _T('\\'));
|
|
if (pch)
|
|
{
|
|
*pch = _T('\0');
|
|
}
|
|
|
|
_tcscat(achDllPath, _T("\\jscript.dll"));
|
|
|
|
hInstDll = CoLoadLibrary(achDllPath, TRUE);
|
|
if (!hInstDll)
|
|
{
|
|
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
|
|
ErrorPopup(_T("Your copy of JSCRIPT.DLL contains a problem which may prevent you from using this tool.\n")
|
|
_T("Please update that DLL to version 5.1.0.4702 or later.\n")
|
|
_T("You may put the new DLL in the same directory as mtscript.exe to avoid upgrading the system DLL."));
|
|
|
|
// ErrorPopup clears the GetLastError() status.
|
|
goto Cleanup;
|
|
}
|
|
|
|
pfnGCO = (LPFNGETCLASSOBJECT)GetProcAddress(hInstDll, "DllGetClassObject");
|
|
if (!pfnGCO)
|
|
goto Win32Error;
|
|
|
|
hr = (*pfnGCO)(clsid, IID_IClassFactory, (LPVOID*)&_pJScriptFactory);
|
|
if (hr)
|
|
goto Cleanup;
|
|
}
|
|
}
|
|
|
|
if (_pJScriptFactory)
|
|
hr = _pJScriptFactory->CreateInstance(pUnk, riid, ppv);
|
|
else
|
|
hr = S_FALSE;
|
|
|
|
Cleanup:
|
|
delete [] pBuf;
|
|
|
|
return hr;
|
|
|
|
Win32Error:
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::ConfigureUI, public
|
|
//
|
|
// Synopsis: Creates our hidden window and puts an icon on the taskbar
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
BOOL
|
|
CMTScript::ConfigureUI()
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
WNDCLASS wc = { 0 };
|
|
NOTIFYICONDATA ni = { 0 };
|
|
ATOM aWin;
|
|
BOOL fRet;
|
|
|
|
// The window will be a hidden window so we don't set many of the attributes
|
|
|
|
wc.lpfnWndProc = MainWndProc;
|
|
wc.hInstance = g_hInstance;
|
|
wc.lpszClassName = SZ_WNDCLASS;
|
|
|
|
aWin = RegisterClass(&wc);
|
|
if (aWin == 0)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
_hwnd = CreateWindowEx(WS_EX_TOOLWINDOW,
|
|
(LPTSTR)aWin,
|
|
g_szWindowName,
|
|
WS_OVERLAPPED,
|
|
10, // Coordinates don't matter - it will never
|
|
10, // be visible.
|
|
10,
|
|
10,
|
|
NULL,
|
|
NULL,
|
|
g_hInstance,
|
|
(LPVOID)this);
|
|
if (_hwnd == NULL)
|
|
{
|
|
return FALSE;
|
|
}
|
|
|
|
ni.cbSize = sizeof(NOTIFYICONDATA);
|
|
ni.hWnd = _hwnd;
|
|
ni.uID = 1;
|
|
ni.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
|
|
ni.uCallbackMessage = WM_USER;
|
|
ni.hIcon = LoadIcon(g_hInstance, L"myicon_small");
|
|
wcscpy(ni.szTip, L"Remote Script Engine");
|
|
|
|
fRet = Shell_NotifyIcon(NIM_ADD, &ni);
|
|
|
|
DestroyIcon(ni.hIcon);
|
|
|
|
return fRet;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::CleanupUI, public
|
|
//
|
|
// Synopsis: Cleans up UI related things.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
CMTScript::CleanupUI()
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
NOTIFYICONDATA ni = { 0 };
|
|
|
|
if (_hwnd != NULL)
|
|
{
|
|
ni.cbSize = sizeof(NOTIFYICONDATA);
|
|
ni.hWnd = _hwnd;
|
|
ni.uID = 1;
|
|
|
|
Shell_NotifyIcon(NIM_DELETE, &ni);
|
|
|
|
DestroyWindow(_hwnd);
|
|
}
|
|
}
|
|
|
|
HRESULT
|
|
CMTScript::LoadTypeLibraries()
|
|
{
|
|
VERIFY_THREAD();
|
|
HRESULT hr = S_OK;
|
|
|
|
_pTIMachine = NULL;
|
|
|
|
if (!_pTypeLibEXE)
|
|
{
|
|
hr = THR(LoadRegTypeLib(LIBID_MTScriptEngine, 1, 0, 0, &_pTypeLibEXE));
|
|
|
|
if (hr)
|
|
goto Cleanup;
|
|
}
|
|
|
|
if (!_pTIMachine)
|
|
{
|
|
TYPEATTR *pTypeAttr;
|
|
UINT mb = IDYES;
|
|
|
|
hr = THR(_pTypeLibEXE->GetTypeInfoOfGuid(IID_IConnectedMachine, &_pTIMachine));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
hr = THR(_pTIMachine->GetTypeAttr(&pTypeAttr));
|
|
if (hr)
|
|
goto Cleanup;
|
|
|
|
if (pTypeAttr->wMajorVerNum != IConnectedMachine_lVersionMajor || pTypeAttr->wMinorVerNum != IConnectedMachine_lVersionMinor)
|
|
{
|
|
mb = PrintfMessageBox(NULL,
|
|
L"Mtscript.exe version (%d.%d) does not match mtlocal.dll (%d.%d).\n"
|
|
L"You may experience undefined behavior if you continue.\n"
|
|
L"Do you wish to ignore this error and continue?",
|
|
L"Version mismatch error",
|
|
MB_YESNO | MB_ICONWARNING | MB_SETFOREGROUND | MB_DEFBUTTON2,
|
|
IConnectedMachine_lVersionMajor, IConnectedMachine_lVersionMinor,
|
|
pTypeAttr->wMajorVerNum, pTypeAttr->wMinorVerNum);
|
|
}
|
|
_pTIMachine->ReleaseTypeAttr(pTypeAttr);
|
|
if (mb != IDYES)
|
|
return E_FAIL;
|
|
}
|
|
Cleanup:
|
|
|
|
if (hr)
|
|
{
|
|
PrintfMessageBox(NULL,
|
|
_T("FATAL: Could not load type library (%x).\nIs mtlocal.dll registered?"),
|
|
_T("MTScript"),
|
|
MB_OK | MB_ICONERROR | MB_SETFOREGROUND,
|
|
hr);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::ShowMenu, public
|
|
//
|
|
// Synopsis: Displays a menu when the user right-clicks on the tray icon.
|
|
//
|
|
// Arguments: [x] -- x location
|
|
// [y] -- y location
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
CMTScript::ShowMenu(int x, int y)
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
ULONG ulItem;
|
|
|
|
HMENU hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_MAINMENU));
|
|
|
|
if (x == -1 && y == -1)
|
|
{
|
|
POINT pt;
|
|
|
|
GetCursorPos(&pt);
|
|
|
|
x = pt.x;
|
|
y = pt.y;
|
|
}
|
|
|
|
SetForegroundWindow(_hwnd);
|
|
|
|
ulItem = TrackPopupMenuEx(GetSubMenu(hMenu, 0),
|
|
TPM_RETURNCMD |
|
|
TPM_NONOTIFY |
|
|
TPM_RIGHTBUTTON |
|
|
TPM_LEFTALIGN,
|
|
x, y,
|
|
_hwnd,
|
|
NULL);
|
|
switch (ulItem)
|
|
{
|
|
case IDM_EXIT:
|
|
UpdateOptionSettings(true);
|
|
PostQuitMessage(0);
|
|
break;
|
|
|
|
case IDM_CONFIGURE:
|
|
{
|
|
CConfig * pConfig = new CConfig(this);
|
|
|
|
if (pConfig)
|
|
{
|
|
pConfig->StartThread(NULL);
|
|
|
|
pConfig->Release();
|
|
}
|
|
}
|
|
break;
|
|
|
|
case IDM_RESTART:
|
|
Restart();
|
|
|
|
break;
|
|
|
|
case IDM_STATUS:
|
|
if (!_pStatusDialog)
|
|
OpenStatusDialog();
|
|
if (_pStatusDialog)
|
|
_pStatusDialog->Show();
|
|
break;
|
|
|
|
#if DBG == 1
|
|
case IDM_TRACETAG:
|
|
DbgExDoTracePointsDialog(FALSE);
|
|
break;
|
|
|
|
case IDM_MEMORYMON:
|
|
DbgExOpenMemoryMonitor();
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
DestroyMenu(hMenu);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::HandleThreadMessage, public
|
|
//
|
|
// Synopsis: Another thread has sent us a cross-thread message that we need
|
|
// to handle.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
CMTScript::HandleThreadMessage()
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
THREADMSG tm;
|
|
BYTE bData[MSGDATABUFSIZE];
|
|
DWORD cbData;
|
|
|
|
while (GetNextMsg(&tm, (void *)bData, &cbData))
|
|
{
|
|
switch (tm)
|
|
{
|
|
case MD_SECONDARYSCRIPTTERMINATE:
|
|
{
|
|
//
|
|
// A secondary script ended. Find it in our list, remove it,
|
|
// and delete it.
|
|
//
|
|
CScriptHost *pbs = *(CScriptHost**)bData;
|
|
|
|
LOCK_LOCALS(this);
|
|
Verify(_aryScripts.DeleteByValue(pbs));
|
|
|
|
pbs->Release();
|
|
}
|
|
break;
|
|
|
|
case MD_MACHINECONNECT:
|
|
PostToThread(GetPrimaryScript(), MD_MACHINECONNECT);
|
|
break;
|
|
|
|
case MD_SENDTOPROCESS:
|
|
{
|
|
MACHPROC_EVENT_DATA *pmed = *(MACHPROC_EVENT_DATA**)bData;
|
|
|
|
CProcessThread *pProc = FindProcess(pmed->dwProcId);
|
|
|
|
if (pProc && pProc->GetProcComm())
|
|
{
|
|
pProc->GetProcComm()->SendToProcess(pmed);
|
|
}
|
|
else
|
|
{
|
|
V_VT(pmed->pvReturn) = VT_I4;
|
|
V_I4(pmed->pvReturn) = -1;
|
|
SetEvent(pmed->hEvent);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case MD_REBOOT:
|
|
Reboot();
|
|
break;
|
|
|
|
case MD_RESTART:
|
|
Restart();
|
|
break;
|
|
|
|
case MD_PLEASEEXIT:
|
|
PostQuitMessage(0);
|
|
break;
|
|
case MD_OUTPUTDEBUGSTRING:
|
|
if (_pStatusDialog)
|
|
{
|
|
_pStatusDialog->OUTPUTDEBUGSTRING( (LPWSTR) bData);
|
|
}
|
|
break;
|
|
default:
|
|
AssertSz(FALSE, "CMTScript got a message it couldn't handle!");
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::RunScript, public
|
|
//
|
|
// Synopsis: Creates a scripting thread and runs a given script
|
|
// Can be called from any thread.
|
|
//
|
|
// Arguments: [pszPath] -- If NULL, we're starting the primary thread.
|
|
// Otherwise, it's the name of a file in the
|
|
// scripts directory.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CMTScript::RunScript(LPWSTR pszPath, VARIANT *pvarParam)
|
|
{
|
|
HRESULT hr;
|
|
CScriptHost * pScript = NULL;
|
|
CStr cstrScript;
|
|
SCRIPT_PARAMS scrParams;
|
|
|
|
if (!pszPath)
|
|
_options.GetInitScript(&cstrScript);
|
|
else
|
|
cstrScript.Set(pszPath);
|
|
|
|
AssertSz(cstrScript.Length() > 0, "CRASH: Bogus script path");
|
|
|
|
pScript = new CScriptHost(this,
|
|
(pszPath) ? FALSE : TRUE,
|
|
FALSE);
|
|
if (!pScript)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto Error;
|
|
}
|
|
|
|
scrParams.pszPath = cstrScript;
|
|
scrParams.pvarParams = pvarParam;
|
|
|
|
// Race: We can successfully start a thread.
|
|
// That thread can run to completion, and exit.
|
|
// CScriptHost would then post MD_SECONDARYSCRIPTTERMINATE
|
|
// in an attempt to remove the script from the list and free
|
|
// it.
|
|
// Thus, we must add it to the list first, then remove it
|
|
// if the script fails to start.
|
|
{
|
|
LOCK_LOCALS(this);
|
|
_aryScripts.Append(pScript);
|
|
}
|
|
hr = pScript->StartThread(&scrParams);
|
|
if (FAILED(hr) && pszPath) // DO NOT REMOVE THE PRIMARY SCRIPT! Instead, return SUCCESS.
|
|
{ // The main thread makes a special check for the primary script
|
|
LOCK_LOCALS(this);
|
|
Verify(_aryScripts.DeleteByValue(pScript));
|
|
pScript->Release();
|
|
goto Error;
|
|
}
|
|
Assert(pszPath || _aryScripts.Size() == 1);
|
|
|
|
return S_OK;
|
|
|
|
Error:
|
|
ReleaseInterface(pScript);
|
|
if (pszPath == 0)
|
|
ErrorPopup(L"An error occurred running the initial script");
|
|
|
|
return hr;
|
|
}
|
|
|
|
HRESULT
|
|
CMTScript::UpdateOptionSettings(BOOL fSave)
|
|
{
|
|
LOCK_LOCALS(&_options); // Makes this method thread safe
|
|
|
|
static REGKEYINFORMATION aKeyValuesOptions[] =
|
|
{
|
|
{ _T("File Paths"), RKI_KEY, 0 },
|
|
{ _T("Script Path"), RKI_EXPANDSZ, offsetof(OPTIONSETTINGS, cstrScriptPath) },
|
|
{ _T("Initial Script"), RKI_STRING, offsetof(OPTIONSETTINGS, cstrInitScript) },
|
|
};
|
|
|
|
HRESULT hr;
|
|
hr = RegSettingsIO(g_szRegistry, fSave, aKeyValuesOptions, ARRAY_SIZE(aKeyValuesOptions), (BYTE *)&_options);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::Restart, public
|
|
//
|
|
// Synopsis: Restarts like we were just starting
|
|
//
|
|
// Notes: All currently running scripts are stopped and destroyed.
|
|
// Public information is freed and then everything is restarted.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
CMTScript::Restart()
|
|
{
|
|
int i;
|
|
|
|
// Make sure the status dialog doesn't try to do anything while we're
|
|
// restarting.
|
|
|
|
if (_pStatusDialog)
|
|
_pStatusDialog->Pause();
|
|
|
|
// Kill all running scripts and start over.
|
|
for (i = _aryScripts.Size() - 1; i >= 0; i--)
|
|
{
|
|
Shutdown(_aryScripts[i]);
|
|
|
|
// Scripts will be released when they notify us of their being
|
|
// shutdown.
|
|
}
|
|
|
|
// Kill all running processes
|
|
for (i = _aryProcesses.Size() - 1; i >= 0; i--)
|
|
{
|
|
Shutdown(_aryProcesses[i]);
|
|
}
|
|
_aryProcesses.ReleaseAll();
|
|
|
|
if (_dwPublicDataCookie != 0)
|
|
{
|
|
_pGIT->RevokeInterfaceFromGlobal(_dwPublicDataCookie);
|
|
_dwPublicDataCookie = 0;
|
|
}
|
|
|
|
if (_dwPrivateDataCookie != 0)
|
|
{
|
|
_pGIT->RevokeInterfaceFromGlobal(_dwPrivateDataCookie);
|
|
_dwPrivateDataCookie = 0;
|
|
}
|
|
|
|
VariantClear(&_vPublicData);
|
|
VariantClear(&_vPrivateData);
|
|
|
|
// Reset the statusvalues to 0
|
|
memset(_rgnStatusValues, 0, sizeof(_rgnStatusValues));
|
|
|
|
// The above call to shutdown will terminate the primary script thread,
|
|
// which will trigger the restart code in ThreadMain().
|
|
|
|
_fRestarting = TRUE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::OpenStatusDialog, public
|
|
//
|
|
// Synopsis: Opens the status modeless dialog
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
CMTScript::OpenStatusDialog()
|
|
{
|
|
if (!_pStatusDialog)
|
|
_pStatusDialog = new CStatusDialog(_hwnd, this);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::Reboot, public
|
|
//
|
|
// Synopsis: Reboots the local machine. The user must have appropriate
|
|
// rights to do so.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
void
|
|
CMTScript::Reboot()
|
|
{
|
|
TOKEN_PRIVILEGES tp;
|
|
LUID luid;
|
|
HANDLE hToken;
|
|
|
|
//
|
|
// Try to make sure we get shutdown last
|
|
//
|
|
SetProcessShutdownParameters(0x101, 0);
|
|
|
|
//
|
|
// Setup shutdown priviledges
|
|
//
|
|
|
|
if (!OpenProcessToken(GetCurrentProcess(),
|
|
TOKEN_ADJUST_PRIVILEGES,
|
|
&hToken))
|
|
{
|
|
goto Error;
|
|
}
|
|
|
|
if (!LookupPrivilegeValue(NULL,
|
|
SE_SHUTDOWN_NAME,
|
|
&luid))
|
|
{
|
|
goto Error;
|
|
}
|
|
|
|
tp.PrivilegeCount = 1;
|
|
tp.Privileges[0].Luid = luid;
|
|
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
|
|
|
AdjustTokenPrivileges(hToken,
|
|
FALSE,
|
|
&tp,
|
|
sizeof(TOKEN_PRIVILEGES),
|
|
(PTOKEN_PRIVILEGES) NULL,
|
|
(PDWORD) NULL);
|
|
|
|
if (GetLastError() != ERROR_SUCCESS)
|
|
goto Error;
|
|
|
|
PostQuitMessage(0);
|
|
|
|
// BUGBUG -- This call is Windows2000 specific.
|
|
|
|
ExitWindowsEx(EWX_REBOOT | EWX_FORCEIFHUNG, 0xFFFF);
|
|
|
|
return;
|
|
|
|
Error:
|
|
TraceTag((tagError, "Failed to get security to reboot."));
|
|
return;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::AddProcess, public
|
|
//
|
|
// Synopsis: Adds a process to our process thread list. Any old ones
|
|
// hanging around are cleaned up in the meantime.
|
|
//
|
|
// Arguments: [pProc] -- New process object to add.
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CMTScript::AddProcess(CProcessThread *pProc)
|
|
{
|
|
LOCK_LOCALS(this);
|
|
|
|
CleanupOldProcesses();
|
|
|
|
return _aryProcesses.Append(pProc);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::FindProcess, public
|
|
//
|
|
// Synopsis: Returns a process object for the given process ID.
|
|
//
|
|
// Arguments: [dwProcID] -- Process ID to find
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
CProcessThread *
|
|
CMTScript::FindProcess(DWORD dwProcID)
|
|
{
|
|
CProcessThread **ppProc;
|
|
int cProcs;
|
|
|
|
LOCK_LOCALS(this);
|
|
|
|
CleanupOldProcesses();
|
|
|
|
for (ppProc = _aryProcesses, cProcs = _aryProcesses.Size();
|
|
cProcs;
|
|
ppProc++, cProcs--)
|
|
{
|
|
if ((*ppProc)->ProcId() == dwProcID)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (cProcs == 0)
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
return *ppProc;
|
|
}
|
|
|
|
BOOL CMTScript::SetScriptPath(const TCHAR *pszScriptPath, const TCHAR *pszInitScript)
|
|
{
|
|
LOCK_LOCALS(&_options);
|
|
|
|
// If there is any change then prompt the user, then force a restart.
|
|
//
|
|
// NOTE: The CStr "class" does not protect itself, so we must test it
|
|
// first before using it!
|
|
//
|
|
if ( (_options.cstrScriptPath == 0 || _tcscmp(pszScriptPath, _options.cstrScriptPath) != 0) ||
|
|
(_options.cstrInitScript == 0 || _tcscmp(pszInitScript, _options.cstrInitScript) != 0))
|
|
{
|
|
if (!_fRestarting)
|
|
{
|
|
UINT mb = MessageBox(NULL, L"This will require a restart. Continue?", L"Changing script path or starting script", MB_OKCANCEL | MB_ICONWARNING | MB_SETFOREGROUND);
|
|
if (mb == IDCANCEL)
|
|
return FALSE;
|
|
}
|
|
_options.cstrScriptPath.Set(pszScriptPath);
|
|
_options.cstrInitScript.Set(pszInitScript);
|
|
// Write it out to the registry.
|
|
UpdateOptionSettings(TRUE);
|
|
if (!_fRestarting)
|
|
PostToThread(this, MD_RESTART);
|
|
}
|
|
return TRUE;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::CleanupOldProcesses, public
|
|
//
|
|
// Synopsis: Walks the array of process objects and looks for ones that
|
|
// have been dead for more than a specified amount of time. For
|
|
// those that are, the objects are freed.
|
|
//
|
|
// Notes: Assumes that the caller has already locked the process array.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
const ULONG MAX_PROCESS_DEADTIME = 5 * 60 * 1000; // Cleanup after 5 minutes
|
|
|
|
void
|
|
CMTScript::CleanupOldProcesses()
|
|
{
|
|
int i;
|
|
|
|
//$ CONSIDER: Adding a max number of dead processes as well.
|
|
|
|
// We assume that the process array is already locked (via LOCK_LOCALS)!
|
|
|
|
// Iterate in reverse order since we'll be removing elements as we go.
|
|
|
|
for (i = _aryProcesses.Size() - 1; i >= 0; i--)
|
|
{
|
|
if (_aryProcesses[i]->GetDeadTime() > MAX_PROCESS_DEADTIME)
|
|
{
|
|
_aryProcesses.ReleaseAndDelete(i);
|
|
}
|
|
}
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::GetScriptNames, public
|
|
//
|
|
// Synopsis: Walks the array of scripts, copying the script names
|
|
// into the supplied buffer.
|
|
// Each name is null terminated. The list is double null terminated.
|
|
// If the buffer is not large enough, then *pcBuffer
|
|
// is set the the required size and FALSE is returned.
|
|
//
|
|
// Notes: Returns 0 if index is past end of array of scripts.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
CMTScript::GetScriptNames(TCHAR *pchBuffer, long *pcBuffer)
|
|
{
|
|
VERIFY_THREAD();
|
|
LOCK_LOCALS(this);
|
|
long nChars = 0;
|
|
int i;
|
|
TCHAR *pch = pchBuffer;
|
|
for(i = 0; i < _aryScripts.Size(); ++i)
|
|
{
|
|
TCHAR *ptr = _T("<invalid>");
|
|
CScriptSite *site = _aryScripts[i]->GetSite();
|
|
if (site)
|
|
{
|
|
ptr = _tcsrchr((LPTSTR)site->_cstrName, _T('\\'));
|
|
if (!ptr)
|
|
ptr = (LPTSTR)site->_cstrName;
|
|
}
|
|
int n = _tcslen(ptr) + 1;
|
|
nChars += n;
|
|
if ( nChars + 1 < *pcBuffer)
|
|
{
|
|
_tcscpy(pch, ptr);
|
|
pch += n;
|
|
}
|
|
*pch = 0; // double null terminator.
|
|
}
|
|
BOOL retval = nChars + 1 < *pcBuffer;
|
|
*pcBuffer = nChars + 1; // double null termination
|
|
return retval;
|
|
}
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::GetPrimaryScript, public
|
|
//
|
|
// Synopsis: Returns the first script in the array
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CScriptHost *CMTScript::GetPrimaryScript()
|
|
{
|
|
LOCK_LOCALS(this);
|
|
return _aryScripts[0];
|
|
}
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Member: CMTScript::GetProcess, public
|
|
//
|
|
// Synopsis: Walks the array of processes
|
|
//
|
|
// Notes: Returns 0 if index is past end of array of processes.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
CProcessThread *
|
|
CMTScript::GetProcess(int index)
|
|
{
|
|
VERIFY_THREAD();
|
|
|
|
LOCK_LOCALS(this);
|
|
|
|
if (index < 0 || index >= _aryProcesses.Size())
|
|
return 0;
|
|
|
|
return _aryProcesses[index];
|
|
}
|
|
|
|
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: MainWndProc
|
|
//
|
|
// Synopsis: Main window procedure for our hidden window. Used mainly to
|
|
// handle context menu events on our tray icon.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
LRESULT CALLBACK
|
|
MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
static CMTScript *s_pMT = NULL;
|
|
|
|
switch (msg)
|
|
{
|
|
case WM_CREATE:
|
|
{
|
|
CREATESTRUCT UNALIGNED *pcs = (CREATESTRUCT *)lParam;
|
|
|
|
s_pMT = (CMTScript *)pcs->lpCreateParams;
|
|
}
|
|
return 0;
|
|
|
|
case WM_USER:
|
|
switch (lParam)
|
|
{
|
|
case WM_LBUTTONDOWN:
|
|
case WM_RBUTTONDOWN:
|
|
case WM_CONTEXTMENU:
|
|
if (s_pMT)
|
|
{
|
|
s_pMT->ShowMenu(-1, -1);
|
|
}
|
|
return 0;
|
|
}
|
|
return 0;
|
|
|
|
case WM_COMMAND:
|
|
return 0;
|
|
break;
|
|
|
|
case WM_LBUTTONDOWN:
|
|
return 0;
|
|
break;
|
|
|
|
}
|
|
|
|
return DefWindowProc(hwnd, msg, wParam, lParam);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: get_StatusValue
|
|
//
|
|
// Synopsis: Return the value at [nIndex] in the StatusValues array
|
|
// Currently the implementation of this property has a small
|
|
// limit to the range of "nIndex".
|
|
// This allows us to avoid any dynamic memory allocation
|
|
// and also allows us to dispense with the usual thread locking.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
CMTScript::get_StatusValue(long nIndex, long *pnStatus)
|
|
{
|
|
if (!pnStatus)
|
|
return E_POINTER;
|
|
|
|
if (nIndex < 0 || nIndex >= ARRAY_SIZE(_rgnStatusValues))
|
|
return E_INVALIDARG;
|
|
|
|
*pnStatus = _rgnStatusValues[nIndex];
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: put_StatusValue
|
|
//
|
|
// Synopsis: Set the value at [nIndex] in the StatusValues array
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
CMTScript::put_StatusValue(long nIndex, long nStatus)
|
|
{
|
|
if (nIndex < 0 || nIndex >= ARRAY_SIZE(_rgnStatusValues))
|
|
return E_INVALIDARG;
|
|
|
|
_rgnStatusValues[nIndex] = nStatus;
|
|
return S_OK;
|
|
}
|
|
|