Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1919 lines
48 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1998
  5. //
  6. // File: mtscript.cxx
  7. //
  8. // Contents: Implementation of the MTScript class
  9. //
  10. // Written by Lyle Corbin
  11. //
  12. //----------------------------------------------------------------------------
  13. #include "headers.hxx"
  14. #include <shellapi.h>
  15. #include <advpub.h> // for RegInstall
  16. #include "StatusDialog.h"
  17. #include "RegSettingsIO.h"
  18. HINSTANCE g_hInstDll;
  19. HINSTANCE g_hinstAdvPack = NULL;
  20. REGINSTALL g_pfnRegInstall = NULL;
  21. const TCHAR *g_szWindowName = _T("MTScript");
  22. LRESULT CALLBACK MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  23. //
  24. // Global variables
  25. //
  26. HINSTANCE g_hInstance = NULL;
  27. EXTERN_C HANDLE g_hProcessHeap = NULL;
  28. DWORD g_dwFALSE = 0;
  29. DeclareTagOther(tagDebugger, "MTScript", "Register with script debugger (MUST RESTART)");
  30. DeclareTagOther(tagIMSpy, "!Memory", "Register IMallocSpy (MUST RESTART)");
  31. //+---------------------------------------------------------------------------
  32. //
  33. // Function: ErrorPopup
  34. //
  35. // Synopsis: Displays a message to the user.
  36. //
  37. //----------------------------------------------------------------------------
  38. #define ERRPOPUP_BUFSIZE 300
  39. void
  40. ErrorPopup(LPWSTR szMessage)
  41. {
  42. WCHAR achBuf[ERRPOPUP_BUFSIZE];
  43. _snwprintf(achBuf, ERRPOPUP_BUFSIZE, L"%s: (%d)", szMessage, GetLastError());
  44. achBuf[ERRPOPUP_BUFSIZE - 1] = L'\0';
  45. MessageBox(NULL, achBuf, L"MTScript", MB_OK | MB_SETFOREGROUND);
  46. }
  47. int PrintfMessageBox(HWND hwnd, LPCTSTR lpText, LPCTSTR lpCaption, UINT uType, ...)
  48. {
  49. TCHAR chBuffer[256];
  50. va_list args;
  51. va_start(args, uType);
  52. _vsnwprintf(chBuffer, 256, lpText, args);
  53. va_end(args);
  54. return MessageBox(hwnd, chBuffer, lpCaption, uType);
  55. }
  56. //+---------------------------------------------------------------------------
  57. //
  58. // Function: LoadAdvPack
  59. //
  60. // Synopsis: Loads AdvPack.dll for DLL registration.
  61. //
  62. //----------------------------------------------------------------------------
  63. HRESULT
  64. LoadAdvPack()
  65. {
  66. HRESULT hr = S_OK;
  67. g_hinstAdvPack = LoadLibrary(_T("ADVPACK.DLL"));
  68. if (!g_hinstAdvPack)
  69. goto Error;
  70. g_pfnRegInstall = (REGINSTALL)GetProcAddress(g_hinstAdvPack, achREGINSTALL);
  71. if (!g_pfnRegInstall)
  72. goto Error;
  73. Cleanup:
  74. return hr;
  75. Error:
  76. hr = HRESULT_FROM_WIN32(GetLastError());
  77. if (g_hinstAdvPack)
  78. {
  79. FreeLibrary(g_hinstAdvPack);
  80. }
  81. goto Cleanup;
  82. }
  83. //+---------------------------------------------------------------------------
  84. //
  85. // Function: Register
  86. //
  87. // Synopsis: Register the various important information needed by this
  88. // executable.
  89. //
  90. // Notes: Uses AdvPack.dll and an INF file to do the registration
  91. //
  92. //----------------------------------------------------------------------------
  93. HRESULT
  94. Register()
  95. {
  96. HRESULT hr;
  97. OLECHAR strClsid[40];
  98. TCHAR keyName [256];
  99. STRTABLE stReg = { 0, NULL };
  100. if (!g_hinstAdvPack)
  101. {
  102. hr = LoadAdvPack();
  103. if (hr)
  104. goto Cleanup;
  105. }
  106. hr = g_pfnRegInstall(GetModuleHandle(NULL), "Register", &stReg);
  107. if (!hr)
  108. {
  109. DWORD dwRet;
  110. HKEY hKey;
  111. BOOL fSetACL = FALSE;
  112. // If the access key already exists, then don't modify it in case
  113. // someone changed it from the defaults.
  114. StringFromGUID2(CLSID_RemoteMTScript, strClsid, 40);
  115. wsprintf (keyName, TEXT("APPID\\%s"), strClsid);
  116. dwRet = RegOpenKeyEx (HKEY_CLASSES_ROOT,
  117. keyName,
  118. 0,
  119. KEY_ALL_ACCESS,
  120. &hKey);
  121. if (dwRet == ERROR_SUCCESS)
  122. {
  123. dwRet = RegQueryValueEx (hKey,
  124. TEXT("AccessPermission"),
  125. NULL,
  126. NULL,
  127. NULL,
  128. NULL);
  129. if (dwRet != ERROR_SUCCESS)
  130. {
  131. fSetACL = TRUE;
  132. }
  133. RegCloseKey (hKey);
  134. }
  135. else
  136. {
  137. fSetACL = TRUE;
  138. }
  139. if (fSetACL)
  140. {
  141. // Give everyone access rights
  142. hr = ChangeAppIDACL(CLSID_RemoteMTScript, _T("EVERYONE"), TRUE, TRUE, TRUE);
  143. if (!hr)
  144. {
  145. // Deny everyone launch rights
  146. hr = ChangeAppIDACL(CLSID_RemoteMTScript, _T("EVERYONE"), FALSE, TRUE, TRUE);
  147. }
  148. }
  149. }
  150. Cleanup:
  151. RegFlushKey(HKEY_CLASSES_ROOT);
  152. return hr;
  153. }
  154. //+------------------------------------------------------------------------
  155. //
  156. // Function: Unregister
  157. //
  158. // Synopsis: Undo the actions of Register.
  159. //
  160. //-------------------------------------------------------------------------
  161. HRESULT
  162. Unregister()
  163. {
  164. HRESULT hr;
  165. HKEY hKey;
  166. DWORD dwRet;
  167. OLECHAR strClsid[40];
  168. TCHAR keyName [256];
  169. STRTABLE stReg = { 0, NULL };
  170. if (!g_hinstAdvPack)
  171. {
  172. hr = LoadAdvPack();
  173. if (hr)
  174. goto Cleanup;
  175. }
  176. //
  177. // Remove the security keys that we created while registering
  178. //
  179. StringFromGUID2(CLSID_RemoteMTScript, strClsid, 40);
  180. wsprintf (keyName, TEXT("APPID\\%s"), strClsid);
  181. dwRet = RegOpenKeyEx (HKEY_CLASSES_ROOT,
  182. keyName,
  183. 0,
  184. KEY_ALL_ACCESS,
  185. &hKey);
  186. if (dwRet == ERROR_SUCCESS)
  187. {
  188. RegDeleteValue (hKey, TEXT("AccessPermission"));
  189. RegDeleteValue (hKey, TEXT("LaunchPermission"));
  190. RegCloseKey (hKey);
  191. }
  192. hr = g_pfnRegInstall(GetModuleHandle(NULL), "Unregister", &stReg);
  193. Cleanup:
  194. RegFlushKey(HKEY_CLASSES_ROOT);
  195. return hr;
  196. }
  197. //+------------------------------------------------------------------------
  198. //
  199. // Function: IAmTheOnlyMTScript, private
  200. //
  201. // Synopsis: Guarantees that only 1 MTScript gets to run
  202. //
  203. // Arguments:
  204. //
  205. // Returns: True is there is not already a running MTScript.
  206. //
  207. // Note: This function "leaks" a Mutex handle intentionally.
  208. // The system frees this handle on exit - so we know
  209. // for sure that we have finished all other cleanup
  210. // before it OK for another instance of MTScript to run.
  211. //
  212. //-------------------------------------------------------------------------
  213. static bool IAmTheOnlyMTScript()
  214. {
  215. HANDLE hMutex = CreateMutex(0, FALSE, g_szWindowName);
  216. if (!hMutex)
  217. {
  218. ErrorPopup(_T("Cannot create MTScript mutex!"));
  219. return false;
  220. }
  221. if( GetLastError() == ERROR_ALREADY_EXISTS)
  222. {
  223. ErrorPopup(_T("Cannot run more than one mtscript.exe!"));
  224. return false;
  225. }
  226. return true;
  227. }
  228. //+------------------------------------------------------------------------
  229. //
  230. // Function: WinMain, public
  231. //
  232. // Synopsis: Entry routine called by Windows upon startup.
  233. //
  234. // Arguments: [hInstance] -- handle to the program's instance
  235. // [hPrevInstance] -- Always NULL
  236. // [lpCmdLine] -- Command line arguments
  237. // [nCmdShow] -- Value to be passed to ShowWindow when the
  238. // main window is initialized.
  239. //
  240. // Returns: FALSE on error or the value passed from PostQuitMessage on exit
  241. //
  242. //-------------------------------------------------------------------------
  243. EXTERN_C int PASCAL
  244. WinMain(
  245. HINSTANCE hInstance,
  246. HINSTANCE hPrevInstance,
  247. LPSTR lpCmdLine,
  248. int nCmdShow)
  249. {
  250. OSVERSIONINFO ovi;
  251. CMTScript * pMT = NULL;
  252. int iRet = 0;
  253. #if DBG == 1
  254. IMallocSpy * pSpy = NULL;
  255. #endif
  256. #ifdef USE_STACK_SPEW
  257. InitChkStk(0xCCCCCCCC);
  258. #endif
  259. //
  260. // Initialize data structures.
  261. //
  262. g_hProcessHeap = GetProcessHeap();
  263. g_hInstance = hInstance;
  264. #if DBG == 1
  265. DbgExRestoreDefaultDebugState();
  266. #endif
  267. //
  268. // Get system information
  269. //
  270. ovi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
  271. GetVersionEx(&ovi);
  272. if (ovi.dwPlatformId != VER_PLATFORM_WIN32_NT)
  273. {
  274. // Win95 doesn't implement MessageBoxW
  275. MessageBoxA(NULL,
  276. "MTScript",
  277. "This program can only be run on Windows NT",
  278. MB_OK | MB_SETFOREGROUND);
  279. goto Error;
  280. }
  281. if (lpCmdLine && _stricmp(&lpCmdLine[1], "register") == 0)
  282. {
  283. HRESULT hr;
  284. hr = Register();
  285. if (FAILED(hr))
  286. goto Error;
  287. return 0;
  288. }
  289. else if (lpCmdLine && _stricmp(&lpCmdLine[1], "unregister") == 0)
  290. {
  291. HRESULT hr;
  292. hr = Unregister();
  293. if (FAILED(hr))
  294. goto Error;
  295. return 0;
  296. }
  297. if (!IAmTheOnlyMTScript() )
  298. {
  299. return 1;
  300. }
  301. #if DBG == 1
  302. if (DbgExIsTagEnabled(tagIMSpy))
  303. {
  304. pSpy = (IMallocSpy *)DbgExGetMallocSpy();
  305. if (pSpy)
  306. {
  307. CoRegisterMallocSpy(pSpy);
  308. }
  309. }
  310. #endif
  311. //
  312. // Can't put it on the stack because it depends on having zero'd memory
  313. //
  314. pMT = new CMTScript();
  315. if (!pMT)
  316. goto Error;
  317. if (!pMT->Init())
  318. goto Error;
  319. //
  320. // Start doing real stuff
  321. //
  322. iRet = pMT->ThreadMain();
  323. Cleanup:
  324. if (pMT)
  325. pMT->Release();
  326. #if DBG == 1
  327. if (pSpy)
  328. {
  329. // Note, due to the fact that we do not have control over DLL unload
  330. // ordering, the IMallocSpy implementation may report false leaks.
  331. // (lylec) The only way to fix this is to explicitely load all
  332. // dependent DLLs and unload them in their proper order (mshtmdbg.dll
  333. // last).
  334. CoRevokeMallocSpy();
  335. }
  336. #endif
  337. return iRet;
  338. Error:
  339. iRet = 1;
  340. goto Cleanup;
  341. }
  342. //+---------------------------------------------------------------------------
  343. //
  344. // CMTScript::OPTIONSETTINGS class
  345. //
  346. // Handles user-configurable options
  347. //
  348. //----------------------------------------------------------------------------
  349. CMTScript::OPTIONSETTINGS::OPTIONSETTINGS()
  350. : cstrScriptPath(CSTR_NOINIT),
  351. cstrInitScript(CSTR_NOINIT)
  352. {
  353. }
  354. //+---------------------------------------------------------------------------
  355. //
  356. // Member: CMTScript::OPTIONSETTINGS::GetModulePath, public
  357. //
  358. // Synopsis: Returns the path of this executable. Used for finding other
  359. // related files.
  360. //
  361. // Arguments: [pstr] -- Place to put path.
  362. //
  363. // Notes: Any existing string in pstr will be cleared.
  364. //
  365. //----------------------------------------------------------------------------
  366. void
  367. CMTScript::OPTIONSETTINGS::GetModulePath(CStr *pstr)
  368. {
  369. WCHAR *pch;
  370. WCHAR achBuf[MAX_PATH];
  371. pstr->Free();
  372. if (!GetModuleFileName(NULL, achBuf, sizeof(achBuf)))
  373. return;
  374. pch = wcsrchr(achBuf, L'\\');
  375. if (!pch)
  376. return;
  377. *pch = L'\0';
  378. pstr->Set(achBuf);
  379. }
  380. void
  381. CMTScript::OPTIONSETTINGS::GetScriptPath(CStr *cstrScript)
  382. {
  383. LOCK_LOCALS(this);
  384. if (cstrScriptPath.Length() == 0)
  385. {
  386. // TCHAR achBuf[MAX_PATH];
  387. // TCHAR *psz;
  388. GetModulePath(cstrScript);
  389. /*
  390. cstrScript->Append(L"\\..\\..\\scripts");
  391. GetFullPathName(*cstrScript, MAX_PATH, achBuf, &psz);
  392. cstrScript->Set(achBuf);
  393. */
  394. }
  395. else
  396. {
  397. cstrScript->Set(cstrScriptPath);
  398. }
  399. }
  400. void
  401. CMTScript::OPTIONSETTINGS::GetInitScript(CStr *cstr)
  402. {
  403. static WCHAR * pszInitScript = L"mtscript.js";
  404. LOCK_LOCALS(this);
  405. if (cstrInitScript.Length() == 0)
  406. {
  407. cstr->Set(pszInitScript);
  408. }
  409. else
  410. {
  411. cstr->Set(cstrInitScript);
  412. }
  413. }
  414. //+---------------------------------------------------------------------------
  415. //
  416. // CMTScript class
  417. //
  418. // Handles the main UI thread
  419. //
  420. //----------------------------------------------------------------------------
  421. CMTScript::CMTScript()
  422. {
  423. Assert(_fInDestructor == FALSE);
  424. Assert(_pGIT == NULL);
  425. Assert(_dwPublicDataCookie == 0);
  426. Assert(_dwPrivateDataCookie == 0);
  427. Assert(_dwPublicSerialNum == 0);
  428. Assert(_dwPrivateSerialNum == 0);
  429. VariantInit(&_vPublicData);
  430. VariantInit(&_vPrivateData);
  431. _ulRefs = 1;
  432. }
  433. //+---------------------------------------------------------------------------
  434. //
  435. // Member: CMTScript::~CMTScript, public
  436. //
  437. // Synopsis: destructor
  438. //
  439. // Notes: Anything done in the Init() call must be undone here. This
  440. // method must also be able to handle a partial initialization,
  441. // in case something inside Init() failed halfway through.
  442. //
  443. //----------------------------------------------------------------------------
  444. CMTScript::~CMTScript()
  445. {
  446. int i;
  447. VERIFY_THREAD();
  448. // Anything done in the Init() call must be undone here
  449. UnregisterClassObjects();
  450. _fInDestructor = TRUE;
  451. //
  452. // Process any pending messages such as PROCESSTHREADTERMINATE now.
  453. //
  454. HandleThreadMessage();
  455. // Cleanup any running processes.
  456. for (i = 0; i < _aryProcesses.Size(); i++)
  457. {
  458. Shutdown(_aryProcesses[i]);
  459. }
  460. _aryProcesses.ReleaseAll();
  461. //
  462. // This must be done in reverse order because the primary script (element 0)
  463. // must be shutdown last.
  464. //
  465. for (i = _aryScripts.Size() - 1; i >= 0; i--)
  466. {
  467. Shutdown(_aryScripts[i]);
  468. }
  469. _aryScripts.ReleaseAll();
  470. if (_pMachine)
  471. {
  472. Shutdown(_pMachine);
  473. ReleaseInterface(_pMachine);
  474. }
  475. if (_dwPublicDataCookie != 0)
  476. {
  477. _pGIT->RevokeInterfaceFromGlobal(_dwPublicDataCookie);
  478. }
  479. if (_dwPrivateDataCookie != 0)
  480. {
  481. _pGIT->RevokeInterfaceFromGlobal(_dwPrivateDataCookie);
  482. }
  483. ReleaseInterface(_pTIMachine);
  484. ReleaseInterface(_pTypeLibEXE);
  485. ReleaseInterface(_pGIT);
  486. ReleaseInterface(_pJScriptFactory);
  487. VariantClear(&_vPublicData);
  488. VariantClear(&_vPrivateData);
  489. DeInitScriptDebugger();
  490. CoUninitialize();
  491. CleanupUI();
  492. // This delete call must be done after we've destroyed our window, which
  493. // in turn will destroy the status window.
  494. delete _pStatusDialog;
  495. _pStatusDialog = NULL;
  496. }
  497. HRESULT
  498. CMTScript::QueryInterface(REFIID iid, void **ppvObj)
  499. {
  500. if (iid == IID_IUnknown)
  501. {
  502. *ppvObj = (IUnknown *)this;
  503. }
  504. else
  505. {
  506. *ppvObj = NULL;
  507. return E_NOINTERFACE;
  508. }
  509. ((IUnknown *)*ppvObj)->AddRef();
  510. return S_OK;
  511. }
  512. //+---------------------------------------------------------------------------
  513. //
  514. // Member: CMTScript::Init, public
  515. //
  516. // Synopsis: Initialization method that can fail.
  517. //
  518. //----------------------------------------------------------------------------
  519. BOOL
  520. CMTScript::Init()
  521. {
  522. HRESULT hr;
  523. _dwThreadId = GetCurrentThreadId();
  524. if (!CThreadComm::Init())
  525. return FALSE;
  526. //
  527. // Load our default configuration
  528. //
  529. UpdateOptionSettings(FALSE);
  530. if (FAILED(CoInitializeEx(NULL,
  531. COINIT_MULTITHREADED |
  532. COINIT_DISABLE_OLE1DDE |
  533. COINIT_SPEED_OVER_MEMORY)))
  534. {
  535. goto Error;
  536. }
  537. // This code may be needed if we want to do our own custom security.
  538. // However, it is easier to let DCOM do it for us.
  539. // The following code removes all security always. It cannot be overridden
  540. // using the registry.
  541. if (FAILED(CoInitializeSecurity(NULL,
  542. -1,
  543. NULL,
  544. NULL,
  545. RPC_C_AUTHN_LEVEL_NONE,
  546. RPC_C_IMP_LEVEL_IMPERSONATE,
  547. NULL,
  548. EOAC_NONE,
  549. NULL)))
  550. {
  551. goto Error;
  552. }
  553. hr = LoadTypeLibraries();
  554. if (hr)
  555. goto Error;
  556. InitScriptDebugger();
  557. if (FAILED(CoCreateInstance(CLSID_StdGlobalInterfaceTable,
  558. NULL,
  559. CLSCTX_INPROC_SERVER,
  560. IID_IGlobalInterfaceTable,
  561. (void **)&_pGIT)))
  562. {
  563. goto Error;
  564. }
  565. //
  566. // Create our hidden window and put an icon on the taskbar
  567. //
  568. if (!ConfigureUI())
  569. goto Error;
  570. //
  571. // Run the initial script
  572. //
  573. if (FAILED(RunScript(NULL, NULL)))
  574. goto Error;
  575. if (RegisterClassObjects(this) != S_OK)
  576. goto Error;
  577. #if DBG == 1
  578. OpenStatusDialog();
  579. #endif
  580. return TRUE;
  581. Error:
  582. return FALSE;
  583. }
  584. //+---------------------------------------------------------------------------
  585. //
  586. // Member: CMTScript::ThreadMain, public
  587. //
  588. // Synopsis: Main UI message loop.
  589. //
  590. // Arguments: [hWnd] -- Modeless Dialog HWND
  591. //
  592. //----------------------------------------------------------------------------
  593. DWORD
  594. CMTScript::ThreadMain()
  595. {
  596. DWORD dwRet;
  597. HANDLE ahEvents[3];
  598. int cEvents = 2;
  599. DWORD dwReturn = 0;
  600. VERIFY_THREAD();
  601. SetName("CMTScript");
  602. // Don't need to call ThreadStarted() because StartThread() was not used
  603. // to start the main thread!
  604. ahEvents[0] = _hCommEvent;
  605. ahEvents[1] = GetPrimaryScript()->hThread();
  606. while (TRUE)
  607. {
  608. MSG msg;
  609. //
  610. // Purge out all window messages.
  611. //
  612. while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
  613. {
  614. if (msg.message == WM_QUIT)
  615. {
  616. goto Cleanup;
  617. }
  618. if (_pStatusDialog)
  619. {
  620. if (_pStatusDialog->IsDialogMessage(&msg))
  621. continue;
  622. }
  623. TranslateMessage(&msg);
  624. DispatchMessage(&msg);
  625. }
  626. //
  627. // Wait for anything we need to deal with.
  628. //
  629. dwRet = MsgWaitForMultipleObjects(cEvents,
  630. ahEvents,
  631. FALSE,
  632. INFINITE,
  633. QS_ALLINPUT);
  634. if (dwRet == WAIT_OBJECT_0)
  635. {
  636. //
  637. // Another thread is sending us a message.
  638. //
  639. HandleThreadMessage();
  640. }
  641. else if (dwRet == WAIT_OBJECT_0 + 1)
  642. {
  643. // The Primary Script Thread terminated due to a problem loading
  644. // the initial script. Bring up the configuration dialog.
  645. {
  646. LOCK_LOCALS(this);
  647. _aryScripts.ReleaseAll();
  648. }
  649. if (!_fRestarting)
  650. {
  651. int iRet = MessageBox(_hwnd, _T("An error occurred loading the default script.\n\nDo you wish to edit the default configuration?"),
  652. _T("MTScript Error"),
  653. MB_YESNO | MB_SETFOREGROUND | MB_ICONERROR);
  654. if (iRet == IDNO)
  655. {
  656. goto Cleanup;
  657. }
  658. _fRestarting = TRUE; // Preventthe config dialog from doing a restart in this case.
  659. CConfig * pConfig = new CConfig(this);
  660. if (pConfig)
  661. {
  662. pConfig->StartThread(NULL);
  663. WaitForSingleObject(pConfig->hThread(), INFINITE);
  664. pConfig->Release();
  665. }
  666. _fRestarting = FALSE;
  667. }
  668. else
  669. {
  670. _fRestarting = FALSE;
  671. }
  672. //
  673. // Try re-loading the initial script
  674. //
  675. if (FAILED(RunScript(NULL, NULL)))
  676. goto Error;
  677. ahEvents[1] = GetPrimaryScript()->hThread();
  678. if (_pStatusDialog)
  679. _pStatusDialog->Restart();
  680. }
  681. else if (dwRet == WAIT_TIMEOUT)
  682. {
  683. // Make sure our message queue is empty first.
  684. HandleThreadMessage();
  685. // Right now we never fall in this loop.
  686. }
  687. else if (dwRet == WAIT_FAILED)
  688. {
  689. TraceTag((tagError, "MsgWaitForMultipleObjects failure (%d)", GetLastError()));
  690. AssertSz(FALSE, "MsgWaitForMultipleObjects failure");
  691. goto Cleanup;
  692. }
  693. }
  694. Cleanup:
  695. return dwReturn;
  696. Error:
  697. dwReturn = 1;
  698. goto Cleanup;
  699. }
  700. void
  701. CMTScript::InitScriptDebugger()
  702. {
  703. HRESULT hr;
  704. if (!IsTagEnabled(tagDebugger))
  705. {
  706. return;
  707. }
  708. hr = CoCreateInstance(CLSID_ProcessDebugManager,
  709. NULL,
  710. CLSCTX_INPROC_SERVER |
  711. CLSCTX_INPROC_HANDLER |
  712. CLSCTX_LOCAL_SERVER,
  713. IID_IProcessDebugManager,
  714. (LPVOID*)&_pPDM);
  715. if (hr)
  716. {
  717. TraceTag((tagError, "Could not create ProcessDebugManager: %x", hr));
  718. return;
  719. }
  720. hr = THR(_pPDM->CreateApplication(&_pDA));
  721. if (hr)
  722. goto Error;
  723. _pDA->SetName(L"MTScript");
  724. hr = THR(_pPDM->AddApplication(_pDA, &_dwAppCookie));
  725. if (hr)
  726. goto Error;
  727. return;
  728. Error:
  729. ClearInterface(&_pDA);
  730. ClearInterface(&_pPDM);
  731. return;
  732. }
  733. void
  734. CMTScript::DeInitScriptDebugger()
  735. {
  736. _try
  737. {
  738. if (_pPDM)
  739. {
  740. _pPDM->RemoveApplication(_dwAppCookie);
  741. _pDA->Close();
  742. ReleaseInterface(_pPDM);
  743. ReleaseInterface(_pDA);
  744. }
  745. }
  746. _except(EXCEPTION_EXECUTE_HANDLER)
  747. {
  748. //$ BUGBUG -- Figure out what's wrong here!
  749. // Ignore the crash caused by the Script Debugger
  750. }
  751. }
  752. //+---------------------------------------------------------------------------
  753. //
  754. // Member: CMTScript::HackCreateInstance, public
  755. //
  756. // Synopsis: Loads a private jscript.dll since the one that shipped with
  757. // Win2K is broken for what we need it for.
  758. //
  759. // Arguments: [clsid] -- Same parameters as CoCreateInstance.
  760. // [pUnk] --
  761. // [clsctx] --
  762. // [riid] --
  763. // [ppv] --
  764. //
  765. // Returns: HRESULT
  766. //
  767. //----------------------------------------------------------------------------
  768. HRESULT
  769. CMTScript::HackCreateInstance(REFCLSID clsid,
  770. IUnknown *pUnk,
  771. DWORD clsctx,
  772. REFIID riid,
  773. LPVOID *ppv)
  774. {
  775. TCHAR achDllPath[MAX_PATH];
  776. HINSTANCE hInstDll;
  777. DWORD iRet;
  778. TCHAR *pch;
  779. LPFNGETCLASSOBJECT pfnGCO = NULL;
  780. HRESULT hr;
  781. DWORD dwJunk;
  782. BYTE *pBuf = NULL;
  783. VS_FIXEDFILEINFO *pFI = NULL;
  784. UINT iLen;
  785. if (!_pJScriptFactory && _fHackVersionChecked)
  786. {
  787. return S_FALSE;
  788. }
  789. if (!_pJScriptFactory)
  790. {
  791. LOCK_LOCALS(this);
  792. // Make sure another thread didn't take care of this while we were
  793. // waiting for the lock.
  794. if (!_fHackVersionChecked)
  795. {
  796. // Remember we've done this check so we won't do it again.
  797. _fHackVersionChecked = TRUE;
  798. // First, check the version number on the system DLL
  799. iRet = GetSystemDirectory(achDllPath, MAX_PATH);
  800. if (iRet == 0)
  801. goto Win32Error;
  802. _tcscat(achDllPath, _T("\\jscript.dll"));
  803. iRet = GetFileVersionInfoSize(achDllPath, &dwJunk);
  804. if (iRet == 0)
  805. goto Win32Error;
  806. pBuf = new BYTE[iRet];
  807. iRet = GetFileVersionInfo(achDllPath, NULL, iRet, pBuf);
  808. if (iRet == 0)
  809. goto Win32Error;
  810. if (!VerQueryValue(pBuf, _T("\\"), (LPVOID*)&pFI, &iLen))
  811. goto Win32Error;
  812. //
  813. // Is the system DLL a version that has our needed fix?
  814. //
  815. // Version 5.1.0.4702 has the fix but isn't approved for Win2K.
  816. // The first official version that has the fix is 5.5.0.4703.
  817. //
  818. if ( (pFI->dwProductVersionMS == 0x00050001 && pFI->dwProductVersionLS >= 4702)
  819. || (pFI->dwProductVersionMS >= 0x00050005 && pFI->dwProductVersionLS >= 4703))
  820. {
  821. hr = S_FALSE;
  822. goto Cleanup;
  823. }
  824. iRet = GetModuleFileName(NULL, achDllPath, MAX_PATH);
  825. if (iRet == 0)
  826. goto Win32Error;
  827. pch = _tcsrchr(achDllPath, _T('\\'));
  828. if (pch)
  829. {
  830. *pch = _T('\0');
  831. }
  832. _tcscat(achDllPath, _T("\\jscript.dll"));
  833. hInstDll = CoLoadLibrary(achDllPath, TRUE);
  834. if (!hInstDll)
  835. {
  836. hr = HRESULT_FROM_WIN32(GetLastError());
  837. ErrorPopup(_T("Your copy of JSCRIPT.DLL contains a problem which may prevent you from using this tool.\n")
  838. _T("Please update that DLL to version 5.1.0.4702 or later.\n")
  839. _T("You may put the new DLL in the same directory as mtscript.exe to avoid upgrading the system DLL."));
  840. // ErrorPopup clears the GetLastError() status.
  841. goto Cleanup;
  842. }
  843. pfnGCO = (LPFNGETCLASSOBJECT)GetProcAddress(hInstDll, "DllGetClassObject");
  844. if (!pfnGCO)
  845. goto Win32Error;
  846. hr = (*pfnGCO)(clsid, IID_IClassFactory, (LPVOID*)&_pJScriptFactory);
  847. if (hr)
  848. goto Cleanup;
  849. }
  850. }
  851. if (_pJScriptFactory)
  852. hr = _pJScriptFactory->CreateInstance(pUnk, riid, ppv);
  853. else
  854. hr = S_FALSE;
  855. Cleanup:
  856. delete [] pBuf;
  857. return hr;
  858. Win32Error:
  859. hr = HRESULT_FROM_WIN32(GetLastError());
  860. goto Cleanup;
  861. }
  862. //+---------------------------------------------------------------------------
  863. //
  864. // Member: CMTScript::ConfigureUI, public
  865. //
  866. // Synopsis: Creates our hidden window and puts an icon on the taskbar
  867. //
  868. //----------------------------------------------------------------------------
  869. BOOL
  870. CMTScript::ConfigureUI()
  871. {
  872. VERIFY_THREAD();
  873. WNDCLASS wc = { 0 };
  874. NOTIFYICONDATA ni = { 0 };
  875. ATOM aWin;
  876. BOOL fRet;
  877. // The window will be a hidden window so we don't set many of the attributes
  878. wc.lpfnWndProc = MainWndProc;
  879. wc.hInstance = g_hInstance;
  880. wc.lpszClassName = SZ_WNDCLASS;
  881. aWin = RegisterClass(&wc);
  882. if (aWin == 0)
  883. {
  884. return FALSE;
  885. }
  886. _hwnd = CreateWindowEx(WS_EX_TOOLWINDOW,
  887. (LPTSTR)aWin,
  888. g_szWindowName,
  889. WS_OVERLAPPED,
  890. 10, // Coordinates don't matter - it will never
  891. 10, // be visible.
  892. 10,
  893. 10,
  894. NULL,
  895. NULL,
  896. g_hInstance,
  897. (LPVOID)this);
  898. if (_hwnd == NULL)
  899. {
  900. return FALSE;
  901. }
  902. ni.cbSize = sizeof(NOTIFYICONDATA);
  903. ni.hWnd = _hwnd;
  904. ni.uID = 1;
  905. ni.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
  906. ni.uCallbackMessage = WM_USER;
  907. ni.hIcon = LoadIcon(g_hInstance, L"myicon_small");
  908. wcscpy(ni.szTip, L"Remote Script Engine");
  909. fRet = Shell_NotifyIcon(NIM_ADD, &ni);
  910. DestroyIcon(ni.hIcon);
  911. return fRet;
  912. }
  913. //+---------------------------------------------------------------------------
  914. //
  915. // Member: CMTScript::CleanupUI, public
  916. //
  917. // Synopsis: Cleans up UI related things.
  918. //
  919. //----------------------------------------------------------------------------
  920. void
  921. CMTScript::CleanupUI()
  922. {
  923. VERIFY_THREAD();
  924. NOTIFYICONDATA ni = { 0 };
  925. if (_hwnd != NULL)
  926. {
  927. ni.cbSize = sizeof(NOTIFYICONDATA);
  928. ni.hWnd = _hwnd;
  929. ni.uID = 1;
  930. Shell_NotifyIcon(NIM_DELETE, &ni);
  931. DestroyWindow(_hwnd);
  932. }
  933. }
  934. HRESULT
  935. CMTScript::LoadTypeLibraries()
  936. {
  937. VERIFY_THREAD();
  938. HRESULT hr = S_OK;
  939. _pTIMachine = NULL;
  940. if (!_pTypeLibEXE)
  941. {
  942. hr = THR(LoadRegTypeLib(LIBID_MTScriptEngine, 1, 0, 0, &_pTypeLibEXE));
  943. if (hr)
  944. goto Cleanup;
  945. }
  946. if (!_pTIMachine)
  947. {
  948. TYPEATTR *pTypeAttr;
  949. UINT mb = IDYES;
  950. hr = THR(_pTypeLibEXE->GetTypeInfoOfGuid(IID_IConnectedMachine, &_pTIMachine));
  951. if (hr)
  952. goto Cleanup;
  953. hr = THR(_pTIMachine->GetTypeAttr(&pTypeAttr));
  954. if (hr)
  955. goto Cleanup;
  956. if (pTypeAttr->wMajorVerNum != IConnectedMachine_lVersionMajor || pTypeAttr->wMinorVerNum != IConnectedMachine_lVersionMinor)
  957. {
  958. mb = PrintfMessageBox(NULL,
  959. L"Mtscript.exe version (%d.%d) does not match mtlocal.dll (%d.%d).\n"
  960. L"You may experience undefined behavior if you continue.\n"
  961. L"Do you wish to ignore this error and continue?",
  962. L"Version mismatch error",
  963. MB_YESNO | MB_ICONWARNING | MB_SETFOREGROUND | MB_DEFBUTTON2,
  964. IConnectedMachine_lVersionMajor, IConnectedMachine_lVersionMinor,
  965. pTypeAttr->wMajorVerNum, pTypeAttr->wMinorVerNum);
  966. }
  967. _pTIMachine->ReleaseTypeAttr(pTypeAttr);
  968. if (mb != IDYES)
  969. return E_FAIL;
  970. }
  971. Cleanup:
  972. if (hr)
  973. {
  974. PrintfMessageBox(NULL,
  975. _T("FATAL: Could not load type library (%x).\nIs mtlocal.dll registered?"),
  976. _T("MTScript"),
  977. MB_OK | MB_ICONERROR | MB_SETFOREGROUND,
  978. hr);
  979. }
  980. return hr;
  981. }
  982. //+---------------------------------------------------------------------------
  983. //
  984. // Member: CMTScript::ShowMenu, public
  985. //
  986. // Synopsis: Displays a menu when the user right-clicks on the tray icon.
  987. //
  988. // Arguments: [x] -- x location
  989. // [y] -- y location
  990. //
  991. //----------------------------------------------------------------------------
  992. void
  993. CMTScript::ShowMenu(int x, int y)
  994. {
  995. VERIFY_THREAD();
  996. ULONG ulItem;
  997. HMENU hMenu = LoadMenu(g_hInstance, MAKEINTRESOURCE(IDR_MAINMENU));
  998. if (x == -1 && y == -1)
  999. {
  1000. POINT pt;
  1001. GetCursorPos(&pt);
  1002. x = pt.x;
  1003. y = pt.y;
  1004. }
  1005. SetForegroundWindow(_hwnd);
  1006. ulItem = TrackPopupMenuEx(GetSubMenu(hMenu, 0),
  1007. TPM_RETURNCMD |
  1008. TPM_NONOTIFY |
  1009. TPM_RIGHTBUTTON |
  1010. TPM_LEFTALIGN,
  1011. x, y,
  1012. _hwnd,
  1013. NULL);
  1014. switch (ulItem)
  1015. {
  1016. case IDM_EXIT:
  1017. UpdateOptionSettings(true);
  1018. PostQuitMessage(0);
  1019. break;
  1020. case IDM_CONFIGURE:
  1021. {
  1022. CConfig * pConfig = new CConfig(this);
  1023. if (pConfig)
  1024. {
  1025. pConfig->StartThread(NULL);
  1026. pConfig->Release();
  1027. }
  1028. }
  1029. break;
  1030. case IDM_RESTART:
  1031. Restart();
  1032. break;
  1033. case IDM_STATUS:
  1034. if (!_pStatusDialog)
  1035. OpenStatusDialog();
  1036. if (_pStatusDialog)
  1037. _pStatusDialog->Show();
  1038. break;
  1039. #if DBG == 1
  1040. case IDM_TRACETAG:
  1041. DbgExDoTracePointsDialog(FALSE);
  1042. break;
  1043. case IDM_MEMORYMON:
  1044. DbgExOpenMemoryMonitor();
  1045. break;
  1046. #endif
  1047. }
  1048. DestroyMenu(hMenu);
  1049. }
  1050. //+---------------------------------------------------------------------------
  1051. //
  1052. // Member: CMTScript::HandleThreadMessage, public
  1053. //
  1054. // Synopsis: Another thread has sent us a cross-thread message that we need
  1055. // to handle.
  1056. //
  1057. //----------------------------------------------------------------------------
  1058. void
  1059. CMTScript::HandleThreadMessage()
  1060. {
  1061. VERIFY_THREAD();
  1062. THREADMSG tm;
  1063. BYTE bData[MSGDATABUFSIZE];
  1064. DWORD cbData;
  1065. while (GetNextMsg(&tm, (void *)bData, &cbData))
  1066. {
  1067. switch (tm)
  1068. {
  1069. case MD_SECONDARYSCRIPTTERMINATE:
  1070. {
  1071. //
  1072. // A secondary script ended. Find it in our list, remove it,
  1073. // and delete it.
  1074. //
  1075. CScriptHost *pbs = *(CScriptHost**)bData;
  1076. LOCK_LOCALS(this);
  1077. Verify(_aryScripts.DeleteByValue(pbs));
  1078. pbs->Release();
  1079. }
  1080. break;
  1081. case MD_MACHINECONNECT:
  1082. PostToThread(GetPrimaryScript(), MD_MACHINECONNECT);
  1083. break;
  1084. case MD_SENDTOPROCESS:
  1085. {
  1086. MACHPROC_EVENT_DATA *pmed = *(MACHPROC_EVENT_DATA**)bData;
  1087. CProcessThread *pProc = FindProcess(pmed->dwProcId);
  1088. if (pProc && pProc->GetProcComm())
  1089. {
  1090. pProc->GetProcComm()->SendToProcess(pmed);
  1091. }
  1092. else
  1093. {
  1094. V_VT(pmed->pvReturn) = VT_I4;
  1095. V_I4(pmed->pvReturn) = -1;
  1096. SetEvent(pmed->hEvent);
  1097. }
  1098. }
  1099. break;
  1100. case MD_REBOOT:
  1101. Reboot();
  1102. break;
  1103. case MD_RESTART:
  1104. Restart();
  1105. break;
  1106. case MD_PLEASEEXIT:
  1107. PostQuitMessage(0);
  1108. break;
  1109. case MD_OUTPUTDEBUGSTRING:
  1110. if (_pStatusDialog)
  1111. {
  1112. _pStatusDialog->OUTPUTDEBUGSTRING( (LPWSTR) bData);
  1113. }
  1114. break;
  1115. default:
  1116. AssertSz(FALSE, "CMTScript got a message it couldn't handle!");
  1117. break;
  1118. }
  1119. }
  1120. }
  1121. //+---------------------------------------------------------------------------
  1122. //
  1123. // Member: CMTScript::RunScript, public
  1124. //
  1125. // Synopsis: Creates a scripting thread and runs a given script
  1126. // Can be called from any thread.
  1127. //
  1128. // Arguments: [pszPath] -- If NULL, we're starting the primary thread.
  1129. // Otherwise, it's the name of a file in the
  1130. // scripts directory.
  1131. //
  1132. //----------------------------------------------------------------------------
  1133. HRESULT
  1134. CMTScript::RunScript(LPWSTR pszPath, VARIANT *pvarParam)
  1135. {
  1136. HRESULT hr;
  1137. CScriptHost * pScript = NULL;
  1138. CStr cstrScript;
  1139. SCRIPT_PARAMS scrParams;
  1140. if (!pszPath)
  1141. _options.GetInitScript(&cstrScript);
  1142. else
  1143. cstrScript.Set(pszPath);
  1144. AssertSz(cstrScript.Length() > 0, "CRASH: Bogus script path");
  1145. pScript = new CScriptHost(this,
  1146. (pszPath) ? FALSE : TRUE,
  1147. FALSE);
  1148. if (!pScript)
  1149. {
  1150. hr = E_OUTOFMEMORY;
  1151. goto Error;
  1152. }
  1153. scrParams.pszPath = cstrScript;
  1154. scrParams.pvarParams = pvarParam;
  1155. // Race: We can successfully start a thread.
  1156. // That thread can run to completion, and exit.
  1157. // CScriptHost would then post MD_SECONDARYSCRIPTTERMINATE
  1158. // in an attempt to remove the script from the list and free
  1159. // it.
  1160. // Thus, we must add it to the list first, then remove it
  1161. // if the script fails to start.
  1162. {
  1163. LOCK_LOCALS(this);
  1164. _aryScripts.Append(pScript);
  1165. }
  1166. hr = pScript->StartThread(&scrParams);
  1167. if (FAILED(hr) && pszPath) // DO NOT REMOVE THE PRIMARY SCRIPT! Instead, return SUCCESS.
  1168. { // The main thread makes a special check for the primary script
  1169. LOCK_LOCALS(this);
  1170. Verify(_aryScripts.DeleteByValue(pScript));
  1171. pScript->Release();
  1172. goto Error;
  1173. }
  1174. Assert(pszPath || _aryScripts.Size() == 1);
  1175. return S_OK;
  1176. Error:
  1177. ReleaseInterface(pScript);
  1178. if (pszPath == 0)
  1179. ErrorPopup(L"An error occurred running the initial script");
  1180. return hr;
  1181. }
  1182. HRESULT
  1183. CMTScript::UpdateOptionSettings(BOOL fSave)
  1184. {
  1185. LOCK_LOCALS(&_options); // Makes this method thread safe
  1186. static REGKEYINFORMATION aKeyValuesOptions[] =
  1187. {
  1188. { _T("File Paths"), RKI_KEY, 0 },
  1189. { _T("Script Path"), RKI_EXPANDSZ, offsetof(OPTIONSETTINGS, cstrScriptPath) },
  1190. { _T("Initial Script"), RKI_STRING, offsetof(OPTIONSETTINGS, cstrInitScript) },
  1191. };
  1192. HRESULT hr;
  1193. hr = RegSettingsIO(g_szRegistry, fSave, aKeyValuesOptions, ARRAY_SIZE(aKeyValuesOptions), (BYTE *)&_options);
  1194. return hr;
  1195. }
  1196. //+---------------------------------------------------------------------------
  1197. //
  1198. // Member: CMTScript::Restart, public
  1199. //
  1200. // Synopsis: Restarts like we were just starting
  1201. //
  1202. // Notes: All currently running scripts are stopped and destroyed.
  1203. // Public information is freed and then everything is restarted.
  1204. //
  1205. //----------------------------------------------------------------------------
  1206. void
  1207. CMTScript::Restart()
  1208. {
  1209. int i;
  1210. // Make sure the status dialog doesn't try to do anything while we're
  1211. // restarting.
  1212. if (_pStatusDialog)
  1213. _pStatusDialog->Pause();
  1214. // Kill all running scripts and start over.
  1215. for (i = _aryScripts.Size() - 1; i >= 0; i--)
  1216. {
  1217. Shutdown(_aryScripts[i]);
  1218. // Scripts will be released when they notify us of their being
  1219. // shutdown.
  1220. }
  1221. // Kill all running processes
  1222. for (i = _aryProcesses.Size() - 1; i >= 0; i--)
  1223. {
  1224. Shutdown(_aryProcesses[i]);
  1225. }
  1226. _aryProcesses.ReleaseAll();
  1227. if (_dwPublicDataCookie != 0)
  1228. {
  1229. _pGIT->RevokeInterfaceFromGlobal(_dwPublicDataCookie);
  1230. _dwPublicDataCookie = 0;
  1231. }
  1232. if (_dwPrivateDataCookie != 0)
  1233. {
  1234. _pGIT->RevokeInterfaceFromGlobal(_dwPrivateDataCookie);
  1235. _dwPrivateDataCookie = 0;
  1236. }
  1237. VariantClear(&_vPublicData);
  1238. VariantClear(&_vPrivateData);
  1239. // Reset the statusvalues to 0
  1240. memset(_rgnStatusValues, 0, sizeof(_rgnStatusValues));
  1241. // The above call to shutdown will terminate the primary script thread,
  1242. // which will trigger the restart code in ThreadMain().
  1243. _fRestarting = TRUE;
  1244. }
  1245. //+---------------------------------------------------------------------------
  1246. //
  1247. // Member: CMTScript::OpenStatusDialog, public
  1248. //
  1249. // Synopsis: Opens the status modeless dialog
  1250. //
  1251. //----------------------------------------------------------------------------
  1252. void
  1253. CMTScript::OpenStatusDialog()
  1254. {
  1255. if (!_pStatusDialog)
  1256. _pStatusDialog = new CStatusDialog(_hwnd, this);
  1257. }
  1258. //+---------------------------------------------------------------------------
  1259. //
  1260. // Member: CMTScript::Reboot, public
  1261. //
  1262. // Synopsis: Reboots the local machine. The user must have appropriate
  1263. // rights to do so.
  1264. //
  1265. //----------------------------------------------------------------------------
  1266. void
  1267. CMTScript::Reboot()
  1268. {
  1269. TOKEN_PRIVILEGES tp;
  1270. LUID luid;
  1271. HANDLE hToken;
  1272. //
  1273. // Try to make sure we get shutdown last
  1274. //
  1275. SetProcessShutdownParameters(0x101, 0);
  1276. //
  1277. // Setup shutdown priviledges
  1278. //
  1279. if (!OpenProcessToken(GetCurrentProcess(),
  1280. TOKEN_ADJUST_PRIVILEGES,
  1281. &hToken))
  1282. {
  1283. goto Error;
  1284. }
  1285. if (!LookupPrivilegeValue(NULL,
  1286. SE_SHUTDOWN_NAME,
  1287. &luid))
  1288. {
  1289. goto Error;
  1290. }
  1291. tp.PrivilegeCount = 1;
  1292. tp.Privileges[0].Luid = luid;
  1293. tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
  1294. AdjustTokenPrivileges(hToken,
  1295. FALSE,
  1296. &tp,
  1297. sizeof(TOKEN_PRIVILEGES),
  1298. (PTOKEN_PRIVILEGES) NULL,
  1299. (PDWORD) NULL);
  1300. if (GetLastError() != ERROR_SUCCESS)
  1301. goto Error;
  1302. PostQuitMessage(0);
  1303. // BUGBUG -- This call is Windows2000 specific.
  1304. ExitWindowsEx(EWX_REBOOT | EWX_FORCEIFHUNG, 0xFFFF);
  1305. return;
  1306. Error:
  1307. TraceTag((tagError, "Failed to get security to reboot."));
  1308. return;
  1309. }
  1310. //+---------------------------------------------------------------------------
  1311. //
  1312. // Member: CMTScript::AddProcess, public
  1313. //
  1314. // Synopsis: Adds a process to our process thread list. Any old ones
  1315. // hanging around are cleaned up in the meantime.
  1316. //
  1317. // Arguments: [pProc] -- New process object to add.
  1318. //
  1319. // Returns: HRESULT
  1320. //
  1321. //----------------------------------------------------------------------------
  1322. HRESULT
  1323. CMTScript::AddProcess(CProcessThread *pProc)
  1324. {
  1325. LOCK_LOCALS(this);
  1326. CleanupOldProcesses();
  1327. return _aryProcesses.Append(pProc);
  1328. }
  1329. //+---------------------------------------------------------------------------
  1330. //
  1331. // Member: CMTScript::FindProcess, public
  1332. //
  1333. // Synopsis: Returns a process object for the given process ID.
  1334. //
  1335. // Arguments: [dwProcID] -- Process ID to find
  1336. //
  1337. //----------------------------------------------------------------------------
  1338. CProcessThread *
  1339. CMTScript::FindProcess(DWORD dwProcID)
  1340. {
  1341. CProcessThread **ppProc;
  1342. int cProcs;
  1343. LOCK_LOCALS(this);
  1344. CleanupOldProcesses();
  1345. for (ppProc = _aryProcesses, cProcs = _aryProcesses.Size();
  1346. cProcs;
  1347. ppProc++, cProcs--)
  1348. {
  1349. if ((*ppProc)->ProcId() == dwProcID)
  1350. {
  1351. break;
  1352. }
  1353. }
  1354. if (cProcs == 0)
  1355. {
  1356. return NULL;
  1357. }
  1358. return *ppProc;
  1359. }
  1360. BOOL CMTScript::SetScriptPath(const TCHAR *pszScriptPath, const TCHAR *pszInitScript)
  1361. {
  1362. LOCK_LOCALS(&_options);
  1363. // If there is any change then prompt the user, then force a restart.
  1364. //
  1365. // NOTE: The CStr "class" does not protect itself, so we must test it
  1366. // first before using it!
  1367. //
  1368. if ( (_options.cstrScriptPath == 0 || _tcscmp(pszScriptPath, _options.cstrScriptPath) != 0) ||
  1369. (_options.cstrInitScript == 0 || _tcscmp(pszInitScript, _options.cstrInitScript) != 0))
  1370. {
  1371. if (!_fRestarting)
  1372. {
  1373. UINT mb = MessageBox(NULL, L"This will require a restart. Continue?", L"Changing script path or starting script", MB_OKCANCEL | MB_ICONWARNING | MB_SETFOREGROUND);
  1374. if (mb == IDCANCEL)
  1375. return FALSE;
  1376. }
  1377. _options.cstrScriptPath.Set(pszScriptPath);
  1378. _options.cstrInitScript.Set(pszInitScript);
  1379. // Write it out to the registry.
  1380. UpdateOptionSettings(TRUE);
  1381. if (!_fRestarting)
  1382. PostToThread(this, MD_RESTART);
  1383. }
  1384. return TRUE;
  1385. }
  1386. //+---------------------------------------------------------------------------
  1387. //
  1388. // Member: CMTScript::CleanupOldProcesses, public
  1389. //
  1390. // Synopsis: Walks the array of process objects and looks for ones that
  1391. // have been dead for more than a specified amount of time. For
  1392. // those that are, the objects are freed.
  1393. //
  1394. // Notes: Assumes that the caller has already locked the process array.
  1395. //
  1396. //----------------------------------------------------------------------------
  1397. const ULONG MAX_PROCESS_DEADTIME = 5 * 60 * 1000; // Cleanup after 5 minutes
  1398. void
  1399. CMTScript::CleanupOldProcesses()
  1400. {
  1401. int i;
  1402. //$ CONSIDER: Adding a max number of dead processes as well.
  1403. // We assume that the process array is already locked (via LOCK_LOCALS)!
  1404. // Iterate in reverse order since we'll be removing elements as we go.
  1405. for (i = _aryProcesses.Size() - 1; i >= 0; i--)
  1406. {
  1407. if (_aryProcesses[i]->GetDeadTime() > MAX_PROCESS_DEADTIME)
  1408. {
  1409. _aryProcesses.ReleaseAndDelete(i);
  1410. }
  1411. }
  1412. }
  1413. //+---------------------------------------------------------------------------
  1414. //
  1415. // Member: CMTScript::GetScriptNames, public
  1416. //
  1417. // Synopsis: Walks the array of scripts, copying the script names
  1418. // into the supplied buffer.
  1419. // Each name is null terminated. The list is double null terminated.
  1420. // If the buffer is not large enough, then *pcBuffer
  1421. // is set the the required size and FALSE is returned.
  1422. //
  1423. // Notes: Returns 0 if index is past end of array of scripts.
  1424. //
  1425. //----------------------------------------------------------------------------
  1426. BOOL
  1427. CMTScript::GetScriptNames(TCHAR *pchBuffer, long *pcBuffer)
  1428. {
  1429. VERIFY_THREAD();
  1430. LOCK_LOCALS(this);
  1431. long nChars = 0;
  1432. int i;
  1433. TCHAR *pch = pchBuffer;
  1434. for(i = 0; i < _aryScripts.Size(); ++i)
  1435. {
  1436. TCHAR *ptr = _T("<invalid>");
  1437. CScriptSite *site = _aryScripts[i]->GetSite();
  1438. if (site)
  1439. {
  1440. ptr = _tcsrchr((LPTSTR)site->_cstrName, _T('\\'));
  1441. if (!ptr)
  1442. ptr = (LPTSTR)site->_cstrName;
  1443. }
  1444. int n = _tcslen(ptr) + 1;
  1445. nChars += n;
  1446. if ( nChars + 1 < *pcBuffer)
  1447. {
  1448. _tcscpy(pch, ptr);
  1449. pch += n;
  1450. }
  1451. *pch = 0; // double null terminator.
  1452. }
  1453. BOOL retval = nChars + 1 < *pcBuffer;
  1454. *pcBuffer = nChars + 1; // double null termination
  1455. return retval;
  1456. }
  1457. //+---------------------------------------------------------------------------
  1458. //
  1459. // Member: CMTScript::GetPrimaryScript, public
  1460. //
  1461. // Synopsis: Returns the first script in the array
  1462. //
  1463. //----------------------------------------------------------------------------
  1464. CScriptHost *CMTScript::GetPrimaryScript()
  1465. {
  1466. LOCK_LOCALS(this);
  1467. return _aryScripts[0];
  1468. }
  1469. //+---------------------------------------------------------------------------
  1470. //
  1471. // Member: CMTScript::GetProcess, public
  1472. //
  1473. // Synopsis: Walks the array of processes
  1474. //
  1475. // Notes: Returns 0 if index is past end of array of processes.
  1476. //
  1477. //----------------------------------------------------------------------------
  1478. CProcessThread *
  1479. CMTScript::GetProcess(int index)
  1480. {
  1481. VERIFY_THREAD();
  1482. LOCK_LOCALS(this);
  1483. if (index < 0 || index >= _aryProcesses.Size())
  1484. return 0;
  1485. return _aryProcesses[index];
  1486. }
  1487. //+---------------------------------------------------------------------------
  1488. //
  1489. // Function: MainWndProc
  1490. //
  1491. // Synopsis: Main window procedure for our hidden window. Used mainly to
  1492. // handle context menu events on our tray icon.
  1493. //
  1494. //----------------------------------------------------------------------------
  1495. LRESULT CALLBACK
  1496. MainWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  1497. {
  1498. static CMTScript *s_pMT = NULL;
  1499. switch (msg)
  1500. {
  1501. case WM_CREATE:
  1502. {
  1503. CREATESTRUCT UNALIGNED *pcs = (CREATESTRUCT *)lParam;
  1504. s_pMT = (CMTScript *)pcs->lpCreateParams;
  1505. }
  1506. return 0;
  1507. case WM_USER:
  1508. switch (lParam)
  1509. {
  1510. case WM_LBUTTONDOWN:
  1511. case WM_RBUTTONDOWN:
  1512. case WM_CONTEXTMENU:
  1513. if (s_pMT)
  1514. {
  1515. s_pMT->ShowMenu(-1, -1);
  1516. }
  1517. return 0;
  1518. }
  1519. return 0;
  1520. case WM_COMMAND:
  1521. return 0;
  1522. break;
  1523. case WM_LBUTTONDOWN:
  1524. return 0;
  1525. break;
  1526. }
  1527. return DefWindowProc(hwnd, msg, wParam, lParam);
  1528. }
  1529. //+---------------------------------------------------------------------------
  1530. //
  1531. // Function: get_StatusValue
  1532. //
  1533. // Synopsis: Return the value at [nIndex] in the StatusValues array
  1534. // Currently the implementation of this property has a small
  1535. // limit to the range of "nIndex".
  1536. // This allows us to avoid any dynamic memory allocation
  1537. // and also allows us to dispense with the usual thread locking.
  1538. //
  1539. //----------------------------------------------------------------------------
  1540. HRESULT
  1541. CMTScript::get_StatusValue(long nIndex, long *pnStatus)
  1542. {
  1543. if (!pnStatus)
  1544. return E_POINTER;
  1545. if (nIndex < 0 || nIndex >= ARRAY_SIZE(_rgnStatusValues))
  1546. return E_INVALIDARG;
  1547. *pnStatus = _rgnStatusValues[nIndex];
  1548. return S_OK;
  1549. }
  1550. //+---------------------------------------------------------------------------
  1551. //
  1552. // Function: put_StatusValue
  1553. //
  1554. // Synopsis: Set the value at [nIndex] in the StatusValues array
  1555. //
  1556. //----------------------------------------------------------------------------
  1557. HRESULT
  1558. CMTScript::put_StatusValue(long nIndex, long nStatus)
  1559. {
  1560. if (nIndex < 0 || nIndex >= ARRAY_SIZE(_rgnStatusValues))
  1561. return E_INVALIDARG;
  1562. _rgnStatusValues[nIndex] = nStatus;
  1563. return S_OK;
  1564. }