msi: Add full JScript/VBScript support and partial expandable OLE automation support. [PATCH 3/3]

Misha Koshelev mk144210 at bcm.tmc.edu
Sat Feb 24 00:35:14 CST 2007


This patch adds a complete implementation of an IActiveScriptSite in the
script.c file which allows wine MSI to interface with Microsoft Script
(or our own native scripting when we have one) and to fully support
JScript and VBScript in msi files.

Then, the patch adds a framework to support the MSI OLE automation
classes, which are wrappers around existing MSI functions we provide.
With this framework, only the minimal amount of extra code will be
necessary to fully implement the OLE automation classes (see the bottom
of the file).

Finally, this patch implements some core methods in several OLE
automation classes (Session, Database, View, Field). This implementation
is sufficient to fix bug #7357 and allows Vector NTI to be installed on
wine by dropping in mfc42.dll and running the downloadable installer
(yay). Additionally, as we cannot have a bona fide conformance test for 
MSI scripting support, as Wine has no native scripting framework, this
provides a conformance check of sorts on the scripting framework and the
automation classes implemented.

Changelog:

	* msi: Add full JScript/VBScript support and partial expandable OLE
automation support.
-------------- next part --------------
From fc6b6c0634abdc1e67a901bd01e8c3c6d31aa960 Mon Sep 17 00:00:00 2001
From: Misha Koshelev <mk144210 at bcm.tmc.edu>
Date: Fri, 23 Feb 2007 23:53:09 -0600
Subject: msi: Add full JScript/VBScript support and partial expandable OLE automation support.
---
 dlls/msi/Makefile.in  |    4 
 dlls/msi/automation.c |  756 +++++++++++++++++++++++++++++++++++++++++++++++++
 dlls/msi/automation.h |  138 +++++++++
 dlls/msi/script.c     |  448 +++++++++++++++++++++++++++++
 4 files changed, 1346 insertions(+), 0 deletions(-)

diff --git a/dlls/msi/Makefile.in b/dlls/msi/Makefile.in
index 5fb0941..4e01dab 100644
--- a/dlls/msi/Makefile.in
+++ b/dlls/msi/Makefile.in
@@ -12,6 +12,7 @@ C_SRCS = \
 	action.c \
 	alter.c \
 	appsearch.c \
+	automation.c \
 	classes.c \
 	create.c \
 	custom.c \
@@ -37,6 +38,7 @@ C_SRCS = \
 	record.c \
 	registry.c \
 	regsvr.c \
+	script.c \
 	select.c \
 	source.c \
 	string.c \
@@ -48,6 +50,8 @@ C_SRCS = \
 	where.c
 
 IDL_TLB_SRCS = msiserver.idl
+IDL_H_SRCS = msiserver.idl
+IDL_I_SRCS = msiserver.idl 
 
 BISON_SRCS = \
 	cond.y \
