Working on JScript/VBScript support for MSI
Misha Koshelev
mk144210 at bcm.tmc.edu
Tue Feb 20 16:05:57 CST 2007
On Tue, 2007-02-20 at 21:44 +0000, Robert Shearman wrote:
> Misha Koshelev wrote:
> > Hi, since MSDN states that any installer that requires JScript/VBScript
> > must make sure to install it, and since some applications require these
> > for install (bug #7353 and another one is there for VBScript for a
> > different installer), I figure there is no reason why builtin MSI cannot
> > use Microsoft Script if it is there (and plus when we have our own
> > JScript/VBScript we will be able to use it off the bat with MSI).
> >
> > I have posted my initial work on this on the bugzilla for bug #7353. The
> > first file (a patch, will attach also) is my patch which updates
> > custom.c to have the appropriate handlers for the custom action types,
> > extracts the script from the appropriate place, writes it to a file, and
> > uses a framework very similar to the current thread framework for DLLs
> > to call my helper call_script function. This first patch, in theory,
> > should not have to change much (if at all), although I have not
> > thoroughly tested it yet so it probably will.
> >
> > The second file (and attached here too) is dlls/msi/script.c, which is
> > where the meat and potatoes of the actual call_script function is/will
> > be. Right now it is pretty barebones, but it does load the script from
> > the file and calls it appropriately.
> >
> > Now I am working on implementing the Session object, although it looks
> > like a wrapper around MSI functions we already have so I think it should
> > be doable.
> >
> > Anyways, any comments on my work so far will be appreciated.
> >
>
> Nice work! Does MSI expose any builtin objects to the scripts (a quick
> Google search indicates there should be a Session object exposed)?
>
Btw, I did a little more work on my script.c yesterday, added a Session
object. Right now it just does a FIXME for any properties/methods that
are looked up, but what I need to do now is implement the Session class
(fair amount of work) and then just add an ITypeLib interface to Session
and then on to testing.
Misha
-------------- next part --------------
From 04dd51cf916c6756e410395d1b00db7752c53588 Mon Sep 17 00:00:00 2001
From: Misha Koshelev <mk144210 at bcm.tmc.edu>
Date: Mon, 19 Feb 2007 23:19:46 -0600
Subject: msi: Added core classes for JScript/VBScript support.
---
dlls/msi/script.c | 540 +++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 540 insertions(+), 0 deletions(-)
diff --git a/dlls/msi/script.c b/dlls/msi/script.c
new file mode 100644
index 0000000..d36316a
--- /dev/null
+++ b/dlls/msi/script.c
@@ -0,0 +1,540 @@
+/*
+ * 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 <stdarg.h>
+#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"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+const WCHAR szJScript[] = { 'J','S','c','r','i','p','t',0};
+const WCHAR szVBScript[] = { 'V','B','S','c','r','i','p','t',0};
+const WCHAR szSession[] = {'S','e','s','s','i','o','n',0};
+
+/*
+ * MSISessionDispatch
+ */
+
+typedef struct {
+ IDispatch lpVtbl;
+ LONG ref;
+} IMSISessionDispatch;
+
+static const struct IDispatchVtbl MSISessionDispatch_Vtbl;
+
+static HRESULT MSISessionDispatch_create(IUnknown *pUnkOuter, LPVOID *ppObj)
+{
+ IMSISessionDispatch* object;
+
+ TRACE("(%p,%p)\n", pUnkOuter, ppObj);
+
+ if( pUnkOuter )
+ return CLASS_E_NOAGGREGATION;
+
+ object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IMSISessionDispatch));
+
+ object->lpVtbl.lpVtbl = &MSISessionDispatch_Vtbl;
+ object->ref = 1;
+
+ *ppObj = object;
+
+ return S_OK;
+}
+
+/*
+ * MSIActiveScriptSite
+ */
+
+typedef struct {
+ IActiveScriptSite lpVtbl;
+ IMSISessionDispatch *iMSISessionDispatch;
+ LONG ref;
+} IMSIActiveScriptSite;
+
+static const struct IActiveScriptSiteVtbl ASS_Vtbl;
+
+static HRESULT ASS_create(IUnknown *pUnkOuter, LPVOID *ppObj)
+{
+ IMSIActiveScriptSite* object;
+
+ TRACE("(%p,%p)\n", pUnkOuter, ppObj);
+
+ if( pUnkOuter )
+ return CLASS_E_NOAGGREGATION;
+
+ object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(IMSIActiveScriptSite));
+
+ object->lpVtbl.lpVtbl = &ASS_Vtbl;
+ object->ref = 1;
+
+ *ppObj = object;
+
+ return S_OK;
+}
+
+/*
+ * Helper functions to run scripts
+ */
+
+LPCWSTR read_script_from_file(LPCWSTR szFile)
+{
+ HANDLE hFile;
+ DWORD sz, szHighWord = 0, read;
+ WCHAR *script;
+
+ hFile = CreateFileW(szFile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+ if (hFile == INVALID_HANDLE_VALUE)
+ return NULL;
+
+ sz = GetFileSize(hFile, &szHighWord);
+ if (sz == INVALID_FILE_SIZE || szHighWord != 0)
+ return NULL;
+
+ script = msi_alloc(sizeof(CHAR)*sz);
+ if (!script)
+ return NULL;
+
+ if (!ReadFile(hFile, (LPVOID)script, sz, &read, NULL))
+ {
+ msi_free(script);
+ return NULL;
+ }
+
+ return script;
+}
+
+/* JScript or VBScript? */
+LPCWSTR progid_from_type(INT type)
+{
+ if (type & msidbCustomActionTypeJScript) return szJScript;
+ else if (type & msidbCustomActionTypeVBScript) return szVBScript;
+
+ ERR("Unknown script type %d\n", type);
+ return NULL;
+}
+
+/**
+ * Call a script. This is our meat and potatoes.
+ */
+DWORD call_script(MSIPACKAGE *package, INT type, LPCWSTR filename, LPCWSTR function, LPCWSTR action)
+{
+ LPCWSTR script = NULL;
+ HRESULT hr;
+ IActiveScript *iActiveScript = NULL;
+ IActiveScriptParse *iActiveScriptParse = NULL;
+ IMSIActiveScriptSite *iMSIActiveScriptSite = NULL;
+ IMSISessionDispatch *iMSISessionDispatch = NULL;
+ IDispatch *iDispatch = NULL;
+ DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
+ DISPID dispid;
+ CLSID clsid;
+ VARIANT var;
+
+ /* Return success by default (e.g., if Windows Script not installed) - not native behavior */
+ DWORD ret = ERROR_SUCCESS;
+
+ CoInitialize(NULL);
+
+ /* Create IMSIActiveScriptSite object */
+ hr = ASS_create(NULL, (void **)&iMSIActiveScriptSite);
+ if (hr != S_OK) goto done;
+
+ /* Create an IMSISessionDispatch object */
+ hr = MSISessionDispatch_create(NULL, (void **)&iMSISessionDispatch);
+ if (hr != S_OK) goto done;
+ IUnknown_AddRef((IUnknown *)iMSISessionDispatch);
+ iMSIActiveScriptSite->iMSISessionDispatch = iMSISessionDispatch;
+
+ /* Create the scripting engine */
+ hr = CLSIDFromProgID(progid_from_type(type), &clsid);
+ if (FAILED(hr)) goto done;
+ hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IActiveScript, (void **)&iActiveScript);
+ if (FAILED(hr)) goto done;
+
+ /* If we got this far, Windows Script is installed, so don't return success by default anymore */
+/* ret = ERROR_INSTALL_FAILURE; */
+
+ /* Try to load the script file */
+ script = read_script_from_file(filename);
+ if (!script) goto done;
+
+ /* Get the IActiveScriptParse engine interface */
+ hr = IActiveScript_QueryInterface(iActiveScript, &IID_IActiveScriptParse, (void **)&iActiveScriptParse);
+ if (FAILED(hr)) goto done;
+
+ /* Give our host to the engine */
+ hr = IActiveScript_SetScriptSite(iActiveScript, (IActiveScriptSite *)iMSIActiveScriptSite);
+ if (FAILED(hr)) goto done;
+
+ /* Initialize the script engine */
+ hr = IActiveScriptParse_InitNew(iActiveScriptParse);
+ if (FAILED(hr)) goto done;
+
+ /* Add the session object */
+ hr = IActiveScript_AddNamedItem(iActiveScript, szSession, SCRIPTITEM_ISSOURCE | SCRIPTITEM_ISVISIBLE);
+
+ /* Pass the script to the engine */
+ hr = IActiveScriptParse_ParseScriptText(iActiveScriptParse, script, NULL, NULL, NULL, 0, 0, 0L, NULL, NULL);
+ if (FAILED(hr)) goto done;
+
+ /* Start processing the script */
+ hr = IActiveScript_SetScriptState(iActiveScript, 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", debugstr_w(function));
+
+ hr = IActiveScript_GetScriptDispatch(iActiveScript, NULL, &iDispatch);
+ if (FAILED(hr)) goto done;
+
+ hr = IDispatch_GetIDsOfNames(iDispatch, &IID_NULL, (WCHAR **)&function, 1,LOCALE_USER_DEFAULT, &dispid);
+ if (FAILED(hr)) goto done;
+
+ hr = IDispatch_Invoke(iDispatch, 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 (iDispatch) IDispatch_Release(iDispatch);
+ if (iActiveScript) IActiveScript_Release(iActiveScript);
+ if (iMSISessionDispatch) IDispatch_Release((IDispatch *)iMSISessionDispatch);
+ if (iMSIActiveScriptSite) IActiveScriptSite_Release((IActiveScriptSite *)iMSIActiveScriptSite);
+ if (script) msi_free((WCHAR *)script);
+
+ CoUninitialize(); /* must call even if CoInitialize failed */
+
+ return ret;
+}
+
+/*
+ * IMSIActiveScriptSite
+ */
+
+/*** IUnknown methods ***/
+static HRESULT WINAPI IMSIActiveScriptSite_QueryInterface(IActiveScriptSite* iface, REFIID riid, void** ppvObject)
+{
+ IMSIActiveScriptSite *This = (IMSIActiveScriptSite *)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 IMSIActiveScriptSite_AddRef(IActiveScriptSite* iface)
+{
+ IMSIActiveScriptSite *This = (IMSIActiveScriptSite *)iface;
+
+ TRACE("(%p/%p)\n", iface, This);
+
+ return InterlockedIncrement(&This->ref);
+}
+
+static ULONG WINAPI IMSIActiveScriptSite_Release(IActiveScriptSite* iface)
+{
+ IMSIActiveScriptSite *This = (IMSIActiveScriptSite *)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 IMSIActiveScriptSite_GetLCID(IActiveScriptSite* iface, LCID* plcid)
+{
+ IMSIActiveScriptSite *This = (IMSIActiveScriptSite *)iface;
+ TRACE("(%p/%p)->(%p) stub!\n", This, iface, plcid);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI IMSIActiveScriptSite_GetItemInfo(IActiveScriptSite* iface, LPCOLESTR pstrName, DWORD dwReturnMask, IUnknown** ppiunkItem, ITypeInfo** ppti)
+{
+ IMSIActiveScriptSite *This = (IMSIActiveScriptSite *)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) FIXME: Implement ITypeInfo
+ return ITypeInfo_LoadTypeInfo(This->iMSISessionDispatch, CLSID_iMSISessionDispatch, 0);
+ else */ if (dwReturnMask & SCRIPTINFO_IUNKNOWN) {
+ IDispatch_QueryInterface((IDispatch *)This->iMSISessionDispatch, &IID_IUnknown, (void **)ppiunkItem);
+ return S_OK;
+ }
+ }
+
+ return TYPE_E_ELEMENTNOTFOUND;
+}
+
+static HRESULT WINAPI IMSIActiveScriptSite_GetDocVersionString(IActiveScriptSite* iface, BSTR* pbstrVersion)
+{
+ IMSIActiveScriptSite *This = (IMSIActiveScriptSite *)iface;
+ TRACE("(%p/%p)->(%p) stub\n", This, iface, pbstrVersion);
+ return E_NOTIMPL;
+}
+
+static HRESULT WINAPI IMSIActiveScriptSite_OnScriptTerminate(IActiveScriptSite* iface, const VARIANT* pvarResult, const EXCEPINFO* pexcepinfo)
+{
+ IMSIActiveScriptSite *This = (IMSIActiveScriptSite *)iface;
+ TRACE("(%p/%p)->(%p,%p) stub\n", This, iface, pvarResult, pexcepinfo);
+ return S_OK;
+}
+
+static HRESULT WINAPI IMSIActiveScriptSite_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 IMSIActiveScriptSite_OnScriptError(IActiveScriptSite* iface, IActiveScriptError* pscripterror)
+{
+ IMSIActiveScriptSite *This = (IMSIActiveScriptSite *)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", debugstr_w(exception.bstrDescription));
+
+ return S_OK;
+}
+
+static HRESULT WINAPI IMSIActiveScriptSite_OnEnterScript(IActiveScriptSite* iface)
+{
+ IMSIActiveScriptSite *This = (IMSIActiveScriptSite *)iface;
+ TRACE("(%p/%p) stub\n", This, iface);
+ return S_OK;
+}
+
+static HRESULT WINAPI IMSIActiveScriptSite_OnLeaveScript(IActiveScriptSite* iface)
+{
+ IMSIActiveScriptSite *This = (IMSIActiveScriptSite *)iface;
+ TRACE("(%p/%p) stub\n", This, iface);
+ return S_OK;
+}
+
+static const struct IActiveScriptSiteVtbl ASS_Vtbl =
+{
+ IMSIActiveScriptSite_QueryInterface,
+ IMSIActiveScriptSite_AddRef,
+ IMSIActiveScriptSite_Release,
+ IMSIActiveScriptSite_GetLCID,
+ IMSIActiveScriptSite_GetItemInfo,
+ IMSIActiveScriptSite_GetDocVersionString,
+ IMSIActiveScriptSite_OnScriptTerminate,
+ IMSIActiveScriptSite_OnStateChange,
+ IMSIActiveScriptSite_OnScriptError,
+ IMSIActiveScriptSite_OnEnterScript,
+ IMSIActiveScriptSite_OnLeaveScript
+};
+
+/*
+ * IMSISessionDispatch
+ */
+
+/*** IUnknown methods ***/
+static HRESULT WINAPI IMSISessionDispatch_QueryInterface(IDispatch* iface, REFIID riid, void** ppvObject)
+{
+ IMSISessionDispatch *This = (IMSISessionDispatch *)iface;
+
+ TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject);
+
+ if (IsEqualGUID(riid, &IID_IUnknown) ||
+ IsEqualGUID(riid, &IID_IDispatch))
+ {
+ 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 IMSISessionDispatch_AddRef(IDispatch* iface)
+{
+ IMSISessionDispatch *This = (IMSISessionDispatch *)iface;
+
+ TRACE("(%p/%p)\n", iface, This);
+
+ return InterlockedIncrement(&This->ref);
+}
+
+static ULONG WINAPI IMSISessionDispatch_Release(IDispatch* iface)
+{
+ IMSISessionDispatch *This = (IMSISessionDispatch *)iface;
+ ULONG ref = InterlockedDecrement(&This->ref);
+
+ TRACE("(%p/%p)\n", iface, This);
+
+ if (!ref)
+ HeapFree(GetProcessHeap(), 0, This);
+
+ return ref;
+}
+
+static HRESULT WINAPI IMSISessionDispatch_GetTypeInfoCount(
+ IDispatch* iface,
+ UINT* pctinfo)
+{
+ IMSISessionDispatch *This = (IMSISessionDispatch *)iface;
+
+ TRACE("(%p/%p)->(%p)\n", iface, This, pctinfo);
+ *pctinfo = 0; /* FIXME: We don't support ITypeInfo yet */
+ return S_OK;
+}
+
+static HRESULT WINAPI IMSISessionDispatch_GetTypeInfo(
+ IDispatch* iface,
+ UINT iTInfo,
+ LCID lcid,
+ ITypeInfo** ppTInfo)
+{
+ IMSISessionDispatch *This = (IMSISessionDispatch *)iface;
+ TRACE("(%p/%p)->(%d,%d,%p)\n", iface, This, iTInfo, lcid, ppTInfo);
+ return E_NOTIMPL; /* FIXME: We don't support ITypeInfo yet */
+}
+
+static HRESULT WINAPI IMSISessionDispatch_GetIDsOfNames(
+ IDispatch* iface,
+ REFIID riid,
+ LPOLESTR* rgszNames,
+ UINT cNames,
+ LCID lcid,
+ DISPID* rgDispId)
+{
+ IMSISessionDispatch *This = (IMSISessionDispatch *)iface;
+ TRACE("(%p/%p)->(%p,%p,%d,%d,%p)\n", iface, This, riid, rgszNames, cNames, lcid, rgDispId);
+
+ if (!IsEqualGUID(riid, &IID_NULL)) return E_INVALIDARG;
+ if (cNames > 0) FIXME("%s\n", debugstr_w(rgszNames[0]));
+ return E_FAIL;
+}
+
+static HRESULT WINAPI IMSISessionDispatch_Invoke(
+ IDispatch* iface,
+ DISPID dispIdMember,
+ REFIID riid,
+ LCID lcid,
+ WORD wFlags,
+ DISPPARAMS* pDispParams,
+ VARIANT* pVarResult,
+ EXCEPINFO* pExcepInfo,
+ UINT* puArgErr)
+{
+ IMSISessionDispatch *This = (IMSISessionDispatch *)iface;
+ HRESULT hr = E_FAIL;
+ TRACE("(%p/%p)->(%d,%p,%d,%d,%p,%p,%p,%p)\n", iface, This, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
+
+ if (!IsEqualGUID(riid, &IID_NULL) /* || !(wFlags & DISPATCH_METHOD) */)
+ return E_INVALIDARG;
+
+ return hr;
+}
+
+static const struct IDispatchVtbl MSISessionDispatch_Vtbl =
+{
+ IMSISessionDispatch_QueryInterface,
+ IMSISessionDispatch_AddRef,
+ IMSISessionDispatch_Release,
+ IMSISessionDispatch_GetTypeInfoCount,
+ IMSISessionDispatch_GetTypeInfo,
+ IMSISessionDispatch_GetIDsOfNames,
+ IMSISessionDispatch_Invoke
+};
--
1.4.1
More information about the wine-devel
mailing list