From eee455c24a4cd54a112c53c420d0d12e5723afeb Mon Sep 17 00:00:00 2001 From: Misha Koshelev Date: Mon, 30 Apr 2007 20:54:43 -0500 Subject: [PATCH] msi: Add JScript/VBScript support. --- dlls/msi/Makefile.in | 1 + dlls/msi/automation.c | 6 + dlls/msi/custom.c | 12 +- dlls/msi/msipriv.h | 5 + dlls/msi/script.c | 364 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 387 insertions(+), 1 deletion(-) create mode 100644 dlls/msi/script.c diff --git a/dlls/msi/Makefile.in b/dlls/msi/Makefile.in index 696273f201d..e81dd4cc62d 100644 --- a/dlls/msi/Makefile.in +++ b/dlls/msi/Makefile.in @@ -38,6 +38,7 @@ C_SRCS = \ record.c \ registry.c \ regsvr.c \ + script.c \ select.c \ source.c \ streams.c \ diff --git a/dlls/msi/automation.c b/dlls/msi/automation.c index 4afef02b9f3..65639a05bdf 100644 --- a/dlls/msi/automation.c +++ b/dlls/msi/automation.c @@ -920,3 +920,9 @@ HRESULT create_msiserver(IUnknown *pOuter, LPVOID *ppObj) { return create_automation_object(0, pOuter, ppObj, &DIID_Installer, InstallerImpl_Invoke); } + +/* Wrapper around create_automation_object to create a session object. */ +HRESULT create_session(MSIHANDLE msiHandle, IDispatch **pDispatch) +{ + return create_automation_object(msiHandle, NULL, (LPVOID)pDispatch, &DIID_Session, SessionImpl_Invoke); +} diff --git a/dlls/msi/custom.c b/dlls/msi/custom.c index 4467bb4277e..c8d889e5349 100644 --- a/dlls/msi/custom.c +++ b/dlls/msi/custom.c @@ -981,6 +981,7 @@ static UINT HANDLE_CustomType34(MSIPACKAGE *package, LPCWSTR source, static DWORD WINAPI ACTION_CallScript( const LPGUID guid ) { msi_custom_action_info *info; + MSIHANDLE hPackage; UINT r = ERROR_FUNCTION_FAILED; info = find_action_by_guid( guid ); @@ -990,7 +991,16 @@ static DWORD WINAPI ACTION_CallScript( const LPGUID guid ) return r; } - FIXME("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) ); + TRACE("function %s, script %s\n", debugstr_w( info->target ), debugstr_w( info->source ) ); + + hPackage = alloc_msihandle( &info->package->hdr ); + if (hPackage) + { + r = call_script( hPackage, info->type, info->source, info->target, info->action ); + MsiCloseHandle( hPackage ); + } + else + ERR("failed to create handle for %p\n", info->package ); if (info->type & msidbCustomActionTypeAsync && info->type & msidbCustomActionTypeContinue) diff --git a/dlls/msi/msipriv.h b/dlls/msi/msipriv.h index dc9f1d39d41..de08aeb0dc8 100644 --- a/dlls/msi/msipriv.h +++ b/dlls/msi/msipriv.h @@ -793,6 +793,11 @@ extern VOID ControlEvent_UnSubscribeToEvent( MSIPACKAGE *package, LPCWSTR event, /* OLE automation */ extern HRESULT create_msiserver(IUnknown *pOuter, LPVOID *ppObj); +extern HRESULT create_session(MSIHANDLE msiHandle, IDispatch **pDispatch); +extern HRESULT load_type_info(IDispatch *iface, ITypeInfo **pptinfo, REFIID clsid, LCID lcid); + +/* Scripting */ +extern DWORD call_script(MSIHANDLE hPackage, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action); /* User Interface messages from the actions */ extern void ui_progress(MSIPACKAGE *, int, int, int, int); diff --git a/dlls/msi/script.c b/dlls/msi/script.c new file mode 100644 index 00000000000..f2fc6426dfc --- /dev/null +++ b/dlls/msi/script.c @@ -0,0 +1,364 @@ +/* + * Implementation of scripting for Microsoft Installer (msi.dll) + * + * Copyright 2007 Misha Koshelev + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +#define COBJMACROS + +#include +#include "windef.h" +#include "winbase.h" +#include "winerror.h" +#include "winuser.h" +#include "msidefs.h" +#include "msipriv.h" +#include "activscp.h" +#include "oleauto.h" +#include "wine/debug.h" +#include "wine/unicode.h" + +#include "msiserver.h" + +WINE_DEFAULT_DEBUG_CHANNEL(msi); + +static const WCHAR szJScript[] = { 'J','S','c','r','i','p','t',0}; +static const WCHAR szVBScript[] = { 'V','B','S','c','r','i','p','t',0}; +static const WCHAR szSession[] = {'S','e','s','s','i','o','n',0}; + +/* + * MsiActiveScriptSite - Our IActiveScriptSite implementation. + */ + +typedef struct { + IActiveScriptSite lpVtbl; + IDispatch *pSession; + LONG ref; +} MsiActiveScriptSite; + +static const struct IActiveScriptSiteVtbl ASS_Vtbl; + +static HRESULT create_ActiveScriptSite(IUnknown *pUnkOuter, LPVOID *ppObj) +{ + MsiActiveScriptSite* object; + + TRACE("(%p,%p)\n", pUnkOuter, ppObj); + + if( pUnkOuter ) + return CLASS_E_NOAGGREGATION; + + object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(MsiActiveScriptSite)); + + object->lpVtbl.lpVtbl = &ASS_Vtbl; + object->ref = 1; + object->pSession = NULL; + + *ppObj = object; + + return S_OK; +} + +/* + * Call a script. + */ +DWORD call_script(MSIHANDLE hPackage, INT type, LPCWSTR script, LPCWSTR function, LPCWSTR action) +{ + HRESULT hr; + IActiveScript *pActiveScript = NULL; + IActiveScriptParse *pActiveScriptParse = NULL; + MsiActiveScriptSite *pActiveScriptSite = NULL; + IDispatch *pDispatch = NULL; + DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0}; + DISPID dispid; + CLSID clsid; + VARIANT var; + + /* Return success by default (if Windows Script not installed) - not native behavior. This + * should be here until we implement wine scripting. */ + DWORD ret = ERROR_SUCCESS; + + CoInitialize(NULL); + + /* Create MsiActiveScriptSite object */ + hr = create_ActiveScriptSite(NULL, (void **)&pActiveScriptSite); + if (hr != S_OK) goto done; + + /* Create a session object */ + hr = create_session(hPackage, &pActiveScriptSite->pSession); + if (hr != S_OK) goto done; + IUnknown_AddRef((IUnknown *)pActiveScriptSite->pSession); + + /* Create the scripting engine */ + if (type & msidbCustomActionTypeJScript) + hr = CLSIDFromProgID(szJScript, &clsid); + else if (type & msidbCustomActionTypeVBScript) + hr = CLSIDFromProgID(szVBScript, &clsid); + else { + ERR("Unknown script type %d\n", type); + goto done; + } + if (FAILED(hr)) { + ERR("Could not find CLSID for Windows Script\n"); + goto done; + } + hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IActiveScript, (void **)&pActiveScript); + if (FAILED(hr)) { + ERR("Could not instantiate class for Windows Script\n"); + goto done; + } + + /* If we got this far, Windows Script is installed, so don't return success by default anymore */ + ret = ERROR_INSTALL_FAILURE; + + /* Get the IActiveScriptParse engine interface */ + hr = IActiveScript_QueryInterface(pActiveScript, &IID_IActiveScriptParse, (void **)&pActiveScriptParse); + if (FAILED(hr)) goto done; + + /* Give our host to the engine */ + hr = IActiveScript_SetScriptSite(pActiveScript, (IActiveScriptSite *)pActiveScriptSite); + if (FAILED(hr)) goto done; + + /* Initialize the script engine */ + hr = IActiveScriptParse_InitNew(pActiveScriptParse); + if (FAILED(hr)) goto done; + + /* Add the session object */ + hr = IActiveScript_AddNamedItem(pActiveScript, szSession, SCRIPTITEM_ISVISIBLE); + + /* Pass the script to the engine */ + hr = IActiveScriptParse_ParseScriptText(pActiveScriptParse, script, NULL, NULL, NULL, 0, 0, 0L, NULL, NULL); + if (FAILED(hr)) goto done; + + /* Start processing the script */ + hr = IActiveScript_SetScriptState(pActiveScript, SCRIPTSTATE_CONNECTED); + if (FAILED(hr)) goto done; + + /* Call a function if necessary through the IDispatch interface */ + if (function != NULL && strlenW(function) > 0) { + TRACE("Calling function %s\n", debugstr_w(function)); + + hr = IActiveScript_GetScriptDispatch(pActiveScript, NULL, &pDispatch); + if (FAILED(hr)) goto done; + + hr = IDispatch_GetIDsOfNames(pDispatch, &IID_NULL, (WCHAR **)&function, 1,LOCALE_USER_DEFAULT, &dispid); + if (FAILED(hr)) goto done; + + hr = IDispatch_Invoke(pDispatch, dispid, &IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &dispparamsNoArgs, &var, NULL, NULL); + if (FAILED(hr)) goto done; + + /* Check return value, if it's not IDOK we failed */ + hr = VariantChangeType(&var, &var, 0, VT_I4); + if (FAILED(hr)) goto done; + + if (V_I4(&var) == IDOK) + ret = ERROR_SUCCESS; + else ret = ERROR_INSTALL_FAILURE; + + VariantClear(&var); + } else { + /* If no function to be called, MSI behavior is to succeed */ + ret = ERROR_SUCCESS; + } + +done: + + /* Free everything that needs to be freed */ + if (pDispatch) IDispatch_Release(pDispatch); + if (pActiveScript) IActiveScriptSite_Release(pActiveScript); + if (pActiveScriptSite && + pActiveScriptSite->pSession) IUnknown_Release((IUnknown *)pActiveScriptSite->pSession); + if (pActiveScriptSite) IUnknown_Release((IUnknown *)pActiveScriptSite); + + CoUninitialize(); /* must call even if CoInitialize failed */ + + return ret; +} + +/* + * MsiActiveScriptSite + */ + +/*** IUnknown methods ***/ +static HRESULT WINAPI MsiActiveScriptSite_QueryInterface(IActiveScriptSite* iface, REFIID riid, void** ppvObject) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + + TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject); + + if (IsEqualGUID(riid, &IID_IUnknown) || + IsEqualGUID(riid, &IID_IActiveScriptSite)) + { + IClassFactory_AddRef(iface); + *ppvObject = This; + return S_OK; + } + + TRACE("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject); + + return E_NOINTERFACE; +} + +static ULONG WINAPI MsiActiveScriptSite_AddRef(IActiveScriptSite* iface) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + + TRACE("(%p/%p)\n", iface, This); + + return InterlockedIncrement(&This->ref); +} + +static ULONG WINAPI MsiActiveScriptSite_Release(IActiveScriptSite* iface) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + ULONG ref = InterlockedDecrement(&This->ref); + + TRACE("(%p/%p)\n", iface, This); + + if (!ref) + HeapFree(GetProcessHeap(), 0, This); + + return ref; +} + +/*** IActiveScriptSite methods **/ +static HRESULT WINAPI MsiActiveScriptSite_GetLCID(IActiveScriptSite* iface, LCID* plcid) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + TRACE("(%p/%p)->(%p)\n", This, iface, plcid); + return E_NOTIMPL; /* Script will use system-defined locale */ +} + +static HRESULT WINAPI MsiActiveScriptSite_GetItemInfo(IActiveScriptSite* iface, LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown** ppiunkItem, ITypeInfo** ppti) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + TRACE("(%p/%p)->(%p,%d,%p,%p)\n", This, iface, pstrName, dwReturnMask, ppiunkItem, ppti); + + /* Determine the kind of pointer that is requested, and make sure placeholder is valid */ + if (dwReturnMask & SCRIPTINFO_ITYPEINFO) { + if (!ppti) return E_INVALIDARG; + *ppti = NULL; + } + if (dwReturnMask & SCRIPTINFO_IUNKNOWN) { + if (!ppiunkItem) return E_INVALIDARG; + *ppiunkItem = NULL; + } + + /* Are we looking for the session object? */ + if (!strcmpW(szSession, pstrName)) { + if (dwReturnMask & SCRIPTINFO_ITYPEINFO) + return load_type_info(This->pSession, ppti, &DIID_Session, 0); + else if (dwReturnMask & SCRIPTINFO_IUNKNOWN) { + IDispatch_QueryInterface(This->pSession, &IID_IUnknown, (void **)ppiunkItem); + return S_OK; + } + } + + return TYPE_E_ELEMENTNOTFOUND; +} + +static HRESULT WINAPI MsiActiveScriptSite_GetDocVersionString(IActiveScriptSite* iface, BSTR* pbstrVersion) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + TRACE("(%p/%p)->(%p)\n", This, iface, pbstrVersion); + return E_NOTIMPL; +} + +static HRESULT WINAPI MsiActiveScriptSite_OnScriptTerminate(IActiveScriptSite* iface, const VARIANT* pvarResult, const EXCEPINFO* pexcepinfo) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + TRACE("(%p/%p)->(%p,%p)\n", This, iface, pvarResult, pexcepinfo); + return S_OK; +} + +static HRESULT WINAPI MsiActiveScriptSite_OnStateChange(IActiveScriptSite* iface, SCRIPTSTATE ssScriptState) +{ + switch (ssScriptState) { + case SCRIPTSTATE_UNINITIALIZED: + TRACE("State: Uninitialized.\n"); + break; + + case SCRIPTSTATE_INITIALIZED: + TRACE("State: Initialized.\n"); + break; + + case SCRIPTSTATE_STARTED: + TRACE("State: Started.\n"); + break; + + case SCRIPTSTATE_CONNECTED: + TRACE("State: Connected.\n"); + break; + + case SCRIPTSTATE_DISCONNECTED: + TRACE("State: Disconnected.\n"); + break; + + case SCRIPTSTATE_CLOSED: + TRACE("State: Closed.\n"); + break; + + default: + ERR("Unknown State: %d\n", ssScriptState); + break; + } + + return S_OK; +} + +static HRESULT WINAPI MsiActiveScriptSite_OnScriptError(IActiveScriptSite* iface, IActiveScriptError* pscripterror) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + EXCEPINFO exception; + HRESULT hr; + + TRACE("(%p/%p)->(%p)\n", This, iface, pscripterror); + + hr = IActiveScriptError_GetExceptionInfo(pscripterror, &exception); + if (SUCCEEDED(hr)) + ERR("script error: %s\n", debugstr_w(exception.bstrDescription)); + + return S_OK; +} + +static HRESULT WINAPI MsiActiveScriptSite_OnEnterScript(IActiveScriptSite* iface) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + TRACE("(%p/%p)\n", This, iface); + return S_OK; +} + +static HRESULT WINAPI MsiActiveScriptSite_OnLeaveScript(IActiveScriptSite* iface) +{ + MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface; + TRACE("(%p/%p)\n", This, iface); + return S_OK; +} + +static const struct IActiveScriptSiteVtbl ASS_Vtbl = +{ + MsiActiveScriptSite_QueryInterface, + MsiActiveScriptSite_AddRef, + MsiActiveScriptSite_Release, + MsiActiveScriptSite_GetLCID, + MsiActiveScriptSite_GetItemInfo, + MsiActiveScriptSite_GetDocVersionString, + MsiActiveScriptSite_OnScriptTerminate, + MsiActiveScriptSite_OnStateChange, + MsiActiveScriptSite_OnScriptError, + MsiActiveScriptSite_OnEnterScript, + MsiActiveScriptSite_OnLeaveScript +};