//____________________________________________________________________________ // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: ScriptHost.cpp // // Contents: CScriptHostMgr & CScriptHost implementation. // // History: 11/05/1999 AnandhaG Created //____________________________________________________________________________ // #include "stdafx.h" #include "scripthost.h" //+------------------------------------------------------------------- // MMCObjectName is the name scripts will use to refer to mmc object. // // Example: // // Dim doc // Dim snapins // Set doc = MMCApplication.Document // Set snapins = doc.snapins // snapins.Add "{58221c66-ea27-11cf-adcf-00aa00a80033}" ' the services snap-in // //+------------------------------------------------------------------- const LPOLESTR MMCObjectName = OLESTR("MMCApplication"); //+------------------------------------------------------------------- // // Member: CScriptHostMgr::ScInitScriptHostMgr // // Synopsis: Get the ITypeInfo of this instance of mmc. // // Arguments: [pDispatch] // // Returns: SC // //-------------------------------------------------------------------- CScriptHostMgr::CScriptHostMgr(LPDISPATCH pDispatch) { m_spMMCObjectDispatch = pDispatch; // It is ok if below fails. These interfaces (dispatch & typeinfo) are required // in CScriptHost::GetItemInfo method, so that this object can be given to engine. pDispatch->GetTypeInfo(1, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), &m_spMMCObjectTypeInfo); } CScriptHostMgr::~CScriptHostMgr() { DECLARE_SC(sc, _T("CScriptHostMgr::~CScriptHostMgr")); // The script manager is going away so ask all the script // hosts to finish their scripts & then destroy them. sc = ScDestroyScriptHosts(); } SC CScriptHostMgr::ScGetMMCObject(LPUNKNOWN *ppunkItem) { DECLARE_SC(sc, TEXT("CScriptHostMgr::ScGetMMCObject")); sc = ScCheckPointers(ppunkItem); if (sc) return sc; if (m_spMMCObjectDispatch) { *ppunkItem = m_spMMCObjectDispatch; return sc; } return (sc = E_FAIL); } SC CScriptHostMgr::ScGetMMCTypeInfo(LPTYPEINFO *ppTypeInfo) { DECLARE_SC(sc, TEXT("CScriptHostMgr::ScGetMMCObject")); sc = ScCheckPointers(ppTypeInfo); if (sc) return sc; if (m_spMMCObjectDispatch) { *ppTypeInfo = m_spMMCObjectTypeInfo; return sc; } return (sc = E_FAIL); } //+------------------------------------------------------------------- // // Member: ScGetScriptEngineFromExtn // // Synopsis: Using the file extension get the script engine & Clsid. // // Arguments: [strFileExtn] - Script extension. // [strScriptEngine] - Type script. // [rClsid] - CLSID of engine. // // Returns: SC // //-------------------------------------------------------------------- SC CScriptHostMgr::ScGetScriptEngineFromExtn(const tstring& strFileExtn, tstring& strScriptEngine, CLSID& rClsid) { DECLARE_SC(sc, _T("CScriptHostMgr::ScGetScriptEngine")); CRegKey regKey; // Open the extension. LONG lRet = regKey.Open(HKEY_CLASSES_ROOT, strFileExtn.data(), KEY_READ); if (ERROR_SUCCESS != lRet) { sc.FromWin32(lRet); return sc; } TCHAR szTemp[MAX_PATH]; DWORD dwLen = MAX_PATH; tstring strTemp; // Read the default value, the location of file association data. lRet = regKey.QueryValue(szTemp, NULL, &dwLen); if (ERROR_SUCCESS != lRet) { sc.FromWin32(lRet); return sc; } ASSERT(dwLen > 0); // Open the HKCR/FileAssocLoc/ScriptEngine. strTemp = szTemp; strTemp += _T("\\"); strTemp += SCRIPT_ENGINE_KEY; lRet = regKey.Open(HKEY_CLASSES_ROOT, strTemp.data(), KEY_READ); if (ERROR_SUCCESS != lRet) { sc.FromWin32(lRet); return sc; } // Now read the ScriptEngine default value. dwLen = MAX_PATH; lRet = regKey.QueryValue(szTemp, NULL, &dwLen); if (ERROR_SUCCESS != lRet) { sc.FromWin32(lRet); return sc; } ASSERT(dwLen > 0); strScriptEngine = szTemp; // Read HKCR/ScriptEngine/CLSID for ScriptEngine clsid. strTemp = strScriptEngine + _T("\\"); strTemp += CLSIDSTR; lRet = regKey.Open(HKEY_CLASSES_ROOT, strTemp.data(), KEY_READ); if (ERROR_SUCCESS != lRet) { sc.FromWin32(lRet); return sc; } // Read the CLSID value. dwLen = MAX_PATH; lRet = regKey.QueryValue(szTemp, NULL, &dwLen); if (ERROR_SUCCESS != lRet) { sc.FromWin32(lRet); return sc; } ASSERT(dwLen > 0); USES_CONVERSION; LPOLESTR lpClsid = T2OLE(szTemp); sc = CLSIDFromString(lpClsid, &rClsid); if (sc) return sc; return (sc); } //+------------------------------------------------------------------- // // Member: ScGetScriptEngine // // Synopsis: [strFileName] - Script file name. // [eScriptType] - Type script. // [rClsid] - CLSID of engine. // // Arguments: // // Returns: SC // //-------------------------------------------------------------------- SC CScriptHostMgr::ScGetScriptEngine(const tstring& strFileName, tstring& strScriptEngine, CLSID& rClsid) { DECLARE_SC(sc, _T("CScriptHostMgr::ScGetScriptEngine")); // Is this required, the file is already read. // It is a file & it exists. DWORD dwAttr = GetFileAttributes(strFileName.data()); if (-1 == dwAttr) { // What if lasterror is overwritten? sc.FromWin32(::GetLastError()); return sc; } // Get the extension (look for . from end). int iPos = strFileName.rfind(_T('.')); tstring strExtn; if (-1 != iPos) { strExtn = strFileName.substr(iPos, strFileName.length()); } else { sc = E_UNEXPECTED; return sc; } sc = ScGetScriptEngineFromExtn(strExtn, strScriptEngine, rClsid); if (sc) return sc; return (sc); } //+------------------------------------------------------------------- // // Member: ScLoadScriptFromFile // // Synopsis: Allocate memory & Load the script from the given file. // // Arguments: [strFileName] - File to be loaded. // [pszScriptContents] - Memory buffer containing the script // contents (See note). // // Note: The caller should call HeapFree() to free the pszScriptContents. // // Returns: SC // //-------------------------------------------------------------------- SC CScriptHostMgr::ScLoadScriptFromFile (const tstring& strFileName, LPOLESTR* pszScriptText) { DECLARE_SC(sc, _T("CScriptHostMgr::ScLoadScriptFromFile")); sc = ScCheckPointers(pszScriptText); if (sc) return sc; *pszScriptText = NULL; // Open the file. HANDLE hFile = ::CreateFile(strFileName.data(), GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL ); if (hFile == INVALID_HANDLE_VALUE) { sc.FromWin32(::GetLastError()); return sc; } HANDLE hFileMap = NULL; LPSTR pszMBCS = NULL; DWORD dwFileSize = ::GetFileSize(hFile, NULL); if (dwFileSize == 0xFFFFFFFF) { sc.FromWin32(::GetLastError()); goto FileError; } if (dwFileSize == 0) { sc = E_UNEXPECTED; goto FileError; } // Create a file mapping object. hFileMap = ::CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, dwFileSize, NULL ); if (hFileMap == NULL) { sc.FromWin32(::GetLastError()); goto FileError; } // Dummy block. { // Map the file into memory. pszMBCS = (LPSTR) ::MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 0 ); if (pszMBCS == NULL) { sc.FromWin32(::GetLastError()); goto FileMapError; } // Get the size of buffer needed. int n = ::MultiByteToWideChar(CP_ACP, 0, pszMBCS, dwFileSize, NULL, 0 ); // // Allocate script text buffer. +1 for EOS. // LPOLESTR pszText; pszText = (LPOLESTR) ::HeapAlloc(::GetProcessHeap(), 0, (n + 2) * sizeof(wchar_t) ); if (pszText == NULL) { sc.FromWin32(::GetLastError()); goto FileAllocError; } // Store file as WCHAR inthe buffer. ::MultiByteToWideChar(CP_ACP, 0, pszMBCS, dwFileSize, pszText, n ); // // Remove legacy EOF character. // if (pszText[n - 1] == 0x1A) { pszText[n - 1] = '\n'; } pszText[n] = '\n'; pszText[n + 1] = '\0'; *pszScriptText = pszText; } FileAllocError: ::UnmapViewOfFile(pszMBCS); FileMapError: ::CloseHandle(hFileMap); FileError: ::CloseHandle(hFile); //NoError: return sc; } //+------------------------------------------------------------------- // // Member: ScExecuteScript // // Synopsis: Execute given script file. // // Arguments: [strFileName] - The script file. // // Returns: SC // //-------------------------------------------------------------------- SC CScriptHostMgr::ScExecuteScript(const tstring& strFileName) { DECLARE_SC(sc, _T("CScriptHostMgr::ScExecuteScript")); CHeapAllocMemPtr spszFileContents; sc = ScLoadScriptFromFile(strFileName, &spszFileContents); if (sc) return sc; tstring strScriptEngine; CLSID EngineClsid; // Validate the file, get the script engine and script type. sc = ScGetScriptEngine(strFileName, strScriptEngine, EngineClsid); if (sc) return sc; sc = ScExecuteScriptHelper(spszFileContents, strScriptEngine, EngineClsid); return sc; } //+------------------------------------------------------------------- // // Member: ScExecuteScript // // Synopsis: Execute given script. // // Arguments: [pszScriptText] - The script itself. // [strExtn] - The script file extension. // // Note: The extension is used to determine the script // engine (as shell does). // // Returns: SC // //-------------------------------------------------------------------- SC CScriptHostMgr::ScExecuteScript(LPOLESTR pszScriptText, const tstring& strExtn) { DECLARE_SC(sc, _T("CScriptHostMgr::ScExecuteScript")); tstring strScriptEngine; CLSID EngineClsid; // Validate the file, get the script engine and script type. sc = ScGetScriptEngineFromExtn(strExtn, strScriptEngine, EngineClsid); if (sc) return sc; sc = ScExecuteScriptHelper(pszScriptText, strScriptEngine, EngineClsid); return sc; } //+------------------------------------------------------------------- // // Member: ScExecuteScriptHelper // // Synopsis: Helper for ScExecuteScript, Create the Script Host & // asks it to run the script. // // Arguments: [pszScriptText] - The script contents. // [strScriptEngine] - The script engine name. // [EngineClsid] // // Returns: SC // //-------------------------------------------------------------------- SC CScriptHostMgr::ScExecuteScriptHelper (LPCOLESTR pszScriptText, const tstring strScriptEngine, const CLSID& EngineClsid) { DECLARE_SC(sc, _T("ScExecuteScriptHelper")); // Create CScriptHost and ask it to run the script. CComObject* pScriptHost = NULL; sc = CComObject::CreateInstance(&pScriptHost); if (sc) return sc; if (NULL == pScriptHost) return (sc = E_FAIL); IUnknownPtr spUnknown = pScriptHost; if (NULL == spUnknown) return (sc = E_UNEXPECTED); m_ArrayOfHosts.push_back(spUnknown); sc = pScriptHost->ScRunScript(this, pszScriptText, strScriptEngine, EngineClsid); if (sc) return sc; return (sc); } //+------------------------------------------------------------------- // // Member: ScDestroyScriptHosts // // Synopsis: Stop all the running scripts and destroy all // script hosts. // // Arguments: // // Returns: SC // //-------------------------------------------------------------------- SC CScriptHostMgr::ScDestroyScriptHosts() { DECLARE_SC(sc, _T("CScriptHostMgr::ScStopAllScripts")); // Ask each script host created to stop its script. ArrayOfScriptHosts::iterator it = m_ArrayOfHosts.begin(); for (;it != m_ArrayOfHosts.end(); ++it) { CScriptHost* pScriptHost = dynamic_cast(it->GetInterfacePtr()); sc = ScCheckPointers(pScriptHost, E_UNEXPECTED); if (sc) return sc; sc = pScriptHost->ScStopScript(); } // This clear will call release on the IUnknown smart-pointers (that are in this array). m_ArrayOfHosts.clear(); return sc; } CScriptHost::CScriptHost() : m_pScriptHostMgr(NULL) { } CScriptHost::~CScriptHost() { } //+------------------------------------------------------------------- // // Member: ScRunScript // // Synopsis: Run the given script // // Arguments: [pMgr] - Object that manages all CScriptHosts. // [strScript] - The script itself. // [strEngineName] - Script engine name. // [rEngineClsid] - The script engine that runs this script. // // Returns: SC // //-------------------------------------------------------------------- SC CScriptHost::ScRunScript(CScriptHostMgr* pMgr, LPCOLESTR pszScriptText, const tstring& strEngineName, const CLSID& rEngineClsid) { DECLARE_SC(sc, _T("CScriptHost::ScRunScript")); m_pScriptHostMgr = pMgr; sc = ScCheckPointers(m_pScriptHostMgr, E_UNEXPECTED); if (sc) return sc; m_strScriptEngine = strEngineName; m_EngineClsid = rEngineClsid; // Now create the script engine. LPUNKNOWN* pUnknown = NULL; sc = CoCreateInstance(m_EngineClsid, NULL, CLSCTX_INPROC_SERVER, IID_IActiveScript, (void **)&m_spActiveScriptEngine); if (sc) return sc; m_spActiveScriptParser = m_spActiveScriptEngine; if (NULL == m_spActiveScriptParser) { m_spActiveScriptEngine = NULL; // Release the engine. return (sc = E_FAIL); } sc = m_spActiveScriptEngine->SetScriptSite(this); if (sc) return sc; sc = m_spActiveScriptParser->InitNew(); if (sc) return sc; // Add MMC objects to the top-level. sc = m_spActiveScriptEngine->AddNamedItem(MMCObjectName, SCRIPTITEM_ISSOURCE | SCRIPTITEM_GLOBALMEMBERS | SCRIPTITEM_ISVISIBLE); if (sc) { m_spActiveScriptEngine = NULL; m_spActiveScriptParser = NULL; return sc; } sc = m_spActiveScriptParser->ParseScriptText(pszScriptText, NULL, NULL, NULL, 0, 0, 0L, NULL, NULL); if (sc) { m_spActiveScriptEngine = NULL; m_spActiveScriptParser = NULL; return sc; } sc = m_spActiveScriptEngine->SetScriptState(SCRIPTSTATE_CONNECTED); if (sc) { m_spActiveScriptEngine = NULL; m_spActiveScriptParser = NULL; return sc; } return sc; } //+------------------------------------------------------------------- // // Member: ScStopScript // // Synopsis: Stop the script engine. // // Arguments: // // Returns: SC // //-------------------------------------------------------------------- SC CScriptHost::ScStopScript () { DECLARE_SC(sc, _T("CScriptHost::ScStopScript")); sc = ScCheckPointers(m_spActiveScriptEngine, E_UNEXPECTED); if (sc) return sc; sc = m_spActiveScriptEngine->SetScriptState(SCRIPTSTATE_DISCONNECTED); sc = m_spActiveScriptEngine->Close(); return (sc); } //+------------------------------------------------------------------- // // Member: GetLCID // // Synopsis: Return the Lang Id to Script engine. // // Arguments: [plcid] - Language Identifier. // // Returns: HRESULT // //-------------------------------------------------------------------- STDMETHODIMP CScriptHost::GetLCID( LCID *plcid) { DECLARE_SC(sc, _T("CScriptHost::GetLCID")); sc = ScCheckPointers(plcid); if (sc) return sc.ToHr(); *plcid = MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT); return sc.ToHr(); } //+------------------------------------------------------------------- // // Member: GetItemInfo // // Synopsis: Return IUnknown or ITypeInfo of the item added using // IActiveScript::AddNamedItem. Called by script engine. // // Arguments: [pstrName] - The item that was added. // [dwReturnMask] - Request (IUnknown or ITypeInfo). // [ppunkItem] - IUnknown returned if requested. // [ppTypeInfo] - ITypeInfo returned if requested. // // Returns: HRESULT // //-------------------------------------------------------------------- STDMETHODIMP CScriptHost::GetItemInfo( LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown **ppunkItem, ITypeInfo **ppTypeInfo) { DECLARE_SC(sc, _T("CScriptHost::GetItemInfo")); // The IUnknown** & ITypeInfo** can be NULL. if (ppunkItem) *ppunkItem = NULL; if (ppTypeInfo) *ppTypeInfo = NULL; // Make sure it is our object being requested. if (_wcsicmp(MMCObjectName, pstrName)) return (sc = TYPE_E_ELEMENTNOTFOUND).ToHr(); sc = ScCheckPointers(m_pScriptHostMgr, E_UNEXPECTED); if (sc) return sc.ToHr(); if (dwReturnMask & SCRIPTINFO_IUNKNOWN) { if (ppunkItem) { sc = m_pScriptHostMgr->ScGetMMCObject(ppunkItem); if (sc) return sc.ToHr(); (*ppunkItem)->AddRef(); } else return (sc = E_POINTER).ToHr(); } if (dwReturnMask & SCRIPTINFO_ITYPEINFO) { if (ppTypeInfo) { sc = m_pScriptHostMgr->ScGetMMCTypeInfo(ppTypeInfo); if (sc) return sc.ToHr(); (*ppTypeInfo)->AddRef(); } else return (sc = E_POINTER).ToHr(); } return sc.ToHr(); } //+------------------------------------------------------------------- // // Member: GetDocVersionString // // Synopsis: This retrieves a host-defined string that uniquely // identifies the current script (document) version from // the host's point of view. Called by script engine. // // Arguments: [pbstrVersionString] - The doc version string. // // Returns: HRESULT // //-------------------------------------------------------------------- STDMETHODIMP CScriptHost::GetDocVersionString( BSTR *pbstrVersionString) { DECLARE_SC(sc, _T("CScriptHost::GetDocVersionString")); return E_NOTIMPL; } //+------------------------------------------------------------------- // // Member: OnScriptTerminate // // Synopsis: Called by engine when the script has completed execution. // // Arguments: [pvarResult] - Script results. // [pexcepinfo] - Any exceptions generated. // // Returns: HRESULT // //-------------------------------------------------------------------- STDMETHODIMP CScriptHost::OnScriptTerminate( const VARIANT *pvarResult, const EXCEPINFO *pexcepinfo) { DECLARE_SC(sc, _T("CScriptHost::OnScriptTerminate")); return sc.ToHr(); } //+------------------------------------------------------------------- // // Member: OnStateChange // // Synopsis: Called by engine when its state changes. // // Arguments: [ssScriptState] - New state. // // Returns: HRESULT // //-------------------------------------------------------------------- STDMETHODIMP CScriptHost::OnStateChange(SCRIPTSTATE ssScriptState) { DECLARE_SC(sc, _T("CScriptHost::OnStateChange")); return sc.ToHr(); } //+------------------------------------------------------------------- // // Member: OnScriptError // // Synopsis: Engine informs that an execution error occurred // while it was running the script. // // Arguments: [pase ] - Host can obtain info about execution // error using this interface. // // Returns: HRESULT // //-------------------------------------------------------------------- STDMETHODIMP CScriptHost::OnScriptError(IActiveScriptError *pase) { DECLARE_SC(sc, _T("CScriptHost::OnScriptError")); sc = ScCheckPointers(pase); if (sc) return sc.ToHr(); // For test purposes. We need to provide much better debug info, // We will hookup the ScriptDebugger for this. BSTR bstrSourceLine; sc = pase->GetSourceLineText(&bstrSourceLine); EXCEPINFO exinfo; ZeroMemory(&exinfo, sizeof(exinfo)); sc = pase->GetExceptionInfo(&exinfo); DWORD dwSourceContext = 0; ULONG ulLineNumber = -1; LONG lCharPos = -1; sc = pase->GetSourcePosition(&dwSourceContext, &ulLineNumber, &lCharPos); return sc.ToHr(); } //+------------------------------------------------------------------- // // Member: OnEnterScript // // Synopsis: Engine informs that it has begun executing script. // // Arguments: // // Returns: HRESULT // //-------------------------------------------------------------------- STDMETHODIMP CScriptHost::OnEnterScript(void) { DECLARE_SC(sc, _T("CScriptHost::OnEnterScript")); return sc.ToHr(); } //+------------------------------------------------------------------- // // Member: OnEnterScript // // Synopsis: Engine informs that it has returned from executing script. // // Arguments: // // Returns: HRESULT // //-------------------------------------------------------------------- STDMETHODIMP CScriptHost::OnLeaveScript(void) { DECLARE_SC(sc, _T("CScriptHost::OnLeaveScript")); return sc.ToHr(); } //+------------------------------------------------------------------- // // Member: GetWindow // // Synopsis: Engine asks for window that can be parent of a popup // it can display. // // Arguments: [phwnd ] - Parent window. // // Returns: HRESULT // //-------------------------------------------------------------------- STDMETHODIMP CScriptHost::GetWindow(HWND *phwnd) { DECLARE_SC(sc, _T("CScriptHost::GetWindow")); return sc.ToHr(); } //+------------------------------------------------------------------- // // Member: EnableModeless // // Synopsis: Enables/Disables modelessness of parent window. // // Arguments: [fEnable ] - Enable/Disable. // // Returns: HRESULT // //-------------------------------------------------------------------- STDMETHODIMP CScriptHost::EnableModeless(BOOL fEnable) { DECLARE_SC(sc, _T("CScriptHost::EnableModeless")); return sc.ToHr(); }