diff --git a/dlls/msi/automation.c b/dlls/msi/automation.c
new file mode 100644
index 0000000..07992ac
--- /dev/null
+++ b/dlls/msi/automation.c
@@ -0,0 +1,756 @@
+/*
+ * Implementation of OLE Automation 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"
+
+#include "automation.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(msi);
+
+/*
+ * If you would like to implement a new automation function/object, look towards the bottom of this
+ * file for the "meat and potatoes" section.
+ */
+
+/* FIXME: I don't know how big this should be */
+#define MAX_MSI_STRING 1000
+
+static const struct IDispatchVtbl AutomationObject_Vtbl; 
+static const struct IProvideClassInfoVtbl AutomationObject_IProvideClassInfo_Vtbl; 
+static const struct IProvideClassInfo2Vtbl AutomationObject_IProvideClassInfo2_Vtbl; 
+static const struct IProvideMultipleClassInfoVtbl AutomationObject_IProvideMultipleClassInfo_Vtbl; 
+
+/* Load type info so we don't have to process GetIDsOfNames */
+HRESULT WINAPI LoadTypeInfo(IDispatch *iface, ITypeInfo **pptinfo, REFIID clsid, LCID lcid)
+{
+    HRESULT hr;
+    LPTYPELIB pLib = NULL;
+    LPTYPEINFO pInfo = NULL;
+    WCHAR szMsiServer[] = {'m','s','i','s','e','r','v','e','r','.','t','l','b'};
+
+    TRACE("(%p)->(%s,%d)\n", iface, debugstr_guid(clsid), lcid);    
+    
+    /* Load registered type library */
+    hr = LoadRegTypeLib(&LIBID_WindowsInstaller, 1, 0, lcid, &pLib);
+    if (FAILED(hr)) {
+	hr = LoadTypeLib(szMsiServer, &pLib);
+	if (FAILED(hr)) {
+	    ERR("Could not load msiserver.tlb\n");
+	    return hr;
+	}
+    }
+
+    /* Get type information for object */
+    hr = ITypeLib_GetTypeInfoOfGuid(pLib, clsid, &pInfo);
+    ITypeLib_Release(pLib);
+    if (FAILED(hr)) {
+	ERR("Could not load ITypeInfo for %s\n", debugstr_guid(clsid));
+	return hr;
+    }
+    *pptinfo = pInfo;
+    return S_OK;
+}
+
+/* Create the automation object, placing the result in the pointer ppObj. The automation object is created
+ * with the appropriate clsid and invocation function. */
+HRESULT create_automation_object(MSIHANDLE msiHandle, IUnknown *pUnkOuter, LPVOID *ppObj, REFIID clsid, LPVOID funcInvoke)
+{
+    AutomationObject *object;
+    HRESULT hr;
+
+    TRACE("(%ld,%p,%p,%s,%p)\n", (unsigned long)msiHandle, pUnkOuter, ppObj, debugstr_guid(clsid), funcInvoke);
+
+    if( pUnkOuter )
+	return CLASS_E_NOAGGREGATION;
+
+    object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AutomationObject));
+
+    /* Set all the VTable references */
+    object->lpVtbl = &AutomationObject_Vtbl;
+    object->lpvtblIProvideClassInfo = &AutomationObject_IProvideClassInfo_Vtbl;
+    object->lpvtblIProvideClassInfo2 = &AutomationObject_IProvideClassInfo2_Vtbl;
+    object->lpvtblIProvideMultipleClassInfo = &AutomationObject_IProvideMultipleClassInfo_Vtbl;
+    object->ref = 1;
+
+    /* Store data that was passed */
+    object->msiHandle = msiHandle;
+    object->clsid = (LPCLSID)clsid;
+    object->funcInvoke = funcInvoke;
+
+    /* Load our TypeInfo so we don't have to process GetIDsOfNames */
+    object->iTypeInfo = NULL;
+    hr = LoadTypeInfo((IDispatch *)object, &object->iTypeInfo, clsid, 0x0);
+    if (FAILED(hr)) { 
+	HeapFree(GetProcessHeap(), 0, object);
+	return hr;
+    }
+
+    *ppObj = object;
+
+    return S_OK;
+}
+
+/* Macros to get pointer to AutomationObject) from the other VTables. */
+static inline AutomationObject *obj_from_IProvideClassInfo( IProvideClassInfo *iface )
+{
+    return (AutomationObject *)((char*)iface - FIELD_OFFSET(AutomationObject, lpvtblIProvideClassInfo));
+}
+
+static inline AutomationObject *obj_from_IProvideClassInfo2( IProvideClassInfo2 *iface )
+{
+    return (AutomationObject *)((char*)iface - FIELD_OFFSET(AutomationObject, lpvtblIProvideClassInfo2));
+}
+
+static inline AutomationObject *obj_from_IProvideMultipleClassInfo( IProvideMultipleClassInfo *iface )
+{
+    return (AutomationObject *)((char*)iface - FIELD_OFFSET(AutomationObject, lpvtblIProvideMultipleClassInfo));
+}
+
+/*
+ * AutomationObject methods
+ */
+
+/*** IUnknown methods ***/
+static HRESULT WINAPI AutomationObject_QueryInterface(IDispatch* iface, REFIID riid, void** ppvObject)
+{
+    AutomationObject *This = (AutomationObject *)iface;
+
+    TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject); 
+
+    /*
+     * Perform a sanity check on the parameters.
+     */
+    if ( (This==0) || (ppvObject==0) )
+      return E_INVALIDARG;
+
+    /*
+     * Initialize the return parameter.
+     */
+    *ppvObject = 0;
+
+    /*
+     * Compare the riid with the interface IDs implemented by this object.
+     */
+    if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IDispatch) || IsEqualGUID(riid, This->clsid))
+        *ppvObject = This;
+    else if (IsEqualGUID(riid, &IID_IProvideClassInfo))
+	*ppvObject = (IProvideClassInfo*)&(This->lpvtblIProvideClassInfo);
+    else if (IsEqualGUID(riid, &IID_IProvideClassInfo2))
+	*ppvObject = (IProvideClassInfo2*)&(This->lpvtblIProvideClassInfo2);
+    else if (IsEqualGUID(riid, &IID_IProvideMultipleClassInfo))
+	*ppvObject = (IProvideMultipleClassInfo*)&(This->lpvtblIProvideMultipleClassInfo);
+
+    /*
+     * Check that we obtained an interface.
+     */
+    if ((*ppvObject)==0)
+    {
+	TRACE("() : asking for unsupported interface %s\n",debugstr_guid(riid));
+	return E_NOINTERFACE;
+    }
+
+    /*
+     * Query Interface always increases the reference count by one when it is
+     * successful
+     */
+    IClassFactory_AddRef(iface);
+
+    return S_OK;
+}
+
+static ULONG WINAPI AutomationObject_AddRef(IDispatch* iface)
+{
+    AutomationObject *This = (AutomationObject *)iface;
+
+    TRACE("(%p/%p)\n", iface, This);
+
+    return InterlockedIncrement(&This->ref);
+}
+
+static ULONG WINAPI AutomationObject_Release(IDispatch* iface)
+{
+    AutomationObject *This = (AutomationObject *)iface;
+    ULONG ref = InterlockedDecrement(&This->ref);
+
+    TRACE("(%p/%p)\n", iface, This);
+
+    if (!ref) 
+    {
+	MsiCloseHandle(This->msiHandle);
+        HeapFree(GetProcessHeap(), 0, This);
+    }
+
+    return ref;
+}
+
+/*** IDispatch methods ***/
+static HRESULT WINAPI AutomationObject_GetTypeInfoCount(
+        IDispatch* iface,
+        UINT* pctinfo)
+{
+    AutomationObject *This = (AutomationObject *)iface;
+
+    TRACE("(%p/%p)->(%p)\n", iface, This, pctinfo);
+    *pctinfo = 1;
+    return S_OK;
+}
+
+static HRESULT WINAPI AutomationObject_GetTypeInfo(
+        IDispatch* iface,
+        UINT iTInfo,
+        LCID lcid,
+        ITypeInfo** ppTInfo)
+{
+    AutomationObject *This = (AutomationObject *)iface;
+    TRACE("(%p/%p)->(%d,%d,%p)\n", iface, This, iTInfo, lcid, ppTInfo);
+
+    ITypeInfo_AddRef(This->iTypeInfo);
+    *ppTInfo = This->iTypeInfo;
+    return S_OK;
+}
+
+static HRESULT WINAPI AutomationObject_GetIDsOfNames(
+        IDispatch* iface,
+        REFIID riid,
+        LPOLESTR* rgszNames,
+        UINT cNames,
+        LCID lcid,
+        DISPID* rgDispId)
+{
+    AutomationObject *This = (AutomationObject *)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;
+    return ITypeInfo_GetIDsOfNames(This->iTypeInfo, rgszNames, cNames, rgDispId);
+}
+
+/* Error checking, etc. is done here to simplify individual object function invocation */
+static HRESULT WINAPI AutomationObject_Invoke(
+        IDispatch* iface,
+        DISPID dispIdMember,
+        REFIID riid,
+        LCID lcid,
+        WORD wFlags,
+        DISPPARAMS* pDispParams,
+        VARIANT* pVarResult,
+        EXCEPINFO* pExcepInfo,
+        UINT* puArgErr)
+{
+    AutomationObject *This = (AutomationObject *)iface;
+    HRESULT (STDMETHODCALLTYPE *Invoke)(
+        AutomationObject*,DISPID,REFIID,LCID,WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*,UINT*) = This->funcInvoke;
+    HRESULT hr;
+    BSTR bstrName = NULL;
+
+    TRACE("(%p/%p)->(%d,%p,%d,%d,%p,%p,%p,%p)\n", iface, This, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
+
+    if (!IsEqualIID(riid, &IID_NULL))
+    {
+	ERR("riid was %s instead of IID_NULL\n", debugstr_guid(riid));
+	return DISP_E_UNKNOWNNAME;
+    }
+
+    if (!pDispParams)
+    {
+	ERR("NULL pDispParams not allowed\n");
+	return DISP_E_PARAMNOTOPTIONAL;
+    }
+
+    if (wFlags & DISPATCH_PROPERTYGET && !pVarResult)
+    {
+	ERR("NULL pVarResult not allowed when DISPATCH_PROPERTYGET specified\n");
+	return DISP_E_PARAMNOTOPTIONAL;
+    }
+
+    /* If there is a result, make default type empty */
+    if (pVarResult) V_VT(pVarResult) = VT_EMPTY;
+
+    /* If we are tracing, we want to see the name of the member we are invoking */
+    if (TRACE_ON(msi))
+    {
+	ITypeInfo_GetDocumentation(This->iTypeInfo, dispIdMember, &bstrName, NULL, NULL, NULL);
+	TRACE("Method %d, %s\n", dispIdMember, debugstr_w(bstrName));
+    }
+
+    hr = Invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
+
+    if (hr == DISP_E_MEMBERNOTFOUND) {
+	if (bstrName == NULL) ITypeInfo_GetDocumentation(This->iTypeInfo, dispIdMember, &bstrName, NULL, NULL, NULL);
+	FIXME("Method %d, %s wflags %d not implemented, clsid %s\n", dispIdMember, debugstr_w(bstrName), wFlags, debugstr_guid(This->clsid));
+    }
+
+    TRACE("Returning %d, %s\n", hr, hr == S_OK ? "ok" : "not ok");
+
+    return hr;
+}
+
+static const struct IDispatchVtbl AutomationObject_Vtbl = 
+{
+    AutomationObject_QueryInterface,
+    AutomationObject_AddRef,
+    AutomationObject_Release,
+    AutomationObject_GetTypeInfoCount,
+    AutomationObject_GetTypeInfo,
+    AutomationObject_GetIDsOfNames,
+    AutomationObject_Invoke
+};
+
+/*
+ * IProvideClassInfo methods 
+ */
+
+static HRESULT WINAPI AutomationObject_IProvideClassInfo_QueryInterface(
+  IProvideClassInfo* iface,
+  REFIID     riid,
+  VOID**     ppvoid)
+{
+    AutomationObject *This = obj_from_IProvideClassInfo(iface);
+    return AutomationObject_QueryInterface((IDispatch *)This, riid, ppvoid);
+}
+
+static ULONG WINAPI AutomationObject_IProvideClassInfo_AddRef(IProvideClassInfo* iface)
+{
+    AutomationObject *This = obj_from_IProvideClassInfo(iface);
+    return AutomationObject_AddRef((IDispatch *)This);
+}
+
+static ULONG WINAPI AutomationObject_IProvideClassInfo_Release(IProvideClassInfo* iface)
+{
+    AutomationObject *This = obj_from_IProvideClassInfo(iface);
+    return AutomationObject_Release((IDispatch *)This);
+}
+
+static HRESULT WINAPI AutomationObject_GetClassInfo(IProvideClassInfo* iface, ITypeInfo** ppTI)
+{
+    AutomationObject *This = obj_from_IProvideClassInfo(iface);
+
+    TRACE("(%p/%p)->(%p)\n", iface, This, ppTI);
+    return LoadTypeInfo((IDispatch *)This, ppTI, This->clsid, 0);
+}
+
+static const IProvideClassInfoVtbl AutomationObject_IProvideClassInfo_Vtbl =
+{
+    AutomationObject_IProvideClassInfo_QueryInterface,
+    AutomationObject_IProvideClassInfo_AddRef,
+    AutomationObject_IProvideClassInfo_Release,    
+    AutomationObject_GetClassInfo
+};
+
+/*
+ * IProvideClassInfo2 methods
+ */
+
+static HRESULT WINAPI AutomationObject_IProvideClassInfo2_QueryInterface(
+  IProvideClassInfo2* iface,
+  REFIID     riid,
+  VOID**     ppvoid)
+{
+    AutomationObject *This = obj_from_IProvideClassInfo2(iface);
+    return AutomationObject_QueryInterface((IDispatch *)This, riid, ppvoid);
+}
+
+static ULONG WINAPI AutomationObject_IProvideClassInfo2_AddRef(IProvideClassInfo2* iface)
+{
+    AutomationObject *This = obj_from_IProvideClassInfo2(iface);
+    return AutomationObject_AddRef((IDispatch *)This);
+}
+
+static ULONG WINAPI AutomationObject_IProvideClassInfo2_Release(IProvideClassInfo2* iface)
+{
+    AutomationObject *This = obj_from_IProvideClassInfo2(iface);
+    return AutomationObject_Release((IDispatch *)This);
+}
+
+static HRESULT WINAPI AutomationObject_IProvideClassInfo2_GetClassInfo(IProvideClassInfo2* iface, ITypeInfo** ppTI)
+{
+    AutomationObject *This = obj_from_IProvideClassInfo2(iface);
+    return AutomationObject_GetClassInfo((IProvideClassInfo*)&(This->lpvtblIProvideClassInfo), ppTI);    
+}
+
+static HRESULT WINAPI AutomationObject_GetGUID(IProvideClassInfo2* iface, DWORD dwGuidKind, GUID* pGUID)
+{
+    AutomationObject *This = obj_from_IProvideClassInfo2(iface);
+    TRACE("(%p/%p)->(%d,%s)\n", iface, This, dwGuidKind, debugstr_guid(pGUID));
+    
+    if (dwGuidKind != GUIDKIND_DEFAULT_SOURCE_DISP_IID)
+	return E_INVALIDARG;
+    else {
+	*pGUID = *This->clsid;
+	return S_OK;
+    }
+}
+
+static const IProvideClassInfo2Vtbl AutomationObject_IProvideClassInfo2_Vtbl =
+{
+    AutomationObject_IProvideClassInfo2_QueryInterface,
+    AutomationObject_IProvideClassInfo2_AddRef,
+    AutomationObject_IProvideClassInfo2_Release,    
+    AutomationObject_IProvideClassInfo2_GetClassInfo,    
+    AutomationObject_GetGUID
+};
+
+/* 
+ * IProvideMultipleClassInfo methods
+ */
+
+static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_QueryInterface(
+  IProvideMultipleClassInfo* iface,
+  REFIID     riid,
+  VOID**     ppvoid)
+{
+    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
+    return AutomationObject_QueryInterface((IDispatch *)This, riid, ppvoid);
+}
+
+static ULONG WINAPI AutomationObject_IProvideMultipleClassInfo_AddRef(IProvideMultipleClassInfo* iface)
+{
+    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
+    return AutomationObject_AddRef((IDispatch *)This);
+}
+
+static ULONG WINAPI AutomationObject_IProvideMultipleClassInfo_Release(IProvideMultipleClassInfo* iface)
+{
+    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
+    return AutomationObject_Release((IDispatch *)This);
+}
+
+static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_GetClassInfo(IProvideMultipleClassInfo* iface, ITypeInfo** ppTI)
+{
+    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
+    return AutomationObject_GetClassInfo((IProvideClassInfo*)&(This->lpvtblIProvideClassInfo), ppTI);    
+}
+
+static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_GetGUID(IProvideMultipleClassInfo* iface, DWORD dwGuidKind, GUID* pGUID)
+{
+    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
+    return AutomationObject_GetGUID((IProvideClassInfo2*)&(This->lpvtblIProvideClassInfo2), dwGuidKind, pGUID);
+}
+
+static HRESULT WINAPI AutomationObject_GetMultiTypeInfoCount(IProvideMultipleClassInfo* iface, ULONG* pcti)
+{
+    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
+
+    TRACE("(%p/%p)->(%p)\n", iface, This, pcti);
+    *pcti = 1;
+    return S_OK;
+}
+
+static HRESULT WINAPI AutomationObject_GetInfoOfIndex(IProvideMultipleClassInfo* iface,
+        ULONG iti,
+        DWORD dwFlags,
+        ITypeInfo** pptiCoClass,
+        DWORD* pdwTIFlags,
+        ULONG* pcdispidReserved,
+        IID* piidPrimary,
+        IID* piidSource)
+{
+    AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
+
+    TRACE("(%p/%p)->(%d,%d,%p,%p,%p,%p,%p)\n", iface, This, iti, dwFlags, pptiCoClass, pdwTIFlags, pcdispidReserved, piidPrimary, piidSource);
+    
+    if (iti != 0)
+	return E_INVALIDARG;
+
+    if (dwFlags & MULTICLASSINFO_GETTYPEINFO)
+	LoadTypeInfo((IDispatch *)This, pptiCoClass, This->clsid, 0);
+
+    if (dwFlags & MULTICLASSINFO_GETNUMRESERVEDDISPIDS)
+    {
+	*pdwTIFlags = 0;
+	*pcdispidReserved = 0;
+    }
+
+    if (dwFlags & MULTICLASSINFO_GETIIDPRIMARY){
+	*piidPrimary = *This->clsid;
+    }
+
+    if (dwFlags & MULTICLASSINFO_GETIIDSOURCE){
+        *piidSource = *This->clsid;
+    }
+
+    return S_OK;
+}
+
+static const IProvideMultipleClassInfoVtbl AutomationObject_IProvideMultipleClassInfo_Vtbl =
+{
+    AutomationObject_IProvideMultipleClassInfo_QueryInterface,
+    AutomationObject_IProvideMultipleClassInfo_AddRef,
+    AutomationObject_IProvideMultipleClassInfo_Release,    
+    AutomationObject_IProvideMultipleClassInfo_GetClassInfo,
+    AutomationObject_IProvideMultipleClassInfo_GetGUID,
+    AutomationObject_GetMultiTypeInfoCount,
+    AutomationObject_GetInfoOfIndex
+};
+
+/*
+ * Individual Object Invocation Functions - Our meat and potatoes
+ *
+ *    - To add a method, just add an appropriate case to an appropriate switch statement
+ *        * Follow syntax here for property get/puts and method calls. Remember, parameters
+ *          are passed IN REVERSE ORDER (last parameter to function is first in array). This got me at first.
+ *        * MSI specs seem to indicate that most functions return an S_OK. If you don't, chances are the
+ *          script will stop running, but check the MSI documentation for your specific function.
+ *    - To add a new object, just add an ObjectImpl_Invoke method and appropriate method call that 
+ *      creates your object. Note that these are not true Invoke methods, but are called from 
+ *      AutomationObject_Invoke with some appropriate parameter error checking, made to ensure that 
+ *      the functions below remain just down to what they need to do.
+ */
+
+HRESULT WINAPI RecordImpl_Invoke(
+        AutomationObject* This,
+        DISPID dispIdMember,
+        REFIID riid,
+        LCID lcid,
+        WORD wFlags,
+        DISPPARAMS* pDispParams,
+        VARIANT* pVarResult,
+        EXCEPINFO* pExcepInfo,
+        UINT* puArgErr)
+{
+    WCHAR szString[MAX_MSI_STRING];
+    DWORD dwLen = MAX_MSI_STRING;
+    UINT ret;
+
+    switch (dispIdMember) 
+    {
+	case RecordDispId_StringData:
+	    if (wFlags & DISPATCH_PROPERTYGET) {
+ 	        V_VT(pVarResult) = VT_BSTR;
+		if ((ret = MsiRecordGetStringW(This->msiHandle, V_I4(&pDispParams->rgvarg[0]),
+				               szString, &dwLen)) == ERROR_SUCCESS)
+		    V_BSTR(pVarResult) = SysAllocString(szString);
+		else
+		{
+		    TRACE("MsiRecordGetString returned %d\n", ret);
+		    V_BSTR(pVarResult) = NULL;
+		}
+		return S_OK;
+	    } else if (wFlags & DISPATCH_PROPERTYPUT) {
+		return (MsiRecordSetStringW(This->msiHandle, V_I4(&pDispParams->rgvarg[1]),
+   				        V_BSTR(&pDispParams->rgvarg[0])) == ERROR_SUCCESS ? S_OK : E_FAIL);
+	    }
+	    break;
+    }
+
+    return DISP_E_MEMBERNOTFOUND;
+}
+
+HRESULT WINAPI ViewImpl_Invoke(
+        AutomationObject* This,
+        DISPID dispIdMember,
+        REFIID riid,
+        LCID lcid,
+        WORD wFlags,
+        DISPPARAMS* pDispParams,
+        VARIANT* pVarResult,
+        EXCEPINFO* pExcepInfo,
+        UINT* puArgErr)
+{
+    MSIHANDLE msiHandle;
+    AutomationObject *obj = NULL;
+    IDispatch *iDispatch = NULL;
+    UINT ret;
+
+    switch (dispIdMember) 
+    {
+	case ViewDispId_Execute:
+	    if (wFlags & DISPATCH_METHOD)
+	    { 
+	        obj = (AutomationObject *)V_DISPATCH(&pDispParams->rgvarg[0]);
+		MsiViewExecute(This->msiHandle, obj == NULL ? 0 : obj->msiHandle);
+		return S_OK;
+	    }
+	    break;
+
+	case ViewDispId_Fetch:
+	    if (wFlags & DISPATCH_METHOD)
+	    { 
+		V_VT(pVarResult) = VT_DISPATCH;
+		if ((ret = MsiViewFetch(This->msiHandle, &msiHandle)) == ERROR_SUCCESS) 
+		    create_automation_object(msiHandle, NULL, (LPVOID)&iDispatch, &DIID_Record, RecordImpl_Invoke);
+		else TRACE("MsiViewFetch returned %d\n", ret);
+		V_DISPATCH(pVarResult) = iDispatch;
+		return S_OK;
+	    }
+	    break;
+
+	case ViewDispId_Close:
+	    if (wFlags & DISPATCH_METHOD)
+	    { 
+		MsiViewClose(This->msiHandle);
+		return S_OK;
+	    }
+	    break;
+    }
+
+    return DISP_E_MEMBERNOTFOUND;
+}
+
+HRESULT WINAPI DatabaseImpl_Invoke(
+        AutomationObject* This,
+        DISPID dispIdMember,
+        REFIID riid,
+        LCID lcid,
+        WORD wFlags,
+        DISPPARAMS* pDispParams,
+        VARIANT* pVarResult,
+        EXCEPINFO* pExcepInfo,
+        UINT* puArgErr)
+{
+    MSIHANDLE msiHandle;
+    IDispatch *iDispatch = NULL;
+    UINT ret;
+
+    switch (dispIdMember) 
+    {
+	case DatabaseDispId_OpenView:
+	    if (wFlags & DISPATCH_METHOD)
+	    { 
+		V_VT(pVarResult) = VT_DISPATCH;
+		if ((ret = MsiDatabaseOpenViewW(This->msiHandle, V_BSTR(&pDispParams->rgvarg[0]), &msiHandle)) == ERROR_SUCCESS) 
+		    create_automation_object(msiHandle, NULL, (LPVOID)&iDispatch, &DIID_View, ViewImpl_Invoke);
+		else TRACE("MsiDatabaseOpenViewW returned %d\n", ret);
+	        V_DISPATCH(pVarResult) = iDispatch;
+		return S_OK;
+	    }
+	    break;
+    }
+
+    return DISP_E_MEMBERNOTFOUND;
+}
+
+HRESULT WINAPI SessionImpl_Invoke(
+        AutomationObject* This,
+        DISPID dispIdMember,
+        REFIID riid,
+        LCID lcid,
+        WORD wFlags,
+        DISPPARAMS* pDispParams,
+        VARIANT* pVarResult,
+        EXCEPINFO* pExcepInfo,
+        UINT* puArgErr)
+{
+    WCHAR szString[MAX_MSI_STRING];
+    DWORD dwLen = MAX_MSI_STRING;
+    IDispatch *iDispatch = NULL;
+    MSIHANDLE msiHandle;
+    LANGID langId;
+    UINT ret;
+    INSTALLSTATE iInstalled, iAction;
+
+    switch (dispIdMember)
+    {
+	case SessionDispId_Property:
+	    if (wFlags & DISPATCH_PROPERTYGET) {
+		V_VT(pVarResult) = VT_BSTR;
+		V_BSTR(pVarResult) = NULL;
+		if (MsiGetPropertyW(This->msiHandle, V_BSTR(&pDispParams->rgvarg[0]),
+				    szString, &dwLen) == ERROR_SUCCESS)
+		    V_BSTR(pVarResult) = SysAllocString(szString);
+		return S_OK;
+	    } else if (wFlags & DISPATCH_PROPERTYPUT) {
+		return (MsiSetPropertyW(This->msiHandle, V_BSTR(&pDispParams->rgvarg[1]),
+   				        V_BSTR(&pDispParams->rgvarg[0])) == ERROR_SUCCESS ? S_OK : E_FAIL);
+	    }
+	    break;
+
+	case SessionDispId_Language:
+	    if (wFlags & DISPATCH_PROPERTYGET) {
+		langId = MsiGetLanguage(This->msiHandle);
+		if (langId != ERROR_INVALID_HANDLE)
+		{
+		    V_VT(pVarResult) = VT_I4;
+		    V_I4(pVarResult) = langId;	
+		    return S_OK;
+		} else return E_FAIL;
+	    } 
+	    break;
+
+	case SessionDispId_Mode:
+	    if (wFlags & DISPATCH_PROPERTYGET) {
+		V_VT(pVarResult) = VT_BOOL;
+		V_BOOL(pVarResult) = MsiGetMode(This->msiHandle, V_I4(&pDispParams->rgvarg[0]));
+		return S_OK;
+	    } else if (wFlags & DISPATCH_PROPERTYPUT) {
+		return (MsiSetMode(This->msiHandle, V_I4(&pDispParams->rgvarg[1]),
+   				   V_BOOL(&pDispParams->rgvarg[0])) == ERROR_SUCCESS ? S_OK : E_FAIL);
+	    }
+	    break;
+
+	case SessionDispId_Database:
+	    if (wFlags & DISPATCH_PROPERTYGET) {
+  	        V_VT(pVarResult) = VT_DISPATCH;
+		msiHandle = MsiGetActiveDatabase(This->msiHandle);
+		if (msiHandle) 
+		    create_automation_object(msiHandle, NULL, (LPVOID)&iDispatch, &DIID_Database, DatabaseImpl_Invoke);
+		else TRACE("MsiGetActiveDatabase failed\n");
+		V_DISPATCH(pVarResult) = iDispatch;
+		return S_OK;
+	    }
+	    break;
+
+	case SessionDispId_FeatureCurrentState:
+	    if (wFlags & DISPATCH_PROPERTYGET) {
+		V_VT(pVarResult) = VT_I4;
+		if ((ret = MsiGetFeatureStateW(This->msiHandle, V_BSTR(&pDispParams->rgvarg[0]),
+					      &iInstalled, &iAction)) == ERROR_SUCCESS)
+		    V_I4(pVarResult) = iInstalled;
+		else 
+		{
+		    TRACE("MsiGetFeatureState returned %d\n", ret);
+		    V_I4(pVarResult) = msiInstallStateUnknown;
+		}
+		return S_OK;
+	    } 
+	    break;
+
+	case SessionDispId_FeatureRequestState:
+	    if (wFlags & DISPATCH_PROPERTYGET) {
+		V_VT(pVarResult) = VT_I4;
+		if ((ret = MsiGetFeatureStateW(This->msiHandle, V_BSTR(&pDispParams->rgvarg[0]),
+					      &iInstalled, &iAction)) == ERROR_SUCCESS)
+		    V_I4(pVarResult) = iAction;
+		else 
+		{
+		    TRACE("MsiGetFeatureState returned %d\n", ret);
+		    V_I4(pVarResult) = msiInstallStateUnknown;
+		}
+		return S_OK;
+	    } else if (wFlags & DISPATCH_PROPERTYPUT) {
+		return (MsiSetFeatureStateW(This->msiHandle, V_BSTR(&pDispParams->rgvarg[1]),
+   				   (INSTALLSTATE)V_I4(&pDispParams->rgvarg[0])) == ERROR_SUCCESS ? S_OK : E_FAIL);
+	    }
+	    break;
+    }
+
+    return DISP_E_MEMBERNOTFOUND;
+}
diff --git a/dlls/msi/automation.h b/dlls/msi/automation.h
new file mode 100644
index 0000000..ed67dbf
--- /dev/null
+++ b/dlls/msi/automation.h
@@ -0,0 +1,138 @@
+/*
+ * Implementation of OLE Automation 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
+ */
+
+#ifndef __WINE_MSI_AUTOMATION__
+#define __WINE_MSI_AUTOMATION__
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "msi.h"
+#include "msiquery.h"
+#include "objbase.h"
+#include "objidl.h"
+#include "winnls.h"
+#include "wine/list.h"
+
+#include "msiserver.h"
+
+/*
+ * AutomationObject - "base" class for all automation objects so we don't have to repeat functions. Just
+ *                    need to implement Invoke function for each dispinterface and pass the new function
+ *                    to create_automation_object.
+ */
+
+typedef struct {
+    /*
+     * VTables - We provide IDispatch, IProvideClassInfo, IProvideClassInfo2, IProvideMultipleClassInfo
+     */
+    const IDispatchVtbl *lpVtbl;
+    const IProvideClassInfoVtbl *lpvtblIProvideClassInfo;
+    const IProvideClassInfo2Vtbl *lpvtblIProvideClassInfo2;
+    const IProvideMultipleClassInfoVtbl *lpvtblIProvideMultipleClassInfo;
+    
+    /* Object reference count */
+    LONG ref;
+
+    /* Clsid for this class and it's appropriate ITypeInfo object */
+    LPCLSID clsid;
+    ITypeInfo *iTypeInfo;
+
+    /* The MSI handle of the current object */
+    MSIHANDLE msiHandle;;
+
+    /* A function that is called from IDispatch::Invoke, specific to this type of object */
+    LPVOID funcInvoke;
+} AutomationObject;
+
+/* This is the function that one needs to call to create an automation object. */
+extern HRESULT create_automation_object(MSIHANDLE msiHandle, IUnknown *pUnkOuter, LPVOID *ppObj, REFIID clsid, LPVOID funcInvoke);
+
+/* We need to expose these functions because our IActiveScriptSite calls it */
+extern HRESULT WINAPI LoadTypeInfo(IDispatch *iface, ITypeInfo **pptinfo, REFIID clsid, LCID lcid);
+extern HRESULT WINAPI SessionImpl_Invoke(AutomationObject*,DISPID,REFIID,LCID,WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*,UINT*);
+
+/* Disp Ids 
+ * (not complete, look in msiserver.idl to add more) */
+typedef enum {
+    RecordDispId_StringData=1,
+    RecordDispId_IntegerData,
+    RecordDispId_SetStream,
+    RecordDispId_ReadStream,
+    RecordDispId_FieldCount,
+    RecordDispId_IsNull,
+    RecordDispId_DataSize,
+    RecordDispId_ClearData,
+    RecordDispId_FormatText
+} RecordDispId;
+
+typedef enum {
+    ViewDispId_Execute=1,
+    ViewDispId_Fetch,
+    ViewDispId_Modify,
+    ViewDispId_Close,
+    ViewDispId_ColumnInfo,
+    ViewDispId_GetError
+} ViewDispId;
+
+typedef enum {
+    DatabaseDispId_DatabaseState=1,
+    DatabaseDispId_SummaryInformation,
+    DatabaseDispId_OpenView,
+    DatabaseDispId_Commit,
+    DatabaseDispId_PrimaryKeys,
+    DatabaseDispId_Import,
+    DatabaseDispId_Export,
+    DatabaseDispId_Merge,
+    DatabaseDispId_GenerateTransform,
+    DatabaseDispId_ApplyTransform,
+    DatabaseDispId_EnableUIPreview,
+    DatabaseDispId_TablePersistent,
+    DatabaseDispId_CreateTransformSummaryInfo
+} DatabaseDispId;
+
+typedef enum {
+    SessionDispId_Installer=1,
+    SessionDispId_Property,
+    SessionDispId_Language,
+    SessionDispId_Mode,
+    SessionDispId_Database,
+    SessionDispId_SourcePath,
+    SessionDispId_TargetPath,
+    SessionDispId_DoAction,
+    SessionDispId_Sequence,
+    SessionDispId_EvaluateCondition,
+    SessionDispId_FormatRecord,
+    SessionDispId_Message,
+    SessionDispId_FeatureCurrentState,
+    SessionDispId_FeatureRequestState,
+    SessionDispId_FeatureValidStates,
+    SessionDispId_FeatureCost,
+    SessionDispId_ComponentCurrentState,
+    SessionDispId_ComponentRequestState,
+    SessionDispId_SetInstallLevel,
+    SessionDispId_VerifyDiskSpace,
+    SessionDispId_ProductProperty,
+    SessionDispId_FeatureInfo,
+    SessionDispId_ComponentCost
+} SessionDispId;
+
+#endif /* __WINE_MSI_AUTOMATION__ */
diff --git a/dlls/msi/script.c b/dlls/msi/script.c
new file mode 100644
index 0000000..6d62c41
--- /dev/null
+++ b/dlls/msi/script.c
@@ -0,0 +1,448 @@
+/*
+ * 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"
+
+#include "automation.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};
+
+/* Would be better not to have to define this here, but need to tell which scripts are Unicode and
+ * which are not so we can convert to unicode */
+#define CUSTOM_ACTION_TYPE_MASK 0x3F
+
+/*
+ * MsiActiveScriptSite - Our IActiveScriptSite implementation.
+ */
+
+typedef struct {
+    IActiveScriptSite lpVtbl;
+    AutomationObject *session;
+    LONG ref;
+} MsiActiveScriptSite;
+
+static const struct IActiveScriptSiteVtbl ASS_Vtbl;
+
+static HRESULT ASS_create(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->session = NULL;
+
+    *ppObj = object;
+
+    return S_OK;
+}
+
+/*
+ * Helper functions to run scripts 
+ *
+ *    - We write all scripts to a file in custom.c so we can use one function to implement both 
+ *      scripts that are files (either installed or written from an msi binary) and from properties
+ *      or from the custom action table.
+ */
+
+LPCWSTR read_script_from_file(LPCWSTR szFile, INT type)
+{
+    HANDLE hFile;
+    DWORD sz, szHighWord = 0, read;
+    WCHAR *script=NULL, *script2=NULL;
+
+    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+1));
+    if (!script) 
+	return NULL;
+  
+    if (!ReadFile(hFile, (LPVOID)script, sz, &read, NULL)) 
+    {
+	msi_free(script);
+	return NULL;
+    }
+
+    /* Convert to unicode if necessary (hack to determine which types need to be converted) */
+    switch (type & CUSTOM_ACTION_TYPE_MASK)
+    {
+	case 5:
+	case 6:
+	case 21:
+	case 22:
+	    /* Null terminate string before converting */
+	    ((CHAR *)script)[read] = 0;
+
+	    TRACE("Converting script to UniCode\n");
+	    sz = MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (LPCSTR)script, -1, script2, 0);
+	    script2 = msi_alloc(sizeof(WCHAR)*sz);
+	    if (!script2) 
+	    {
+		msi_free(script);
+		return NULL;
+	    }
+	    if (!MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, (LPCSTR)script, -1, script2, sz))
+	    {
+		msi_free(script2);
+		msi_free(script);
+		return NULL;
+	    }
+	    TRACE("Converted successfully\n");
+	    return script2;
+
+        default:
+	    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. 
+ *     - Currently, since the function is relatively new, it will always end up returning S_OK.
+ *       Think of it like a bonus feature, we can run the script - great. If we have a problem,
+ *       we are no worse off than if this function had not been called.
+ */
+DWORD call_script(MSIHANDLE hPackage, INT type, LPCWSTR filename, LPCWSTR function, LPCWSTR action)
+{
+    LPCWSTR script = NULL, progId = NULL;
+    HRESULT hr;
+    IActiveScript *iActiveScript = NULL;
+    IActiveScriptParse *iActiveScriptParse = NULL;
+    MsiActiveScriptSite *msiActiveScriptSite = NULL;
+    IDispatch *iDispatch = NULL;
+    DISPPARAMS dispparamsNoArgs = {NULL, NULL, 0, 0};
+    DISPID dispid;
+    CLSID clsid;
+    VARIANT var;
+
+    /* Reeturn 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 = ASS_create(NULL, (void **)&msiActiveScriptSite);
+    if (hr != S_OK) goto done;
+
+    /* Create a session object */
+    hr = create_automation_object(hPackage, NULL, (void **)&msiActiveScriptSite->session, &DIID_Session, SessionImpl_Invoke);
+    if (hr != S_OK) goto done;
+    IUnknown_AddRef((IUnknown *)msiActiveScriptSite->session);
+
+    /* Create the scripting engine */
+    progId = progid_from_type(type);
+    hr = CLSIDFromProgID(progId, &clsid);
+    if (FAILED(hr)) {
+	ERR("Could not find CLSID for program ID %s. Is scripting installed?\n", debugstr_w(progId));
+	goto done;
+    }
+    hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IActiveScript, (void **)&iActiveScript);
+    if (FAILED(hr)) {
+	ERR("Could not instantiate class for program ID %s\n", debugstr_w(progId));
+	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, type);
+    if (!script) goto done;
+
+    TRACE("Calling function %s, script is %s\n", debugstr_w(function), debugstr_w(script));
+
+    /* 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 *)msiActiveScriptSite);
+    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_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\n", 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) IActiveScriptSite_Release(iActiveScript);
+    if (msiActiveScriptSite &&
+	msiActiveScriptSite->session) IUnknown_Release((IUnknown *)msiActiveScriptSite->session);
+    if (msiActiveScriptSite) IUnknown_Release((IUnknown *)msiActiveScriptSite);
+    if (script) msi_free((WCHAR *)script);
+
+    CoUninitialize();    /* must call even if CoInitialize failed */
+
+/*    return ret;  */
+    return ERROR_SUCCESS;       /* FIXME: Until thoroughly tested, always return success */
+}
+
+/*
+ * 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) stub!\n", This, iface, plcid);
+    return E_NOTIMPL;
+}
+
+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 LoadTypeInfo((IDispatch *)This->session, ppti, &DIID_Session, 0); 
+	else if (dwReturnMask & SCRIPTINFO_IUNKNOWN) {
+	    IDispatch_QueryInterface((IDispatch *)This->session, &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) stub\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) stub\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) stub\n", This, iface);
+    return S_OK;
+}
+
+static HRESULT WINAPI MsiActiveScriptSite_OnLeaveScript(IActiveScriptSite* iface)
+{
+    MsiActiveScriptSite *This = (MsiActiveScriptSite *)iface;
+    TRACE("(%p/%p) stub\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    
+};
+
-- 
1.4.1



More information about the wine-patches mailing list