msi/tests: automation: Test invoking methods as properties & vice versa.
[wine] / dlls / msi / automation.c
1 /*
2  * Implementation of OLE Automation for Microsoft Installer (msi.dll)
3  *
4  * Copyright 2007 Misha Koshelev
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define COBJMACROS
22
23 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winerror.h"
27 #include "winuser.h"
28 #include "winreg.h"
29 #include "msidefs.h"
30 #include "msipriv.h"
31 #include "activscp.h"
32 #include "oleauto.h"
33 #include "wine/debug.h"
34 #include "wine/unicode.h"
35
36 #include "msiserver.h"
37 #include "msiserver_dispids.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(msi);
40
41 /*
42  * AutomationObject - "base" class for all automation objects. For each interface, we implement Invoke function
43  *                    called from AutomationObject::Invoke, and pass this function to create_automation_object.
44  */
45
46 typedef interface AutomationObject AutomationObject;
47
48 interface AutomationObject {
49     /*
50      * VTables - We provide IDispatch, IProvideClassInfo, IProvideClassInfo2, IProvideMultipleClassInfo
51      */
52     const IDispatchVtbl *lpVtbl;
53     const IProvideMultipleClassInfoVtbl *lpvtblIProvideMultipleClassInfo;
54
55     /* Object reference count */
56     LONG ref;
57
58     /* Clsid for this class and it's appropriate ITypeInfo object */
59     LPCLSID clsid;
60     ITypeInfo *iTypeInfo;
61
62     /* The MSI handle of the current object */
63     MSIHANDLE msiHandle;
64
65     /* A function that is called from AutomationObject::Invoke, specific to this type of object. */
66     HRESULT (STDMETHODCALLTYPE *funcInvoke)(
67         AutomationObject* This,
68         DISPID dispIdMember,
69         REFIID riid,
70         LCID lcid,
71         WORD wFlags,
72         DISPPARAMS* pDispParams,
73         VARIANT* pVarResult,
74         EXCEPINFO* pExcepInfo,
75         UINT* puArgErr);
76
77     /* A function that is called from AutomationObject::Release when the object is being freed to free any private
78      * data structures (or NULL) */
79     void (STDMETHODCALLTYPE *funcFree)(AutomationObject* This);
80 };
81
82 /*
83  * Structures for additional data required by specific automation objects
84  */
85
86 typedef struct {
87     int iCount;
88     LPWSTR *pszStrings;
89 } StringListData;
90
91 typedef struct {
92     /* The parent Installer object */
93     IDispatch *pInstaller;
94 } SessionData;
95
96 /* VTables */
97 static const struct IDispatchVtbl AutomationObject_Vtbl;
98 static const struct IProvideMultipleClassInfoVtbl AutomationObject_IProvideMultipleClassInfo_Vtbl;
99
100 /* Load type info so we don't have to process GetIDsOfNames */
101 HRESULT load_type_info(IDispatch *iface, ITypeInfo **pptinfo, REFIID clsid, LCID lcid)
102 {
103     HRESULT hr;
104     LPTYPELIB pLib = NULL;
105     LPTYPEINFO pInfo = NULL;
106     static const WCHAR szMsiServer[] = {'m','s','i','s','e','r','v','e','r','.','t','l','b'};
107
108     TRACE("(%p)->(%s,%d)\n", iface, debugstr_guid(clsid), lcid);
109
110     /* Load registered type library */
111     hr = LoadRegTypeLib(&LIBID_WindowsInstaller, 1, 0, lcid, &pLib);
112     if (FAILED(hr)) {
113         hr = LoadTypeLib(szMsiServer, &pLib);
114         if (FAILED(hr)) {
115             ERR("Could not load msiserver.tlb\n");
116             return hr;
117         }
118     }
119
120     /* Get type information for object */
121     hr = ITypeLib_GetTypeInfoOfGuid(pLib, clsid, &pInfo);
122     ITypeLib_Release(pLib);
123     if (FAILED(hr)) {
124         ERR("Could not load ITypeInfo for %s\n", debugstr_guid(clsid));
125         return hr;
126     }
127     *pptinfo = pInfo;
128     return S_OK;
129 }
130
131 /* Create the automation object, placing the result in the pointer ppObj. The automation object is created
132  * with the appropriate clsid and invocation function. */
133 HRESULT create_automation_object(MSIHANDLE msiHandle, IUnknown *pUnkOuter, LPVOID *ppObj, REFIID clsid,
134             HRESULT (STDMETHODCALLTYPE *funcInvoke)(AutomationObject*,DISPID,REFIID,LCID,WORD,DISPPARAMS*,
135                                                     VARIANT*,EXCEPINFO*,UINT*),
136                                  void (STDMETHODCALLTYPE *funcFree)(AutomationObject*),
137                                  SIZE_T sizetPrivateData)
138 {
139     AutomationObject *object;
140     HRESULT hr;
141
142     TRACE("(%ld,%p,%p,%s,%p,%p,%ld)\n", (unsigned long)msiHandle, pUnkOuter, ppObj, debugstr_guid(clsid), funcInvoke, funcFree, sizetPrivateData);
143
144     if( pUnkOuter )
145         return CLASS_E_NOAGGREGATION;
146
147     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AutomationObject)+sizetPrivateData);
148
149     /* Set all the VTable references */
150     object->lpVtbl = &AutomationObject_Vtbl;
151     object->lpvtblIProvideMultipleClassInfo = &AutomationObject_IProvideMultipleClassInfo_Vtbl;
152     object->ref = 1;
153
154     /* Store data that was passed */
155     object->msiHandle = msiHandle;
156     object->clsid = (LPCLSID)clsid;
157     object->funcInvoke = funcInvoke;
158     object->funcFree = funcFree;
159
160     /* Load our TypeInfo so we don't have to process GetIDsOfNames */
161     object->iTypeInfo = NULL;
162     hr = load_type_info((IDispatch *)object, &object->iTypeInfo, clsid, 0x0);
163     if (FAILED(hr)) {
164         HeapFree(GetProcessHeap(), 0, object);
165         return hr;
166     }
167
168     *ppObj = object;
169
170     return S_OK;
171 }
172
173 /* Macros to get pointer to AutomationObject from the other VTables. */
174 static inline AutomationObject *obj_from_IProvideMultipleClassInfo( IProvideMultipleClassInfo *iface )
175 {
176     return (AutomationObject *)((char*)iface - FIELD_OFFSET(AutomationObject, lpvtblIProvideMultipleClassInfo));
177 }
178
179 /* Macro to get pointer to private object data */
180 static inline void *private_data( AutomationObject *This )
181 {
182     return This + 1;
183 }
184
185 /*
186  * AutomationObject methods
187  */
188
189 /*** IUnknown methods ***/
190 static HRESULT WINAPI AutomationObject_QueryInterface(IDispatch* iface, REFIID riid, void** ppvObject)
191 {
192     AutomationObject *This = (AutomationObject *)iface;
193
194     TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject);
195
196     if (ppvObject == NULL)
197       return E_INVALIDARG;
198
199     *ppvObject = 0;
200
201     if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IDispatch) || IsEqualGUID(riid, This->clsid))
202         *ppvObject = This;
203     else if (IsEqualGUID(riid, &IID_IProvideClassInfo) ||
204              IsEqualGUID(riid, &IID_IProvideClassInfo2) ||
205              IsEqualGUID(riid, &IID_IProvideMultipleClassInfo))
206         *ppvObject = &This->lpvtblIProvideMultipleClassInfo;
207     else
208     {
209         TRACE("() : asking for unsupported interface %s\n",debugstr_guid(riid));
210         return E_NOINTERFACE;
211     }
212
213     /*
214      * Query Interface always increases the reference count by one when it is
215      * successful
216      */
217     IClassFactory_AddRef(iface);
218
219     return S_OK;
220 }
221
222 static ULONG WINAPI AutomationObject_AddRef(IDispatch* iface)
223 {
224     AutomationObject *This = (AutomationObject *)iface;
225
226     TRACE("(%p/%p)\n", iface, This);
227
228     return InterlockedIncrement(&This->ref);
229 }
230
231 static ULONG WINAPI AutomationObject_Release(IDispatch* iface)
232 {
233     AutomationObject *This = (AutomationObject *)iface;
234     ULONG ref = InterlockedDecrement(&This->ref);
235
236     TRACE("(%p/%p)\n", iface, This);
237
238     if (!ref)
239     {
240         if (This->funcFree) This->funcFree(This);
241         MsiCloseHandle(This->msiHandle);
242         HeapFree(GetProcessHeap(), 0, This);
243     }
244
245     return ref;
246 }
247
248 /*** IDispatch methods ***/
249 static HRESULT WINAPI AutomationObject_GetTypeInfoCount(
250         IDispatch* iface,
251         UINT* pctinfo)
252 {
253     AutomationObject *This = (AutomationObject *)iface;
254
255     TRACE("(%p/%p)->(%p)\n", iface, This, pctinfo);
256     *pctinfo = 1;
257     return S_OK;
258 }
259
260 static HRESULT WINAPI AutomationObject_GetTypeInfo(
261         IDispatch* iface,
262         UINT iTInfo,
263         LCID lcid,
264         ITypeInfo** ppTInfo)
265 {
266     AutomationObject *This = (AutomationObject *)iface;
267     TRACE("(%p/%p)->(%d,%d,%p)\n", iface, This, iTInfo, lcid, ppTInfo);
268
269     ITypeInfo_AddRef(This->iTypeInfo);
270     *ppTInfo = This->iTypeInfo;
271     return S_OK;
272 }
273
274 static HRESULT WINAPI AutomationObject_GetIDsOfNames(
275         IDispatch* iface,
276         REFIID riid,
277         LPOLESTR* rgszNames,
278         UINT cNames,
279         LCID lcid,
280         DISPID* rgDispId)
281 {
282     AutomationObject *This = (AutomationObject *)iface;
283     HRESULT hr;
284     TRACE("(%p/%p)->(%p,%p,%d,%d,%p)\n", iface, This, riid, rgszNames, cNames, lcid, rgDispId);
285
286     if (!IsEqualGUID(riid, &IID_NULL)) return E_INVALIDARG;
287     hr = ITypeInfo_GetIDsOfNames(This->iTypeInfo, rgszNames, cNames, rgDispId);
288     if (hr == DISP_E_UNKNOWNNAME)
289     {
290         int idx;
291         for (idx=0; idx<cNames; idx++)
292         {
293             if (rgDispId[idx] == DISPID_UNKNOWN)
294                 FIXME("Unknown member %s, clsid %s\n", debugstr_w(rgszNames[idx]), debugstr_guid(This->clsid));
295         }
296     }
297     return hr;
298 }
299
300 /* Maximum number of allowed function parameters+1 */
301 #define MAX_FUNC_PARAMS 20
302
303 /* Some error checking is done here to simplify individual object function invocation */
304 static HRESULT WINAPI AutomationObject_Invoke(
305         IDispatch* iface,
306         DISPID dispIdMember,
307         REFIID riid,
308         LCID lcid,
309         WORD wFlags,
310         DISPPARAMS* pDispParams,
311         VARIANT* pVarResult,
312         EXCEPINFO* pExcepInfo,
313         UINT* puArgErr)
314 {
315     AutomationObject *This = (AutomationObject *)iface;
316     HRESULT hr;
317     unsigned int uArgErr;
318     VARIANT varResultDummy;
319     BSTR bstrName = NULL;
320
321     TRACE("(%p/%p)->(%d,%p,%d,%d,%p,%p,%p,%p)\n", iface, This, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
322
323     if (!IsEqualIID(riid, &IID_NULL))
324     {
325         ERR("riid was %s instead of IID_NULL\n", debugstr_guid(riid));
326         return DISP_E_UNKNOWNNAME;
327     }
328
329     if (wFlags & DISPATCH_PROPERTYGET && !pVarResult)
330     {
331         ERR("NULL pVarResult not allowed when DISPATCH_PROPERTYGET specified\n");
332         return DISP_E_PARAMNOTOPTIONAL;
333     }
334
335     /* This simplifies our individual object invocation functions */
336     if (puArgErr == NULL) puArgErr = &uArgErr;
337     if (pVarResult == NULL) pVarResult = &varResultDummy;
338
339     /* Assume return type is void unless determined otherwise */
340     VariantInit(pVarResult);
341
342     /* If we are tracing, we want to see the name of the member we are invoking */
343     if (TRACE_ON(msi))
344     {
345         ITypeInfo_GetDocumentation(This->iTypeInfo, dispIdMember, &bstrName, NULL, NULL, NULL);
346         TRACE("Method %d, %s\n", dispIdMember, debugstr_w(bstrName));
347     }
348
349     hr = This->funcInvoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
350
351     if (hr == DISP_E_MEMBERNOTFOUND) {
352         if (bstrName == NULL) ITypeInfo_GetDocumentation(This->iTypeInfo, dispIdMember, &bstrName, NULL, NULL, NULL);
353         FIXME("Method %d, %s wflags %d not implemented, clsid %s\n", dispIdMember, debugstr_w(bstrName), wFlags, debugstr_guid(This->clsid));
354     }
355     else if (pExcepInfo &&
356              (hr == DISP_E_PARAMNOTFOUND ||
357               hr == DISP_E_EXCEPTION)) {
358         static const WCHAR szComma[] = { ',',0 };
359         static WCHAR szExceptionSource[] = {'M','s','i',' ','A','P','I',' ','E','r','r','o','r',0};
360         WCHAR szExceptionDescription[MAX_PATH];
361         BSTR bstrParamNames[MAX_FUNC_PARAMS];
362         unsigned namesNo, i;
363         BOOL bFirst = TRUE;
364
365         if (FAILED(ITypeInfo_GetNames(This->iTypeInfo, dispIdMember, bstrParamNames,
366                                       MAX_FUNC_PARAMS, &namesNo)))
367         {
368             TRACE("Failed to retrieve names for dispIdMember %d\n", dispIdMember);
369         }
370         else
371         {
372             memset(szExceptionDescription, 0, sizeof(szExceptionDescription));
373             for (i=0; i<namesNo; i++)
374             {
375                 if (bFirst) bFirst = FALSE;
376                 else {
377                     lstrcpyW(&szExceptionDescription[lstrlenW(szExceptionDescription)], szComma);
378                 }
379                 lstrcpyW(&szExceptionDescription[lstrlenW(szExceptionDescription)], bstrParamNames[i]);
380                 SysFreeString(bstrParamNames[i]);
381             }
382
383             memset(pExcepInfo, 0, sizeof(EXCEPINFO));
384             pExcepInfo->wCode = 1000;
385             pExcepInfo->bstrSource = szExceptionSource;
386             pExcepInfo->bstrDescription = szExceptionDescription;
387             hr = DISP_E_EXCEPTION;
388         }
389     }
390
391     /* Make sure we free the return variant if it is our dummy variant */
392     if (pVarResult == &varResultDummy) VariantClear(pVarResult);
393
394     /* Free function name if we retrieved it */
395     if (bstrName) SysFreeString(bstrName);
396
397     TRACE("Returning 0x%08x, %s\n", hr, SUCCEEDED(hr) ? "ok" : "not ok");
398
399     return hr;
400 }
401
402 static const struct IDispatchVtbl AutomationObject_Vtbl =
403 {
404     AutomationObject_QueryInterface,
405     AutomationObject_AddRef,
406     AutomationObject_Release,
407     AutomationObject_GetTypeInfoCount,
408     AutomationObject_GetTypeInfo,
409     AutomationObject_GetIDsOfNames,
410     AutomationObject_Invoke
411 };
412
413 /*
414  * IProvideMultipleClassInfo methods
415  */
416
417 static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_QueryInterface(
418   IProvideMultipleClassInfo* iface,
419   REFIID     riid,
420   VOID**     ppvoid)
421 {
422     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
423     return AutomationObject_QueryInterface((IDispatch *)This, riid, ppvoid);
424 }
425
426 static ULONG WINAPI AutomationObject_IProvideMultipleClassInfo_AddRef(IProvideMultipleClassInfo* iface)
427 {
428     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
429     return AutomationObject_AddRef((IDispatch *)This);
430 }
431
432 static ULONG WINAPI AutomationObject_IProvideMultipleClassInfo_Release(IProvideMultipleClassInfo* iface)
433 {
434     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
435     return AutomationObject_Release((IDispatch *)This);
436 }
437
438 static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_GetClassInfo(IProvideMultipleClassInfo* iface, ITypeInfo** ppTI)
439 {
440     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
441     TRACE("(%p/%p)->(%p)\n", iface, This, ppTI);
442     return load_type_info((IDispatch *)This, ppTI, This->clsid, 0);
443 }
444
445 static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_GetGUID(IProvideMultipleClassInfo* iface, DWORD dwGuidKind, GUID* pGUID)
446 {
447     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
448     TRACE("(%p/%p)->(%d,%s)\n", iface, This, dwGuidKind, debugstr_guid(pGUID));
449
450     if (dwGuidKind != GUIDKIND_DEFAULT_SOURCE_DISP_IID)
451         return E_INVALIDARG;
452     else {
453         *pGUID = *This->clsid;
454         return S_OK;
455     }
456 }
457
458 static HRESULT WINAPI AutomationObject_GetMultiTypeInfoCount(IProvideMultipleClassInfo* iface, ULONG* pcti)
459 {
460     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
461
462     TRACE("(%p/%p)->(%p)\n", iface, This, pcti);
463     *pcti = 1;
464     return S_OK;
465 }
466
467 static HRESULT WINAPI AutomationObject_GetInfoOfIndex(IProvideMultipleClassInfo* iface,
468         ULONG iti,
469         DWORD dwFlags,
470         ITypeInfo** pptiCoClass,
471         DWORD* pdwTIFlags,
472         ULONG* pcdispidReserved,
473         IID* piidPrimary,
474         IID* piidSource)
475 {
476     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
477
478     TRACE("(%p/%p)->(%d,%d,%p,%p,%p,%p,%p)\n", iface, This, iti, dwFlags, pptiCoClass, pdwTIFlags, pcdispidReserved, piidPrimary, piidSource);
479
480     if (iti != 0)
481         return E_INVALIDARG;
482
483     if (dwFlags & MULTICLASSINFO_GETTYPEINFO)
484         load_type_info((IDispatch *)This, pptiCoClass, This->clsid, 0);
485
486     if (dwFlags & MULTICLASSINFO_GETNUMRESERVEDDISPIDS)
487     {
488         *pdwTIFlags = 0;
489         *pcdispidReserved = 0;
490     }
491
492     if (dwFlags & MULTICLASSINFO_GETIIDPRIMARY){
493         *piidPrimary = *This->clsid;
494     }
495
496     if (dwFlags & MULTICLASSINFO_GETIIDSOURCE){
497         *piidSource = *This->clsid;
498     }
499
500     return S_OK;
501 }
502
503 static const IProvideMultipleClassInfoVtbl AutomationObject_IProvideMultipleClassInfo_Vtbl =
504 {
505     AutomationObject_IProvideMultipleClassInfo_QueryInterface,
506     AutomationObject_IProvideMultipleClassInfo_AddRef,
507     AutomationObject_IProvideMultipleClassInfo_Release,
508     AutomationObject_IProvideMultipleClassInfo_GetClassInfo,
509     AutomationObject_IProvideMultipleClassInfo_GetGUID,
510     AutomationObject_GetMultiTypeInfoCount,
511     AutomationObject_GetInfoOfIndex
512 };
513
514 /*
515  * Individual Object Invocation Functions
516  */
517
518 /* Helper function that copies a passed parameter instead of using VariantChangeType like the actual DispGetParam.
519    This function is only for VARIANT type parameters that have several types that cannot be properly discriminated
520    using DispGetParam/VariantChangeType. */
521 HRESULT WINAPI DispGetParam_CopyOnly(
522         DISPPARAMS *pdispparams, /* [in] Parameter list */
523         UINT        *position,    /* [in] Position of parameter to copy in pdispparams; on return will contain calculated position */
524         VARIANT    *pvarResult)  /* [out] Destination for resulting variant */
525 {
526     /* position is counted backwards */
527     UINT pos;
528
529     TRACE("position=%d, cArgs=%d, cNamedArgs=%d\n",
530           *position, pdispparams->cArgs, pdispparams->cNamedArgs);
531     if (*position < pdispparams->cArgs) {
532       /* positional arg? */
533       pos = pdispparams->cArgs - *position - 1;
534     } else {
535       /* FIXME: is this how to handle named args? */
536       for (pos=0; pos<pdispparams->cNamedArgs; pos++)
537         if (pdispparams->rgdispidNamedArgs[pos] == *position) break;
538
539       if (pos==pdispparams->cNamedArgs)
540         return DISP_E_PARAMNOTFOUND;
541     }
542     *position = pos;
543     return VariantCopyInd(pvarResult,
544                         &pdispparams->rgvarg[pos]);
545 }
546
547 static HRESULT WINAPI RecordImpl_Invoke(
548         AutomationObject* This,
549         DISPID dispIdMember,
550         REFIID riid,
551         LCID lcid,
552         WORD wFlags,
553         DISPPARAMS* pDispParams,
554         VARIANT* pVarResult,
555         EXCEPINFO* pExcepInfo,
556         UINT* puArgErr)
557 {
558     WCHAR *szString;
559     DWORD dwLen;
560     UINT ret;
561     VARIANTARG varg0, varg1;
562     HRESULT hr;
563
564     VariantInit(&varg0);
565     VariantInit(&varg1);
566
567     switch (dispIdMember)
568     {
569         case DISPID_RECORD_STRINGDATA:
570             if (wFlags & DISPATCH_PROPERTYGET) {
571                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
572                 if (FAILED(hr)) return hr;
573                 V_VT(pVarResult) = VT_BSTR;
574                 V_BSTR(pVarResult) = NULL;
575                 ret = MsiRecordGetStringW(This->msiHandle, V_I4(&varg0), NULL, &dwLen);
576                 if (ret == ERROR_SUCCESS)
577                 {
578                     szString = msi_alloc((++dwLen)*sizeof(WCHAR));
579                     if (szString)
580                     {
581                         if ((ret = MsiRecordGetStringW(This->msiHandle, V_I4(&varg0), szString, &dwLen)) == ERROR_SUCCESS)
582                             V_BSTR(pVarResult) = SysAllocString(szString);
583                         msi_free(szString);
584                     }
585                 }
586                 if (ret != ERROR_SUCCESS)
587                     ERR("MsiRecordGetString returned %d\n", ret);
588             } else if (wFlags & DISPATCH_PROPERTYPUT) {
589                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
590                 if (FAILED(hr)) return hr;
591                 hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_BSTR, &varg1, puArgErr);
592                 if (FAILED(hr)) return hr;
593                 if ((ret = MsiRecordSetStringW(This->msiHandle, V_I4(&varg0), V_BSTR(&varg1))) != ERROR_SUCCESS)
594                 {
595                     VariantClear(&varg1);
596                     ERR("MsiRecordSetString returned %d\n", ret);
597                     return DISP_E_EXCEPTION;
598                 }
599             }
600             break;
601
602          default:
603             return DISP_E_MEMBERNOTFOUND;
604     }
605
606     VariantClear(&varg1);
607     VariantClear(&varg0);
608
609     return S_OK;
610 }
611
612 static HRESULT WINAPI StringListImpl_Invoke(
613         AutomationObject* This,
614         DISPID dispIdMember,
615         REFIID riid,
616         LCID lcid,
617         WORD wFlags,
618         DISPPARAMS* pDispParams,
619         VARIANT* pVarResult,
620         EXCEPINFO* pExcepInfo,
621         UINT* puArgErr)
622 {
623     StringListData *data = (StringListData *)private_data(This);
624     HRESULT hr;
625     VARIANTARG varg0;
626
627     VariantInit(&varg0);
628
629     switch (dispIdMember)
630     {
631          case DISPID_STRINGLIST_ITEM:
632             if (wFlags & DISPATCH_PROPERTYGET) {
633                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
634                 if (FAILED(hr)) return hr;
635                 if (V_I4(&varg0) < 0 || V_I4(&varg0) >= data->iCount)
636                     return DISP_E_BADINDEX;
637                 V_VT(pVarResult) = VT_BSTR;
638                 V_BSTR(pVarResult) = SysAllocString(data->pszStrings[V_I4(&varg0)]);
639             }
640             break;
641
642          case DISPID_STRINGLIST_COUNT:
643             if (wFlags & DISPATCH_PROPERTYGET) {
644                 V_VT(pVarResult) = VT_I4;
645                 V_I4(pVarResult) = data->iCount;
646             }
647             break;
648
649          default:
650             return DISP_E_MEMBERNOTFOUND;
651     }
652
653     VariantClear(&varg0);
654
655     return S_OK;
656 }
657
658 static void WINAPI StringListImpl_Free(AutomationObject *This)
659 {
660     StringListData *data = private_data(This);
661     int idx;
662
663     for (idx=0; idx<data->iCount; idx++)
664         SysFreeString(data->pszStrings[idx]);
665     HeapFree(GetProcessHeap(), 0, data->pszStrings);
666 }
667
668 static HRESULT WINAPI ViewImpl_Invoke(
669         AutomationObject* This,
670         DISPID dispIdMember,
671         REFIID riid,
672         LCID lcid,
673         WORD wFlags,
674         DISPPARAMS* pDispParams,
675         VARIANT* pVarResult,
676         EXCEPINFO* pExcepInfo,
677         UINT* puArgErr)
678 {
679     MSIHANDLE msiHandle;
680     IDispatch *pDispatch = NULL;
681     UINT ret;
682     VARIANTARG varg0, varg1;
683     HRESULT hr;
684
685     VariantInit(&varg0);
686     VariantInit(&varg1);
687
688     switch (dispIdMember)
689     {
690         case DISPID_VIEW_EXECUTE:
691             if (wFlags & DISPATCH_METHOD)
692             {
693                 hr = DispGetParam(pDispParams, 0, VT_DISPATCH, &varg0, puArgErr);
694                 if (SUCCEEDED(hr) && V_DISPATCH(&varg0) != NULL)
695                     MsiViewExecute(This->msiHandle, ((AutomationObject *)V_DISPATCH(&varg0))->msiHandle);
696                 else
697                     MsiViewExecute(This->msiHandle, 0);
698             }
699             break;
700
701         case DISPID_VIEW_FETCH:
702             if (wFlags & DISPATCH_METHOD)
703             {
704                 V_VT(pVarResult) = VT_DISPATCH;
705                 if ((ret = MsiViewFetch(This->msiHandle, &msiHandle)) == ERROR_SUCCESS)
706                 {
707                     if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_Record, RecordImpl_Invoke, NULL, 0)))
708                     {
709                         IDispatch_AddRef(pDispatch);
710                         V_DISPATCH(pVarResult) = pDispatch;
711                     }
712                     else
713                         ERR("Failed to create Record object, hresult 0x%08x\n", hr);
714                 }
715                 else if (ret == ERROR_NO_MORE_ITEMS)
716                     V_DISPATCH(pVarResult) = NULL;
717                 else
718                 {
719                     ERR("MsiViewFetch returned %d\n", ret);
720                     return DISP_E_EXCEPTION;
721                 }
722             }
723             break;
724
725         case DISPID_VIEW_CLOSE:
726             if (wFlags & DISPATCH_METHOD)
727             {
728                 MsiViewClose(This->msiHandle);
729             }
730             break;
731
732          default:
733             return DISP_E_MEMBERNOTFOUND;
734     }
735
736     VariantClear(&varg1);
737     VariantClear(&varg0);
738
739     return S_OK;
740 }
741
742 static HRESULT WINAPI DatabaseImpl_Invoke(
743         AutomationObject* This,
744         DISPID dispIdMember,
745         REFIID riid,
746         LCID lcid,
747         WORD wFlags,
748         DISPPARAMS* pDispParams,
749         VARIANT* pVarResult,
750         EXCEPINFO* pExcepInfo,
751         UINT* puArgErr)
752 {
753     MSIHANDLE msiHandle;
754     IDispatch *pDispatch = NULL;
755     UINT ret;
756     VARIANTARG varg0, varg1;
757     HRESULT hr;
758
759     VariantInit(&varg0);
760     VariantInit(&varg1);
761
762     switch (dispIdMember)
763     {
764         case DISPID_DATABASE_OPENVIEW:
765             if (wFlags & DISPATCH_METHOD)
766             {
767                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
768                 if (FAILED(hr)) return hr;
769                 V_VT(pVarResult) = VT_DISPATCH;
770                 if ((ret = MsiDatabaseOpenViewW(This->msiHandle, V_BSTR(&varg0), &msiHandle)) == ERROR_SUCCESS)
771                 {
772                     if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_View, ViewImpl_Invoke, NULL, 0)))
773                     {
774                         IDispatch_AddRef(pDispatch);
775                         V_DISPATCH(pVarResult) = pDispatch;
776                     }
777                     else
778                         ERR("Failed to create View object, hresult 0x%08x\n", hr);
779                 }
780                 else
781                 {
782                     VariantClear(&varg0);
783                     ERR("MsiDatabaseOpenView returned %d\n", ret);
784                     return DISP_E_EXCEPTION;
785                 }
786             }
787             break;
788
789          default:
790             return DISP_E_MEMBERNOTFOUND;
791     }
792
793     VariantClear(&varg1);
794     VariantClear(&varg0);
795
796     return S_OK;
797 }
798
799 static HRESULT WINAPI SessionImpl_Invoke(
800         AutomationObject* This,
801         DISPID dispIdMember,
802         REFIID riid,
803         LCID lcid,
804         WORD wFlags,
805         DISPPARAMS* pDispParams,
806         VARIANT* pVarResult,
807         EXCEPINFO* pExcepInfo,
808         UINT* puArgErr)
809 {
810     SessionData *data = private_data(This);
811     WCHAR *szString;
812     DWORD dwLen;
813     IDispatch *pDispatch = NULL;
814     MSIHANDLE msiHandle;
815     LANGID langId;
816     UINT ret;
817     INSTALLSTATE iInstalled, iAction;
818     VARIANTARG varg0, varg1;
819     HRESULT hr;
820
821     VariantInit(&varg0);
822     VariantInit(&varg1);
823
824     switch (dispIdMember)
825     {
826         case DISPID_SESSION_INSTALLER:
827             if (wFlags & DISPATCH_PROPERTYGET) {
828                 V_VT(pVarResult) = VT_DISPATCH;
829                 IDispatch_AddRef(data->pInstaller);
830                 V_DISPATCH(pVarResult) = data->pInstaller;
831             }
832             break;
833
834         case DISPID_SESSION_PROPERTY:
835             if (wFlags & DISPATCH_PROPERTYGET) {
836                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
837                 if (FAILED(hr)) return hr;
838                 V_VT(pVarResult) = VT_BSTR;
839                 V_BSTR(pVarResult) = NULL;
840                 ret = MsiGetPropertyW(This->msiHandle, V_BSTR(&varg0), NULL, &dwLen);
841                 if (ret == ERROR_SUCCESS)
842                 {
843                     szString = msi_alloc((++dwLen)*sizeof(WCHAR));
844                     if (szString)
845                     {
846                         if ((ret = MsiGetPropertyW(This->msiHandle, V_BSTR(&varg0), szString, &dwLen)) == ERROR_SUCCESS)
847                             V_BSTR(pVarResult) = SysAllocString(szString);
848                         msi_free(szString);
849                     }
850                 }
851                 if (ret != ERROR_SUCCESS)
852                     ERR("MsiGetProperty returned %d\n", ret);
853             } else if (wFlags & DISPATCH_PROPERTYPUT) {
854                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
855                 if (FAILED(hr)) return hr;
856                 hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_BSTR, &varg1, puArgErr);
857                 if (FAILED(hr)) {
858                     VariantClear(&varg0);
859                     return hr;
860                 }
861                 if ((ret = MsiSetPropertyW(This->msiHandle, V_BSTR(&varg0), V_BSTR(&varg1))) != ERROR_SUCCESS)
862                 {
863                     VariantClear(&varg0);
864                     VariantClear(&varg1);
865                     ERR("MsiSetProperty returned %d\n", ret);
866                     return DISP_E_EXCEPTION;
867                 }
868             }
869             break;
870
871         case DISPID_SESSION_LANGUAGE:
872             if (wFlags & DISPATCH_PROPERTYGET) {
873                 langId = MsiGetLanguage(This->msiHandle);
874                 V_VT(pVarResult) = VT_I4;
875                 V_I4(pVarResult) = langId;
876             }
877             break;
878
879         case DISPID_SESSION_MODE:
880             if (wFlags & DISPATCH_PROPERTYGET) {
881                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
882                 if (FAILED(hr)) return hr;
883                 V_VT(pVarResult) = VT_BOOL;
884                 V_BOOL(pVarResult) = MsiGetMode(This->msiHandle, V_I4(&varg0));
885             } else if (wFlags & DISPATCH_PROPERTYPUT) {
886                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
887                 if (FAILED(hr)) return hr;
888                 hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_BOOL, &varg1, puArgErr);
889                 if (FAILED(hr)) return hr;
890                 if ((ret = MsiSetMode(This->msiHandle, V_I4(&varg0), V_BOOL(&varg1))) != ERROR_SUCCESS)
891                 {
892                     ERR("MsiSetMode returned %d\n", ret);
893                     return DISP_E_EXCEPTION;
894                 }
895             }
896             break;
897
898         case DISPID_SESSION_DATABASE:
899             if (wFlags & DISPATCH_PROPERTYGET) {
900                 V_VT(pVarResult) = VT_DISPATCH;
901                 if ((msiHandle = MsiGetActiveDatabase(This->msiHandle)))
902                 {
903                     if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_Database, DatabaseImpl_Invoke, NULL, 0)))
904                     {
905                         IDispatch_AddRef(pDispatch);
906                         V_DISPATCH(pVarResult) = pDispatch;
907                     }
908                     else
909                         ERR("Failed to create Database object, hresult 0x%08x\n", hr);
910                 }
911                 else
912                 {
913                     ERR("MsiGetActiveDatabase failed\n");
914                     return DISP_E_EXCEPTION;
915                 }
916             }
917             break;
918
919         case DISPID_SESSION_DOACTION:
920             if (wFlags & DISPATCH_METHOD) {
921                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
922                 if (FAILED(hr)) return hr;
923                 ret = MsiDoActionW(This->msiHandle, V_BSTR(&varg0));
924                 V_VT(pVarResult) = VT_I4;
925                 switch (ret)
926                 {
927                     case ERROR_FUNCTION_NOT_CALLED:
928                         V_I4(pVarResult) = msiDoActionStatusNoAction;
929                         break;
930                     case ERROR_SUCCESS:
931                         V_I4(pVarResult) = msiDoActionStatusSuccess;
932                         break;
933                     case ERROR_INSTALL_USEREXIT:
934                         V_I4(pVarResult) = msiDoActionStatusUserExit;
935                         break;
936                     case ERROR_INSTALL_FAILURE:
937                         V_I4(pVarResult) = msiDoActionStatusFailure;
938                         break;
939                     case ERROR_INSTALL_SUSPEND:
940                         V_I4(pVarResult) = msiDoActionStatusSuspend;
941                         break;
942                     case ERROR_MORE_DATA:
943                         V_I4(pVarResult) = msiDoActionStatusFinished;
944                         break;
945                     case ERROR_INVALID_HANDLE_STATE:
946                         V_I4(pVarResult) = msiDoActionStatusWrongState;
947                         break;
948                     case ERROR_INVALID_DATA:
949                         V_I4(pVarResult) = msiDoActionStatusBadActionData;
950                         break;
951                     default:
952                         VariantClear(&varg0);
953                         FIXME("MsiDoAction returned unhandled value %d\n", ret);
954                         return DISP_E_EXCEPTION;
955                 }
956             }
957             break;
958
959         case DISPID_SESSION_EVALUATECONDITION:
960             if (wFlags & DISPATCH_METHOD) {
961                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
962                 if (FAILED(hr)) return hr;
963                 V_VT(pVarResult) = VT_I4;
964                 V_I4(pVarResult) = MsiEvaluateConditionW(This->msiHandle, V_BSTR(&varg0));
965             }
966             break;
967
968         case DISPID_SESSION_SETINSTALLLEVEL:
969             hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
970             if (FAILED(hr)) return hr;
971             if ((ret = MsiSetInstallLevel(This->msiHandle, V_I4(&varg0))) != ERROR_SUCCESS)
972             {
973                 ERR("MsiSetInstallLevel returned %d\n", ret);
974                 return DISP_E_EXCEPTION;
975             }
976             break;
977
978         case DISPID_SESSION_FEATURECURRENTSTATE:
979             if (wFlags & DISPATCH_PROPERTYGET) {
980                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
981                 if (FAILED(hr)) return hr;
982                 V_VT(pVarResult) = VT_I4;
983                 if ((ret = MsiGetFeatureStateW(This->msiHandle, V_BSTR(&varg0), &iInstalled, &iAction)) == ERROR_SUCCESS)
984                     V_I4(pVarResult) = iInstalled;
985                 else
986                 {
987                     ERR("MsiGetFeatureState returned %d\n", ret);
988                     V_I4(pVarResult) = msiInstallStateUnknown;
989                 }
990             }
991             break;
992
993         case DISPID_SESSION_FEATUREREQUESTSTATE:
994             if (wFlags & DISPATCH_PROPERTYGET) {
995                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
996                 if (FAILED(hr)) return hr;
997                 V_VT(pVarResult) = VT_I4;
998                 if ((ret = MsiGetFeatureStateW(This->msiHandle, V_BSTR(&varg0), &iInstalled, &iAction)) == ERROR_SUCCESS)
999                     V_I4(pVarResult) = iAction;
1000                 else
1001                 {
1002                     ERR("MsiGetFeatureState returned %d\n", ret);
1003                     V_I4(pVarResult) = msiInstallStateUnknown;
1004                 }
1005             } else if (wFlags & DISPATCH_PROPERTYPUT) {
1006                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1007                 if (FAILED(hr)) return hr;
1008                 hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_I4, &varg1, puArgErr);
1009                 if (FAILED(hr)) {
1010                     VariantClear(&varg0);
1011                     return hr;
1012                 }
1013                 if ((ret = MsiSetFeatureStateW(This->msiHandle, V_BSTR(&varg0), V_I4(&varg1))) != ERROR_SUCCESS)
1014                 {
1015                     VariantClear(&varg0);
1016                     ERR("MsiSetFeatureState returned %d\n", ret);
1017                     return DISP_E_EXCEPTION;
1018                 }
1019             }
1020             break;
1021
1022          default:
1023             return DISP_E_MEMBERNOTFOUND;
1024     }
1025
1026     VariantClear(&varg1);
1027     VariantClear(&varg0);
1028
1029     return S_OK;
1030 }
1031
1032 /* Fill the variant pointed to by pVarResult with the value & size returned by RegQueryValueEx as dictated by the
1033  * registry value type. Used by Installer::RegistryValue. */
1034 static void variant_from_registry_value(VARIANT *pVarResult, DWORD dwType, LPBYTE lpData, DWORD dwSize)
1035 {
1036     static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 };
1037     static const WCHAR szREG_[] = { '(','R','E','G','_',']',0 };
1038     WCHAR *szString = (WCHAR *)lpData;
1039     LPWSTR szNewString = NULL;
1040     DWORD dwNewSize = 0;
1041     int idx;
1042
1043     switch (dwType)
1044     {
1045         /* Registry strings may not be null terminated so we must use SysAllocStringByteLen/Len */
1046         case REG_MULTI_SZ: /* Multi SZ change internal null characters to newlines */
1047             idx = (dwSize/sizeof(WCHAR))-1;
1048             while (idx >= 0 && !szString[idx]) idx--;
1049             for (; idx >= 0; idx--)
1050                 if (!szString[idx]) szString[idx] = '\n';
1051         case REG_SZ:
1052             V_VT(pVarResult) = VT_BSTR;
1053             V_BSTR(pVarResult) = SysAllocStringByteLen((LPCSTR)szString, dwSize);
1054             break;
1055
1056         case REG_EXPAND_SZ:
1057             if (!(dwNewSize = ExpandEnvironmentStringsW(szString, szNewString, dwNewSize)))
1058                 ERR("ExpandEnvironmentStrings returned error %d\n", GetLastError());
1059             else if (!(szNewString = msi_alloc(dwNewSize)))
1060                 ERR("Out of memory\n");
1061             else if (!(dwNewSize = ExpandEnvironmentStringsW(szString, szNewString, dwNewSize)))
1062                 ERR("ExpandEnvironmentStrings returned error %d\n", GetLastError());
1063             else
1064             {
1065                 V_VT(pVarResult) = VT_BSTR;
1066                 V_BSTR(pVarResult) = SysAllocStringLen(szNewString, dwNewSize);
1067             }
1068             msi_free(szNewString);
1069             break;
1070
1071         case REG_DWORD:
1072             V_VT(pVarResult) = VT_I4;
1073             V_I4(pVarResult) = *((DWORD *)lpData);
1074             break;
1075
1076         case REG_QWORD:
1077             V_VT(pVarResult) = VT_BSTR;
1078             V_BSTR(pVarResult) = SysAllocString(szREG_);   /* Weird string, don't know why native returns it */
1079             break;
1080
1081         case REG_BINARY:
1082             V_VT(pVarResult) = VT_BSTR;
1083             V_BSTR(pVarResult) = SysAllocString(szREG_BINARY);
1084             break;
1085
1086         case REG_NONE:
1087             V_VT(pVarResult) = VT_EMPTY;
1088             break;
1089
1090         default:
1091             FIXME("Unhandled registry value type %d\n", dwType);
1092     }
1093 }
1094
1095 static HRESULT WINAPI InstallerImpl_Invoke(
1096         AutomationObject* This,
1097         DISPID dispIdMember,
1098         REFIID riid,
1099         LCID lcid,
1100         WORD wFlags,
1101         DISPPARAMS* pDispParams,
1102         VARIANT* pVarResult,
1103         EXCEPINFO* pExcepInfo,
1104         UINT* puArgErr)
1105 {
1106     MSIHANDLE msiHandle;
1107     IDispatch *pDispatch = NULL;
1108     UINT ret;
1109     VARIANTARG varg0, varg1, varg2;
1110     HRESULT hr;
1111
1112     VariantInit(&varg0);
1113     VariantInit(&varg1);
1114     VariantInit(&varg2);
1115
1116     switch (dispIdMember)
1117     {
1118         case DISPID_INSTALLER_CREATERECORD:
1119             if (wFlags & DISPATCH_METHOD)
1120             {
1121                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1122                 if (FAILED(hr)) return hr;
1123                 V_VT(pVarResult) = VT_DISPATCH;
1124                 if ((msiHandle = MsiCreateRecord(V_I4(&varg0))))
1125                 {
1126                     if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_Record, RecordImpl_Invoke, NULL, 0)))
1127                     {
1128                         IDispatch_AddRef(pDispatch);
1129                         V_DISPATCH(pVarResult) = pDispatch;
1130                     }
1131                     else
1132                         ERR("Failed to create Record object, hresult 0x%08x\n", hr);
1133                 }
1134                 else
1135                 {
1136                     ERR("MsiCreateRecord failed\n");
1137                     return DISP_E_EXCEPTION;
1138                 }
1139             }
1140             break;
1141
1142         case DISPID_INSTALLER_OPENPACKAGE:
1143             if (wFlags & DISPATCH_METHOD)
1144             {
1145                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1146                 if (FAILED(hr)) return hr;
1147                 hr = DispGetParam(pDispParams, 1, VT_I4, &varg1, puArgErr);
1148                 if (FAILED(hr))
1149                 {
1150                     VariantClear(&varg0);
1151                     return hr;
1152                 }
1153                 V_VT(pVarResult) = VT_DISPATCH;
1154                 if ((ret = MsiOpenPackageExW(V_BSTR(&varg0), V_I4(&varg1), &msiHandle)) == ERROR_SUCCESS)
1155                 {
1156                     if (SUCCEEDED(hr = create_session(msiHandle, (IDispatch *)This, &pDispatch)))
1157                     {
1158                         IDispatch_AddRef(pDispatch);
1159                         V_DISPATCH(pVarResult) = pDispatch;
1160                     }
1161                     else
1162                         ERR("Failed to create Session object, hresult 0x%08x\n", hr);
1163                 }
1164                 else
1165                 {
1166                     VariantClear(&varg0);
1167                     ERR("MsiOpenPackageEx returned %d\n", ret);
1168                     return DISP_E_EXCEPTION;
1169                 }
1170             }
1171             break;
1172
1173         case DISPID_INSTALLER_REGISTRYVALUE:
1174             if (wFlags & DISPATCH_METHOD) {
1175                 HKEY hkey;
1176                 LPWSTR szString = NULL;
1177                 DWORD dwSize = 0, dwType;
1178                 UINT posValue = 2;    /* Save valuePos so we can save puArgErr if we are unable to do our type conversions */
1179
1180                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1181                 if (FAILED(hr)) return hr;
1182                 hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr);
1183                 if (FAILED(hr)) return hr;
1184                 hr = DispGetParam_CopyOnly(pDispParams, &posValue, &varg2);
1185                 if (FAILED(hr))
1186                 {
1187                     VariantClear(&varg1);
1188                     return hr;
1189                 }
1190                 ret = RegOpenKeyW((HKEY)V_I4(&varg0), V_BSTR(&varg1), &hkey);
1191
1192                 /* Third parameter can be VT_EMPTY, VT_I4, or VT_BSTR */
1193                 switch (V_VT(&varg2))
1194                 {
1195                     case VT_EMPTY:  /* Return VT_BOOL as to whether or not registry key exists */
1196                         V_VT(pVarResult) = VT_BOOL;
1197                         V_BOOL(pVarResult) = (ret == ERROR_SUCCESS);
1198                         break;
1199
1200                     case VT_BSTR:   /* Return value of specified key if it exists */
1201                         if (ret == ERROR_SUCCESS &&
1202                             (ret = RegQueryValueExW(hkey, V_BSTR(&varg2), NULL, NULL, NULL, &dwSize)) == ERROR_SUCCESS)
1203                         {
1204                             if (!(szString = msi_alloc(dwSize)))
1205                                 ERR("Out of memory\n");
1206                             else if ((ret = RegQueryValueExW(hkey, V_BSTR(&varg2), NULL, &dwType, (LPBYTE)szString, &dwSize)) == ERROR_SUCCESS)
1207                                 variant_from_registry_value(pVarResult, dwType, (LPBYTE)szString, dwSize);
1208                         }
1209
1210                         if (ret != ERROR_SUCCESS)
1211                         {
1212                             msi_free(szString);
1213                             VariantClear(&varg2);
1214                             VariantClear(&varg1);
1215                             return DISP_E_BADINDEX;
1216                         }
1217                         break;
1218
1219                     default:     /* Try to make it into VT_I4, can use VariantChangeType for this */
1220                         hr = VariantChangeType(&varg2, &varg2, 0, VT_I4);
1221                         if (SUCCEEDED(hr) && ret != ERROR_SUCCESS) hr = DISP_E_BADINDEX; /* Conversion fine, but couldn't find key */
1222                         if (FAILED(hr))
1223                         {
1224                             if (hr == DISP_E_TYPEMISMATCH) *puArgErr = posValue;
1225                             VariantClear(&varg2);   /* Unknown type, so let's clear it */
1226                             VariantClear(&varg1);
1227                             return hr;
1228                         }
1229
1230                         /* Retrieve class name or maximum value name or subkey name size */
1231                         if (!V_I4(&varg2))
1232                             ret = RegQueryInfoKeyW(hkey, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1233                         else if (V_I4(&varg2) > 0)
1234                             ret = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL);
1235                         else /* V_I4(&varg2) < 0 */
1236                             ret = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
1237
1238                         if (ret == ERROR_SUCCESS)
1239                         {
1240                             if (!(szString = msi_alloc(++dwSize * sizeof(WCHAR))))
1241                                 ERR("Out of memory\n");
1242                             else if (!V_I4(&varg2))
1243                                 ret = RegQueryInfoKeyW(hkey, szString, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1244                             else if (V_I4(&varg2) > 0)
1245                                 ret = RegEnumValueW(hkey, V_I4(&varg2)-1, szString, &dwSize, 0, 0, NULL, NULL);
1246                             else /* V_I4(&varg2) < 0 */
1247                                 ret = RegEnumKeyW(hkey, -1 - V_I4(&varg2), szString, dwSize);
1248
1249                             if (szString && ret == ERROR_SUCCESS)
1250                             {
1251                                 V_VT(pVarResult) = VT_BSTR;
1252                                 V_BSTR(pVarResult) = SysAllocString(szString);
1253                             }
1254                         }
1255                 }
1256
1257                 msi_free(szString);
1258                 RegCloseKey(hkey);
1259             }
1260             break;
1261
1262         case DISPID_INSTALLER_PRODUCTSTATE:
1263             if (wFlags & DISPATCH_PROPERTYGET) {
1264                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1265                 if (FAILED(hr)) return hr;
1266                 V_VT(pVarResult) = VT_I4;
1267                 V_I4(pVarResult) = MsiQueryProductStateW(V_BSTR(&varg0));
1268             }
1269             break;
1270
1271         case DISPID_INSTALLER_PRODUCTS:
1272             if (wFlags & DISPATCH_PROPERTYGET)
1273             {
1274                 StringListData *sldata = NULL;
1275                 int idx = 0;
1276                 WCHAR szProductBuf[GUID_SIZE];
1277
1278                 /* Find number of products */
1279                 do {
1280                     ret = MsiEnumProductsW(idx, szProductBuf);
1281                     if (ret == ERROR_SUCCESS) idx++;
1282                 } while (ret == ERROR_SUCCESS && ret != ERROR_NO_MORE_ITEMS);
1283
1284                 if (ret != ERROR_SUCCESS && ret != ERROR_NO_MORE_ITEMS)
1285                 {
1286                     ERR("MsiEnumProducts returned %d\n", ret);
1287                     return DISP_E_EXCEPTION;
1288                 }
1289
1290                 V_VT(pVarResult) = VT_DISPATCH;
1291                 if (SUCCEEDED(hr = create_automation_object(0, NULL, (LPVOID*)&pDispatch, &DIID_StringList, StringListImpl_Invoke, StringListImpl_Free, sizeof(StringListData))))
1292                 {
1293                     IDispatch_AddRef(pDispatch);
1294                     V_DISPATCH(pVarResult) = pDispatch;
1295
1296                     /* Save product strings */
1297                     sldata = (StringListData *)private_data((AutomationObject *)pDispatch);
1298                     sldata->iCount = idx;
1299                     sldata->pszStrings = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LPWSTR)*sldata->iCount);
1300                     for (idx = 0; idx < sldata->iCount; idx++)
1301                     {
1302                         ret = MsiEnumProductsW(idx, szProductBuf);
1303                         sldata->pszStrings[idx] = SysAllocString(szProductBuf);
1304                     }
1305                 }
1306                 else
1307                     ERR("Failed to create StringList object, hresult 0x%08x\n", hr);
1308             }
1309             break;
1310
1311          default:
1312             return DISP_E_MEMBERNOTFOUND;
1313     }
1314
1315     VariantClear(&varg2);
1316     VariantClear(&varg1);
1317     VariantClear(&varg0);
1318
1319     return S_OK;
1320 }
1321
1322 /* Wrapper around create_automation_object to create an installer object. */
1323 HRESULT create_msiserver(IUnknown *pOuter, LPVOID *ppObj)
1324 {
1325     return create_automation_object(0, pOuter, ppObj, &DIID_Installer, InstallerImpl_Invoke, NULL, 0);
1326 }
1327
1328 /* Wrapper around create_automation_object to create a session object. */
1329 HRESULT create_session(MSIHANDLE msiHandle, IDispatch *pInstaller, IDispatch **pDispatch)
1330 {
1331     HRESULT hr = create_automation_object(msiHandle, NULL, (LPVOID)pDispatch, &DIID_Session, SessionImpl_Invoke, NULL, sizeof(SessionData));
1332     if (SUCCEEDED(hr) && pDispatch && *pDispatch)
1333         ((SessionData *)private_data((AutomationObject *)*pDispatch))->pInstaller = pInstaller;
1334     return hr;
1335 }