Leaked source code of windows server 2003
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.

742 lines
20 KiB

  1. //
  2. // tbscript.cpp
  3. //
  4. // This module contains the main data which handles the script interface
  5. // for the user. All exported APIs are here.
  6. //
  7. // Copyright (C) 2001 Microsoft Corporation
  8. //
  9. // Author: a-devjen (Devin Jenson)
  10. //
  11. #define INITGUID
  12. #define _WIN32_DCOM
  13. #include <windows.h>
  14. #include <stdio.h>
  15. #include <activscp.h>
  16. #include <olectl.h>
  17. #include <stddef.h>
  18. #include <crtdbg.h>
  19. #include <comcat.h>
  20. #include "CTBShell.h"
  21. #include "CTBGlobal.h"
  22. #include "CActiveScriptEngine.h"
  23. #include "tbscript.h"
  24. #include "scpapi.h"
  25. #include "resource.h"
  26. #define SCPMODULENAME OLESTR("tbscript.exe")
  27. void SCPGetModuleFileName(void);
  28. BSTR SCPReadFileAsBSTR(BSTR FileName);
  29. void SCPFreeBSTR(BSTR Buffer);
  30. void __cdecl IdleCallback(HANDLE Connection, LPCSTR Text, DWORD Seconds);
  31. void DummyPrintMessage(MESSAGETYPE MessageType, LPCSTR Format, ...);
  32. static BOOL DLLIsLoaded = FALSE;
  33. static HMODULE DLLModule;
  34. static OLECHAR DLLFileName[MAX_PATH];
  35. // Pointers to callbacks, set using the library initialization function
  36. PFNIDLECALLBACK g_IdleCallback = NULL;
  37. PFNPRINTMESSAGE g_PrintMessage = NULL;
  38. // Helper class to ease the nesting chaos that comes
  39. // from the lack of exception support in the C++ language
  40. // mapping for COM..
  41. struct HRESULT_EXCEPTION
  42. {
  43. // Default constructor, does nothing
  44. HRESULT_EXCEPTION() {}
  45. // This constructor acts simply as an operator
  46. // to test a value, and throws an exception
  47. // if it is invalid.
  48. HRESULT_EXCEPTION(HRESULT Result) {
  49. if (FAILED(Result))
  50. throw Result;
  51. }
  52. // This is the main attraction of this class.
  53. // When ever we set an invalid HRESULT, we throw
  54. // an exception.
  55. HRESULT operator = (HRESULT Result) {
  56. if (FAILED(Result))
  57. throw Result;
  58. return Result;
  59. }
  60. };
  61. // DisplayEngines
  62. //
  63. // This "HANDLE" is fake for an internal class. It contains references
  64. // to all objects for a given script instance. These mostly include
  65. // the script interfaces.
  66. SCPAPI void SCPDisplayEngines(void)
  67. {
  68. // get the component category manager for this machine
  69. ICatInformation *pci = 0;
  70. unsigned long LanguageCount = 0;
  71. CoInitialize(NULL);
  72. HRESULT Result = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
  73. NULL, CLSCTX_SERVER, IID_ICatInformation, (void **)&pci);
  74. if (SUCCEEDED(Result)) {
  75. // Get the list of parseable script engines
  76. CATID rgcatidImpl[1];
  77. rgcatidImpl[0] = CATID_ActiveScriptParse;
  78. IEnumCLSID *pec = 0;
  79. Result = pci->EnumClassesOfCategories(1, rgcatidImpl, 0, 0, &pec);
  80. if (SUCCEEDED(Result))
  81. {
  82. // Print the list of CLSIDs to the console as ProgIDs
  83. enum { CHUNKSIZE = 16 };
  84. CLSID rgclsid[CHUNKSIZE];
  85. ULONG cActual;
  86. do {
  87. Result = pec->Next(CHUNKSIZE, rgclsid, &cActual);
  88. if (FAILED(Result))
  89. break;
  90. if (Result == S_OK)
  91. cActual = CHUNKSIZE;
  92. for (ULONG i = 0; i < cActual; i++) {
  93. OLECHAR *pwszProgID = 0;
  94. if (SUCCEEDED(ProgIDFromCLSID(rgclsid[i], &pwszProgID))) {
  95. printf("%S\n", pwszProgID);
  96. LanguageCount++;
  97. CoTaskMemFree(pwszProgID);
  98. }
  99. }
  100. } while (Result != S_FALSE);
  101. pec->Release();
  102. if (LanguageCount == 0)
  103. printf("%s",
  104. "ERROR: Windows Scripting Host not installed.\n");
  105. else
  106. printf("\n* Total Languages: %lu\n", LanguageCount);
  107. }
  108. else
  109. printf("ERROR: Failed to retrieve the Class "
  110. "Enumerator (0x%X).\n", Result);
  111. pci->Release();
  112. }
  113. else
  114. printf("ERROR: Failed to load the Category Manager (0x%X).\n", Result);
  115. CoUninitialize();
  116. }
  117. // CActiveScriptHandle
  118. //
  119. // This "HANDLE" is fake for an internal class. It contains references
  120. // to all objects for a given script instance. These mostly include
  121. // the script interfaces.
  122. class CActiveScriptHandle
  123. {
  124. public:
  125. // Holds preferred data specified during handle instantiation.
  126. TSClientData DesiredData;
  127. // COM Class pointers...
  128. CActiveScriptEngine *ActiveScriptEngine;
  129. IActiveScriptParse *ActiveScriptParse;
  130. IActiveScript *ActiveScript;
  131. // Pointers to the two script instances known as the "TS" object, and
  132. // the "Global" object for which you do not need to specify the name.
  133. CTBGlobal *TBGlobal;
  134. CTBShell *TBShell;
  135. // The default user LCID is stored here...
  136. LCID Lcid;
  137. // CActiveScriptHandle::CActiveScriptHandle
  138. //
  139. // The constructor. The handle is now being created
  140. // so use nullify needed pointers, and get other default data.
  141. //
  142. // No return value (called internally).
  143. CActiveScriptHandle() {
  144. // Zero data
  145. ActiveScriptEngine = NULL;
  146. ActiveScriptParse = NULL;
  147. ActiveScript = NULL;
  148. ZeroMemory(&DesiredData, sizeof(DesiredData));
  149. // Ensure COM is initialized
  150. CoInitialize(NULL);
  151. // Allocate the global object
  152. TBGlobal = new CTBGlobal;
  153. if (TBGlobal == NULL) {
  154. throw -1;
  155. }
  156. // Tell the new object we hold a reference of it
  157. else {
  158. TBGlobal->AddRef();
  159. }
  160. // Allocate the shell object
  161. TBShell = new CTBShell;
  162. if (TBShell == NULL) {
  163. TBGlobal->Release();
  164. TBGlobal = NULL;
  165. throw -1;
  166. }
  167. // Tell the new object we hold a reference of it
  168. else {
  169. TBShell->AddRef();
  170. }
  171. // The global object uses the shell, it needs a reference as well.
  172. TBGlobal->SetShellObjPtr(TBShell);
  173. // Allocate a new engine for the script objects
  174. ActiveScriptEngine = new CActiveScriptEngine(TBGlobal, TBShell);
  175. if (ActiveScriptEngine == NULL) {
  176. TBGlobal->Release();
  177. TBGlobal = NULL;
  178. TBShell->Release();
  179. TBShell = NULL;
  180. throw -1;
  181. }
  182. // Tell the script engine we hold a reference of it
  183. ActiveScriptEngine->AddRef();
  184. // Record the default user LCID
  185. Lcid = GetUserDefaultLCID();
  186. // And finally, record this script engine on the global object
  187. // for recursive scripting...
  188. // (The user can LoadScript() more scripts)
  189. TBGlobal->SetScriptEngine((HANDLE)this);
  190. }
  191. // CActiveScriptHandle::~CActiveScriptHandle
  192. //
  193. // The destructor. The handle being closed, remove references.
  194. //
  195. // No return value (called internally).
  196. ~CActiveScriptHandle() {
  197. // First off we need to release the main IDispatch of
  198. // the IActiveScript interface.
  199. if (ActiveScript != NULL) {
  200. IDispatch *Dispatch = NULL;
  201. // Query the to get the reference
  202. HRESULT Result = ActiveScript->GetScriptDispatch(0, &Dispatch);
  203. // And release it
  204. if (SUCCEEDED(Result) && Dispatch != NULL)
  205. Dispatch->Release();
  206. ActiveScript = NULL;
  207. }
  208. // The main script engine first of all, to unbind it.
  209. if (ActiveScriptEngine != NULL) {
  210. ActiveScriptEngine->Release();
  211. ActiveScriptEngine = NULL;
  212. }
  213. // Now release the parser
  214. if (ActiveScriptParse != NULL) {
  215. ActiveScriptParse->Release();
  216. ActiveScriptParse = NULL;
  217. }
  218. // And the main IActiveScript interface itself.
  219. if (ActiveScript != NULL) {
  220. ActiveScript->Release();
  221. ActiveScript = NULL;
  222. }
  223. // The global scripting object
  224. if (TBGlobal != NULL) {
  225. TBGlobal->Release();
  226. TBGlobal = NULL;
  227. }
  228. // Finally, the shell or "TS" object.
  229. if (TBShell != NULL) {
  230. TBShell->Release();
  231. TBShell = NULL;
  232. }
  233. }
  234. };
  235. // TODO: UPDATE THIS FUNCTION WHEN TBSCRIPT BECOMES
  236. // OCX OR A COM COMPATIBLE HOST.
  237. //
  238. // SCPGetModuleFileName
  239. //
  240. // This routine gets the handle to the TBScript module.
  241. // Additionally it also gets the full path where the
  242. // module is located on disk. Due to the nature of the
  243. // call, the variables are held globally, they are called:
  244. // DLLFileName and DLLModule. The function only needs to
  245. // be called once, but additional calls are safe and
  246. // will be silently ignored.
  247. void SCPGetModuleFileName(void)
  248. {
  249. // Check to see if we already have done this procedure
  250. if (DLLIsLoaded == FALSE) {
  251. // First get the handle
  252. DLLModule = GetModuleHandleW(SCPMODULENAME);
  253. // Now copy the file name
  254. GetModuleFileNameW(DLLModule, DLLFileName, MAX_PATH);
  255. // Indicate we have done this call already
  256. DLLIsLoaded = TRUE;
  257. }
  258. }
  259. // SCPLoadTypeInfoFromThisModule
  260. //
  261. // This loads the OLE code held in a resource of this very module.
  262. //
  263. // Returns an HRESULT value.
  264. HRESULT SCPLoadTypeInfoFromThisModule(REFIID RefIID, ITypeInfo **TypeInfo)
  265. {
  266. HRESULT Result;
  267. ITypeLib *TypeLibrary = NULL;
  268. // Ensure we have the handle to the module first
  269. SCPGetModuleFileName();
  270. // Use the API now to load the entire TypeLib
  271. Result = LoadTypeLib(DLLFileName, &TypeLibrary);
  272. // We shouldn't fail, but be prepared...
  273. _ASSERT(SUCCEEDED(Result));
  274. // If we succeeded we have more to do
  275. if (SUCCEEDED(Result)) {
  276. // Nullify the pointer
  277. *TypeInfo = NULL;
  278. // In this TypeLib, grab the TypeInfo data
  279. Result = TypeLibrary->GetTypeInfoOfGuid(RefIID, TypeInfo);
  280. // We now have the TypeInfo, and we don't need the TypeLib
  281. // anymore, so release it.
  282. TypeLibrary->Release();
  283. if (Result == E_OUTOFMEMORY)
  284. Result = TYPE_E_ELEMENTNOTFOUND;
  285. }
  286. return Result;
  287. }
  288. // SCPReadFileAsBSTR
  289. //
  290. // Takes a script filename, reads it into COM allocated memory.
  291. // Don't forget to call SCPFreeBSTR() when done!
  292. //
  293. // Returns a pointer to the allocated object is returned
  294. // on success, or NULL on failure.
  295. BSTR SCPReadFileAsBSTR(BSTR FileName)
  296. {
  297. BSTR Result = NULL;
  298. // Open the file
  299. HANDLE File = CreateFileW(FileName, GENERIC_READ, FILE_SHARE_READ,
  300. 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
  301. // Sanity check
  302. if (File != INVALID_HANDLE_VALUE) {
  303. // Get the file size
  304. DWORD FileSize = GetFileSize(File, 0);
  305. // Allocate a block on the local heap to read the file to
  306. char *MemBlock = (char *)HeapAlloc(GetProcessHeap(),
  307. HEAP_ZERO_MEMORY, FileSize + 1);
  308. // This really shouldn't happen
  309. _ASSERT(MemBlock != NULL);
  310. // Sanity check again
  311. if (MemBlock != NULL) {
  312. // Read the file into memory
  313. DWORD ReadCount;
  314. if ( ReadFile(File, MemBlock, FileSize, &ReadCount, 0) ) {
  315. // Allocate task memory block
  316. Result = (BSTR)CoTaskMemAlloc(sizeof(OLECHAR) * (FileSize + 1));
  317. // Copy from our old buffer to the new one
  318. if (Result != NULL) {
  319. // Convert to wide-character on the new buffer
  320. mbstowcs(Result, MemBlock, FileSize + 1);
  321. // Ensure string termination.
  322. Result[FileSize] = 0;
  323. }
  324. }
  325. // Free the temporary ASCII memory block
  326. HeapFree(GetProcessHeap(), 0, MemBlock);
  327. }
  328. // Close the file
  329. CloseHandle(File);
  330. }
  331. // Tell the user this failed in debug mode
  332. _ASSERT(File != INVALID_HANDLE_VALUE);
  333. return Result;
  334. }
  335. // SCPFreeBSTR
  336. //
  337. // This function is really a wrapper for releasing task memory blocks
  338. // obtained through the function ReadFileAsBSTR
  339. //
  340. // No return value.
  341. void SCPFreeBSTR(BSTR Buffer)
  342. {
  343. CoTaskMemFree(Buffer);
  344. }
  345. // SCPNewScriptEngine
  346. //
  347. // Allocates and initializes a new script engine.
  348. //
  349. // Returns a handle to the new engine, or NULL on failure.
  350. HANDLE SCPNewScriptEngine(BSTR LangName,
  351. TSClientData *DesiredData, LPARAM lParam)
  352. {
  353. CActiveScriptHandle *ActiveScriptHandle = NULL;
  354. try {
  355. HRESULT_EXCEPTION Result;
  356. CLSID ClassID;
  357. // Allocate a new handle
  358. ActiveScriptHandle = new CActiveScriptHandle();
  359. if (ActiveScriptHandle == NULL)
  360. return NULL;
  361. // Much of the initialization has already been done now.. but
  362. // not enough, we have to manually set some stuff.
  363. // Record the user desired data
  364. ActiveScriptHandle->TBGlobal->SetPrintMessage(g_PrintMessage);
  365. ActiveScriptHandle->TBShell->SetDesiredData(DesiredData);
  366. ActiveScriptHandle->TBShell->SetParam(lParam);
  367. // Get the class ID of the language
  368. Result = CLSIDFromProgID(LangName, &ClassID);
  369. // Create an instance of the script parser
  370. Result = CoCreateInstance(ClassID, NULL, CLSCTX_ALL,
  371. IID_IActiveScriptParse,
  372. (void **)&(ActiveScriptHandle->ActiveScriptParse));
  373. // Get the IActiveScript interface
  374. Result = ActiveScriptHandle->ActiveScriptParse->
  375. QueryInterface(IID_IActiveScript,
  376. (void **)&(ActiveScriptHandle->ActiveScript));
  377. // Set script state to INITIALIZED
  378. Result = ActiveScriptHandle->ActiveScriptParse->InitNew();
  379. // Bind our custom made "ActiveScriptSite" to the
  380. // ActiveScript interface
  381. Result = ActiveScriptHandle->ActiveScript->
  382. SetScriptSite(ActiveScriptHandle->ActiveScriptEngine);
  383. // Add the shell and global objects to engine's
  384. // namespace and set state to STARTED
  385. Result = ActiveScriptHandle->ActiveScript->
  386. AddNamedItem(OLESTR("TS"),
  387. SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE);
  388. Result = ActiveScriptHandle->ActiveScript->
  389. AddNamedItem(OLESTR("Global"), SCRIPTITEM_ISVISIBLE |
  390. SCRIPTITEM_ISSOURCE | SCRIPTITEM_GLOBALMEMBERS);
  391. // And globally connect this new script engine
  392. Result = ActiveScriptHandle->ActiveScript->
  393. SetScriptState(SCRIPTSTATE_CONNECTED);
  394. }
  395. // Our handy HRESULT = operator will catch any errors here
  396. catch (HRESULT Result) {
  397. Result = 0;
  398. // If the handle is still active, delete it
  399. if(ActiveScriptHandle != NULL)
  400. delete ActiveScriptHandle;
  401. // Return error
  402. return NULL;
  403. }
  404. // Return the handle
  405. return (HANDLE)ActiveScriptHandle;
  406. }
  407. // SCPRunScript
  408. //
  409. // Takes a file, and runs it as a script. This will only
  410. // return when the script has finished executing.
  411. //
  412. // Returns TRUE if the script completed successfully,
  413. // FALSE otherwise.
  414. SCPAPI BOOL SCPRunScript(BSTR LangName, BSTR FileName,
  415. TSClientData *DesiredData, LPARAM lParam)
  416. {
  417. HANDLE EngineHandle;
  418. // First read the file into memory. We allocate here instead of
  419. // calling SCPParseScriptFile in one shot because if the allocation
  420. // fails, there is no reason to create a script engine, which in
  421. // this case it won't.
  422. BSTR Code = SCPReadFileAsBSTR(FileName);
  423. if (Code == NULL)
  424. return FALSE;
  425. // Next create the script control
  426. EngineHandle = SCPNewScriptEngine(LangName, DesiredData, lParam);
  427. if (EngineHandle == NULL) {
  428. SCPFreeBSTR(Code);
  429. return FALSE;
  430. }
  431. // Parse the script into the engine
  432. if (SCPParseScript(EngineHandle, Code) == FALSE) {
  433. SCPFreeBSTR(Code);
  434. return FALSE;
  435. }
  436. // Success, free the script code
  437. SCPFreeBSTR(Code);
  438. // Close the script engine
  439. SCPCloseScriptEngine(EngineHandle);
  440. return TRUE;
  441. }
  442. // SCPParseScriptFile
  443. //
  444. // Takes a file, reads it into memory, and parses it into the script engine.
  445. // This function only returns when the parsing has completed.
  446. //
  447. // Returns TRUE on success, or FALSE on failure.
  448. BOOL SCPParseScriptFile(HANDLE EngineHandle, BSTR FileName)
  449. {
  450. // First read the file into memory
  451. BSTR Code = SCPReadFileAsBSTR(FileName);
  452. if(Code == NULL)
  453. return FALSE;
  454. // Next parse it
  455. if(SCPParseScript(EngineHandle, Code) == FALSE) {
  456. SCPFreeBSTR(Code);
  457. return FALSE;
  458. }
  459. SCPFreeBSTR(Code);
  460. return TRUE;
  461. }
  462. // SCPParseScript
  463. //
  464. // Reads a script in memory, and parses it into the script engine.
  465. // This function only returns when the parsing has completed.
  466. //
  467. // Returns TRUE on success, or FALSE on failure.
  468. BOOL SCPParseScript(HANDLE EngineHandle, BSTR Script)
  469. {
  470. // First cast the engine handle over to something we can use
  471. CActiveScriptHandle *ActiveScriptHandle =
  472. (CActiveScriptHandle *)EngineHandle;
  473. HRESULT Result = E_FAIL;
  474. // Make exception data
  475. EXCEPINFO ExceptInfo = { 0 };
  476. // Parse the script using the ActiveScript API
  477. Result = ActiveScriptHandle->ActiveScriptParse->ParseScriptText(Script,
  478. 0, 0, 0, 0, 0,
  479. SCRIPTTEXT_ISPERSISTENT | SCRIPTTEXT_ISVISIBLE,
  480. 0, &ExceptInfo);
  481. return SUCCEEDED(Result);
  482. }
  483. // SCPCloseScriptEngine
  484. //
  485. // Closes a script handle simply by deleting it.
  486. //
  487. // No return value.
  488. void SCPCloseScriptEngine(HANDLE EngineHandle)
  489. {
  490. // First cast the engine handle over to something we can use
  491. CActiveScriptHandle *ActiveScriptHandle =
  492. (CActiveScriptHandle *)EngineHandle;
  493. // Release it from memory.. the deconstructor does all the work.
  494. if (ActiveScriptHandle != NULL)
  495. delete ActiveScriptHandle;
  496. }
  497. // SCPCleanupLibrary
  498. //
  499. // This should only be called when all script engines are unloaded
  500. // and the module is going to be uninitialized.
  501. //
  502. // No return value.
  503. SCPAPI void SCPCleanupLibrary(void)
  504. {
  505. CoUninitialize();
  506. g_IdleCallback = NULL;
  507. }
  508. // SCPStartupLibrary
  509. //
  510. // Simply initializes the library, setting up the callback routine.
  511. // This should be called before using any other script procedures.
  512. //
  513. // No return value.
  514. SCPAPI void SCPStartupLibrary(SCINITDATA *InitData,
  515. PFNIDLECALLBACK fnIdleCallback)
  516. {
  517. // Record our idle callback function
  518. g_IdleCallback = fnIdleCallback;
  519. if(InitData != NULL) {
  520. __try {
  521. // Record the print message function in the InitData structure
  522. if(InitData != NULL)
  523. g_PrintMessage = InitData->pfnPrintMessage;
  524. }
  525. __except (EXCEPTION_EXECUTE_HANDLER) {
  526. // Bad pointer, simply initialize T2Client with our own
  527. // callback then.
  528. SCINITDATA LibInitData = { DummyPrintMessage };
  529. T2Init(&LibInitData, IdleCallback);
  530. return;
  531. }
  532. }
  533. // Initialize with T2Client now.
  534. T2Init(InitData, IdleCallback);
  535. }
  536. // IdleCallback
  537. //
  538. // This is an internal wrapping callback procedure used
  539. // for redirecting idle messages.
  540. //
  541. // No return value.
  542. void __cdecl IdleCallback(HANDLE Connection, LPCSTR Text, DWORD Seconds)
  543. {
  544. LPARAM lParam = 0;
  545. // Get the parameter for the connection, and pass it back to the user
  546. if (g_IdleCallback != NULL && T2GetParam(Connection, &lParam) == NULL)
  547. g_IdleCallback(lParam, Text, Seconds);
  548. }
  549. // DummyPrintMessage
  550. //
  551. // Filler in case the user messes up.
  552. //
  553. // No return value.
  554. void DummyPrintMessage(MESSAGETYPE MessageType, LPCSTR Format, ...)
  555. {
  556. return;
  557. }