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