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

//
// tbscript.cpp
//
// This module contains the main data which handles the script interface
// for the user. All exported APIs are here.
//
// Copyright (C) 2001 Microsoft Corporation
//
// Author: a-devjen (Devin Jenson)
//
#define INITGUID
#define _WIN32_DCOM
#include <windows.h>
#include <stdio.h>
#include <activscp.h>
#include <olectl.h>
#include <stddef.h>
#include <crtdbg.h>
#include <comcat.h>
#include "CTBShell.h"
#include "CTBGlobal.h"
#include "CActiveScriptEngine.h"
#include "tbscript.h"
#include "scpapi.h"
#include "resource.h"
#define SCPMODULENAME OLESTR("tbscript.exe")
void SCPGetModuleFileName(void);
BSTR SCPReadFileAsBSTR(BSTR FileName);
void SCPFreeBSTR(BSTR Buffer);
void __cdecl IdleCallback(HANDLE Connection, LPCSTR Text, DWORD Seconds);
void DummyPrintMessage(MESSAGETYPE MessageType, LPCSTR Format, ...);
static BOOL DLLIsLoaded = FALSE;
static HMODULE DLLModule;
static OLECHAR DLLFileName[MAX_PATH];
// Pointers to callbacks, set using the library initialization function
PFNIDLECALLBACK g_IdleCallback = NULL;
PFNPRINTMESSAGE g_PrintMessage = NULL;
// Helper class to ease the nesting chaos that comes
// from the lack of exception support in the C++ language
// mapping for COM..
struct HRESULT_EXCEPTION
{
// Default constructor, does nothing
HRESULT_EXCEPTION() {}
// This constructor acts simply as an operator
// to test a value, and throws an exception
// if it is invalid.
HRESULT_EXCEPTION(HRESULT Result) {
if (FAILED(Result))
throw Result;
}
// This is the main attraction of this class.
// When ever we set an invalid HRESULT, we throw
// an exception.
HRESULT operator = (HRESULT Result) {
if (FAILED(Result))
throw Result;
return Result;
}
};
// DisplayEngines
//
// This "HANDLE" is fake for an internal class. It contains references
// to all objects for a given script instance. These mostly include
// the script interfaces.
SCPAPI void SCPDisplayEngines(void)
{
// get the component category manager for this machine
ICatInformation *pci = 0;
unsigned long LanguageCount = 0;
CoInitialize(NULL);
HRESULT Result = CoCreateInstance(CLSID_StdComponentCategoriesMgr,
NULL, CLSCTX_SERVER, IID_ICatInformation, (void **)&pci);
if (SUCCEEDED(Result)) {
// Get the list of parseable script engines
CATID rgcatidImpl[1];
rgcatidImpl[0] = CATID_ActiveScriptParse;
IEnumCLSID *pec = 0;
Result = pci->EnumClassesOfCategories(1, rgcatidImpl, 0, 0, &pec);
if (SUCCEEDED(Result))
{
// Print the list of CLSIDs to the console as ProgIDs
enum { CHUNKSIZE = 16 };
CLSID rgclsid[CHUNKSIZE];
ULONG cActual;
do {
Result = pec->Next(CHUNKSIZE, rgclsid, &cActual);
if (FAILED(Result))
break;
if (Result == S_OK)
cActual = CHUNKSIZE;
for (ULONG i = 0; i < cActual; i++) {
OLECHAR *pwszProgID = 0;
if (SUCCEEDED(ProgIDFromCLSID(rgclsid[i], &pwszProgID))) {
printf("%S\n", pwszProgID);
LanguageCount++;
CoTaskMemFree(pwszProgID);
}
}
} while (Result != S_FALSE);
pec->Release();
if (LanguageCount == 0)
printf("%s",
"ERROR: Windows Scripting Host not installed.\n");
else
printf("\n* Total Languages: %lu\n", LanguageCount);
}
else
printf("ERROR: Failed to retrieve the Class "
"Enumerator (0x%X).\n", Result);
pci->Release();
}
else
printf("ERROR: Failed to load the Category Manager (0x%X).\n", Result);
CoUninitialize();
}
// CActiveScriptHandle
//
// This "HANDLE" is fake for an internal class. It contains references
// to all objects for a given script instance. These mostly include
// the script interfaces.
class CActiveScriptHandle
{
public:
// Holds preferred data specified during handle instantiation.
TSClientData DesiredData;
// COM Class pointers...
CActiveScriptEngine *ActiveScriptEngine;
IActiveScriptParse *ActiveScriptParse;
IActiveScript *ActiveScript;
// Pointers to the two script instances known as the "TS" object, and
// the "Global" object for which you do not need to specify the name.
CTBGlobal *TBGlobal;
CTBShell *TBShell;
// The default user LCID is stored here...
LCID Lcid;
// CActiveScriptHandle::CActiveScriptHandle
//
// The constructor. The handle is now being created
// so use nullify needed pointers, and get other default data.
//
// No return value (called internally).
CActiveScriptHandle() {
// Zero data
ActiveScriptEngine = NULL;
ActiveScriptParse = NULL;
ActiveScript = NULL;
ZeroMemory(&DesiredData, sizeof(DesiredData));
// Ensure COM is initialized
CoInitialize(NULL);
// Allocate the global object
TBGlobal = new CTBGlobal;
if (TBGlobal == NULL) {
throw -1;
}
// Tell the new object we hold a reference of it
else {
TBGlobal->AddRef();
}
// Allocate the shell object
TBShell = new CTBShell;
if (TBShell == NULL) {
TBGlobal->Release();
TBGlobal = NULL;
throw -1;
}
// Tell the new object we hold a reference of it
else {
TBShell->AddRef();
}
// The global object uses the shell, it needs a reference as well.
TBGlobal->SetShellObjPtr(TBShell);
// Allocate a new engine for the script objects
ActiveScriptEngine = new CActiveScriptEngine(TBGlobal, TBShell);
if (ActiveScriptEngine == NULL) {
TBGlobal->Release();
TBGlobal = NULL;
TBShell->Release();
TBShell = NULL;
throw -1;
}
// Tell the script engine we hold a reference of it
ActiveScriptEngine->AddRef();
// Record the default user LCID
Lcid = GetUserDefaultLCID();
// And finally, record this script engine on the global object
// for recursive scripting...
// (The user can LoadScript() more scripts)
TBGlobal->SetScriptEngine((HANDLE)this);
}
// CActiveScriptHandle::~CActiveScriptHandle
//
// The destructor. The handle being closed, remove references.
//
// No return value (called internally).
~CActiveScriptHandle() {
// First off we need to release the main IDispatch of
// the IActiveScript interface.
if (ActiveScript != NULL) {
IDispatch *Dispatch = NULL;
// Query the to get the reference
HRESULT Result = ActiveScript->GetScriptDispatch(0, &Dispatch);
// And release it
if (SUCCEEDED(Result) && Dispatch != NULL)
Dispatch->Release();
ActiveScript = NULL;
}
// The main script engine first of all, to unbind it.
if (ActiveScriptEngine != NULL) {
ActiveScriptEngine->Release();
ActiveScriptEngine = NULL;
}
// Now release the parser
if (ActiveScriptParse != NULL) {
ActiveScriptParse->Release();
ActiveScriptParse = NULL;
}
// And the main IActiveScript interface itself.
if (ActiveScript != NULL) {
ActiveScript->Release();
ActiveScript = NULL;
}
// The global scripting object
if (TBGlobal != NULL) {
TBGlobal->Release();
TBGlobal = NULL;
}
// Finally, the shell or "TS" object.
if (TBShell != NULL) {
TBShell->Release();
TBShell = NULL;
}
}
};
// TODO: UPDATE THIS FUNCTION WHEN TBSCRIPT BECOMES
// OCX OR A COM COMPATIBLE HOST.
//
// SCPGetModuleFileName
//
// This routine gets the handle to the TBScript module.
// Additionally it also gets the full path where the
// module is located on disk. Due to the nature of the
// call, the variables are held globally, they are called:
// DLLFileName and DLLModule. The function only needs to
// be called once, but additional calls are safe and
// will be silently ignored.
void SCPGetModuleFileName(void)
{
// Check to see if we already have done this procedure
if (DLLIsLoaded == FALSE) {
// First get the handle
DLLModule = GetModuleHandleW(SCPMODULENAME);
// Now copy the file name
GetModuleFileNameW(DLLModule, DLLFileName, MAX_PATH);
// Indicate we have done this call already
DLLIsLoaded = TRUE;
}
}
// SCPLoadTypeInfoFromThisModule
//
// This loads the OLE code held in a resource of this very module.
//
// Returns an HRESULT value.
HRESULT SCPLoadTypeInfoFromThisModule(REFIID RefIID, ITypeInfo **TypeInfo)
{
HRESULT Result;
ITypeLib *TypeLibrary = NULL;
// Ensure we have the handle to the module first
SCPGetModuleFileName();
// Use the API now to load the entire TypeLib
Result = LoadTypeLib(DLLFileName, &TypeLibrary);
// We shouldn't fail, but be prepared...
_ASSERT(SUCCEEDED(Result));
// If we succeeded we have more to do
if (SUCCEEDED(Result)) {
// Nullify the pointer
*TypeInfo = NULL;
// In this TypeLib, grab the TypeInfo data
Result = TypeLibrary->GetTypeInfoOfGuid(RefIID, TypeInfo);
// We now have the TypeInfo, and we don't need the TypeLib
// anymore, so release it.
TypeLibrary->Release();
if (Result == E_OUTOFMEMORY)
Result = TYPE_E_ELEMENTNOTFOUND;
}
return Result;
}
// SCPReadFileAsBSTR
//
// Takes a script filename, reads it into COM allocated memory.
// Don't forget to call SCPFreeBSTR() when done!
//
// Returns a pointer to the allocated object is returned
// on success, or NULL on failure.
BSTR SCPReadFileAsBSTR(BSTR FileName)
{
BSTR Result = NULL;
// Open the file
HANDLE File = CreateFileW(FileName, GENERIC_READ, FILE_SHARE_READ,
0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
// Sanity check
if (File != INVALID_HANDLE_VALUE) {
// Get the file size
DWORD FileSize = GetFileSize(File, 0);
// Allocate a block on the local heap to read the file to
char *MemBlock = (char *)HeapAlloc(GetProcessHeap(),
HEAP_ZERO_MEMORY, FileSize + 1);
// This really shouldn't happen
_ASSERT(MemBlock != NULL);
// Sanity check again
if (MemBlock != NULL) {
// Read the file into memory
DWORD ReadCount;
if ( ReadFile(File, MemBlock, FileSize, &ReadCount, 0) ) {
// Allocate task memory block
Result = (BSTR)CoTaskMemAlloc(sizeof(OLECHAR) * (FileSize + 1));
// Copy from our old buffer to the new one
if (Result != NULL) {
// Convert to wide-character on the new buffer
mbstowcs(Result, MemBlock, FileSize + 1);
// Ensure string termination.
Result[FileSize] = 0;
}
}
// Free the temporary ASCII memory block
HeapFree(GetProcessHeap(), 0, MemBlock);
}
// Close the file
CloseHandle(File);
}
// Tell the user this failed in debug mode
_ASSERT(File != INVALID_HANDLE_VALUE);
return Result;
}
// SCPFreeBSTR
//
// This function is really a wrapper for releasing task memory blocks
// obtained through the function ReadFileAsBSTR
//
// No return value.
void SCPFreeBSTR(BSTR Buffer)
{
CoTaskMemFree(Buffer);
}
// SCPNewScriptEngine
//
// Allocates and initializes a new script engine.
//
// Returns a handle to the new engine, or NULL on failure.
HANDLE SCPNewScriptEngine(BSTR LangName,
TSClientData *DesiredData, LPARAM lParam)
{
CActiveScriptHandle *ActiveScriptHandle = NULL;
try {
HRESULT_EXCEPTION Result;
CLSID ClassID;
// Allocate a new handle
ActiveScriptHandle = new CActiveScriptHandle();
if (ActiveScriptHandle == NULL)
return NULL;
// Much of the initialization has already been done now.. but
// not enough, we have to manually set some stuff.
// Record the user desired data
ActiveScriptHandle->TBGlobal->SetPrintMessage(g_PrintMessage);
ActiveScriptHandle->TBShell->SetDesiredData(DesiredData);
ActiveScriptHandle->TBShell->SetParam(lParam);
// Get the class ID of the language
Result = CLSIDFromProgID(LangName, &ClassID);
// Create an instance of the script parser
Result = CoCreateInstance(ClassID, NULL, CLSCTX_ALL,
IID_IActiveScriptParse,
(void **)&(ActiveScriptHandle->ActiveScriptParse));
// Get the IActiveScript interface
Result = ActiveScriptHandle->ActiveScriptParse->
QueryInterface(IID_IActiveScript,
(void **)&(ActiveScriptHandle->ActiveScript));
// Set script state to INITIALIZED
Result = ActiveScriptHandle->ActiveScriptParse->InitNew();
// Bind our custom made "ActiveScriptSite" to the
// ActiveScript interface
Result = ActiveScriptHandle->ActiveScript->
SetScriptSite(ActiveScriptHandle->ActiveScriptEngine);
// Add the shell and global objects to engine's
// namespace and set state to STARTED
Result = ActiveScriptHandle->ActiveScript->
AddNamedItem(OLESTR("TS"),
SCRIPTITEM_ISVISIBLE | SCRIPTITEM_ISSOURCE);
Result = ActiveScriptHandle->ActiveScript->
AddNamedItem(OLESTR("Global"), SCRIPTITEM_ISVISIBLE |
SCRIPTITEM_ISSOURCE | SCRIPTITEM_GLOBALMEMBERS);
// And globally connect this new script engine
Result = ActiveScriptHandle->ActiveScript->
SetScriptState(SCRIPTSTATE_CONNECTED);
}
// Our handy HRESULT = operator will catch any errors here
catch (HRESULT Result) {
Result = 0;
// If the handle is still active, delete it
if(ActiveScriptHandle != NULL)
delete ActiveScriptHandle;
// Return error
return NULL;
}
// Return the handle
return (HANDLE)ActiveScriptHandle;
}
// SCPRunScript
//
// Takes a file, and runs it as a script. This will only
// return when the script has finished executing.
//
// Returns TRUE if the script completed successfully,
// FALSE otherwise.
SCPAPI BOOL SCPRunScript(BSTR LangName, BSTR FileName,
TSClientData *DesiredData, LPARAM lParam)
{
HANDLE EngineHandle;
// First read the file into memory. We allocate here instead of
// calling SCPParseScriptFile in one shot because if the allocation
// fails, there is no reason to create a script engine, which in
// this case it won't.
BSTR Code = SCPReadFileAsBSTR(FileName);
if (Code == NULL)
return FALSE;
// Next create the script control
EngineHandle = SCPNewScriptEngine(LangName, DesiredData, lParam);
if (EngineHandle == NULL) {
SCPFreeBSTR(Code);
return FALSE;
}
// Parse the script into the engine
if (SCPParseScript(EngineHandle, Code) == FALSE) {
SCPFreeBSTR(Code);
return FALSE;
}
// Success, free the script code
SCPFreeBSTR(Code);
// Close the script engine
SCPCloseScriptEngine(EngineHandle);
return TRUE;
}
// SCPParseScriptFile
//
// Takes a file, reads it into memory, and parses it into the script engine.
// This function only returns when the parsing has completed.
//
// Returns TRUE on success, or FALSE on failure.
BOOL SCPParseScriptFile(HANDLE EngineHandle, BSTR FileName)
{
// First read the file into memory
BSTR Code = SCPReadFileAsBSTR(FileName);
if(Code == NULL)
return FALSE;
// Next parse it
if(SCPParseScript(EngineHandle, Code) == FALSE) {
SCPFreeBSTR(Code);
return FALSE;
}
SCPFreeBSTR(Code);
return TRUE;
}
// SCPParseScript
//
// Reads a script in memory, and parses it into the script engine.
// This function only returns when the parsing has completed.
//
// Returns TRUE on success, or FALSE on failure.
BOOL SCPParseScript(HANDLE EngineHandle, BSTR Script)
{
// First cast the engine handle over to something we can use
CActiveScriptHandle *ActiveScriptHandle =
(CActiveScriptHandle *)EngineHandle;
HRESULT Result = E_FAIL;
// Make exception data
EXCEPINFO ExceptInfo = { 0 };
// Parse the script using the ActiveScript API
Result = ActiveScriptHandle->ActiveScriptParse->ParseScriptText(Script,
0, 0, 0, 0, 0,
SCRIPTTEXT_ISPERSISTENT | SCRIPTTEXT_ISVISIBLE,
0, &ExceptInfo);
return SUCCEEDED(Result);
}
// SCPCloseScriptEngine
//
// Closes a script handle simply by deleting it.
//
// No return value.
void SCPCloseScriptEngine(HANDLE EngineHandle)
{
// First cast the engine handle over to something we can use
CActiveScriptHandle *ActiveScriptHandle =
(CActiveScriptHandle *)EngineHandle;
// Release it from memory.. the deconstructor does all the work.
if (ActiveScriptHandle != NULL)
delete ActiveScriptHandle;
}
// SCPCleanupLibrary
//
// This should only be called when all script engines are unloaded
// and the module is going to be uninitialized.
//
// No return value.
SCPAPI void SCPCleanupLibrary(void)
{
CoUninitialize();
g_IdleCallback = NULL;
}
// SCPStartupLibrary
//
// Simply initializes the library, setting up the callback routine.
// This should be called before using any other script procedures.
//
// No return value.
SCPAPI void SCPStartupLibrary(SCINITDATA *InitData,
PFNIDLECALLBACK fnIdleCallback)
{
// Record our idle callback function
g_IdleCallback = fnIdleCallback;
if(InitData != NULL) {
__try {
// Record the print message function in the InitData structure
if(InitData != NULL)
g_PrintMessage = InitData->pfnPrintMessage;
}
__except (EXCEPTION_EXECUTE_HANDLER) {
// Bad pointer, simply initialize T2Client with our own
// callback then.
SCINITDATA LibInitData = { DummyPrintMessage };
T2Init(&LibInitData, IdleCallback);
return;
}
}
// Initialize with T2Client now.
T2Init(InitData, IdleCallback);
}
// IdleCallback
//
// This is an internal wrapping callback procedure used
// for redirecting idle messages.
//
// No return value.
void __cdecl IdleCallback(HANDLE Connection, LPCSTR Text, DWORD Seconds)
{
LPARAM lParam = 0;
// Get the parameter for the connection, and pass it back to the user
if (g_IdleCallback != NULL && T2GetParam(Connection, &lParam) == NULL)
g_IdleCallback(lParam, Text, Seconds);
}
// DummyPrintMessage
//
// Filler in case the user messes up.
//
// No return value.
void DummyPrintMessage(MESSAGETYPE MessageType, LPCSTR Format, ...)
{
return;
}