From f04d238d950da30f8481d80e642136c1b2fc40fe Mon Sep 17 00:00:00 2001 From: Misha Koshelev Date: Fri, 23 Feb 2007 20:05:28 -0600 Subject: msi: Add full JScript/VBScript support and partial expandable OLE automation support. --- dlls/msi/Makefile.in | 4 dlls/msi/automation.c | 754 +++++++++++++++++++++++++++++++++++++++++++++++++ dlls/msi/automation.h | 138 +++++++++ dlls/msi/script.c | 448 +++++++++++++++++++++++++++++ 4 files changed, 1344 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..b1527c2 --- /dev/null +++ b/dlls/msi/automation.c @@ -0,0 +1,754 @@ +/* + * 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 +#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. + */ + +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 + +#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 +#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