Release 1.5.29.
[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 "shlwapi.h"
34 #include "wine/debug.h"
35 #include "wine/unicode.h"
36
37 #include "msiserver.h"
38 #include "msiserver_dispids.h"
39
40 WINE_DEFAULT_DEBUG_CHANNEL(msi);
41
42 #define REG_INDEX_CLASSES_ROOT 0
43 #define REG_INDEX_DYN_DATA 6
44
45 typedef struct AutomationObject AutomationObject;
46
47 /* function that is called from AutomationObject::Invoke, specific to this type of object */
48 typedef HRESULT (*auto_invoke_func)(AutomationObject* This,
49     DISPID dispIdMember, REFIID riid, LCID lcid, WORD flags, DISPPARAMS* pDispParams,
50     VARIANT* result, EXCEPINFO* ei, UINT* arg_err);
51 /* function that is called from AutomationObject::Release when the object is being freed
52    to free any private data structures (or NULL) */
53 typedef void (*auto_free_func)(AutomationObject* This);
54
55 typedef struct {
56     REFIID riid;
57     auto_invoke_func fn_invoke;
58     auto_free_func fn_free;
59 } tid_id_t;
60
61
62 static HRESULT database_invoke(AutomationObject*,DISPID,REFIID,LCID,WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*,UINT*);
63 static HRESULT installer_invoke(AutomationObject*,DISPID,REFIID,LCID,WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*,UINT*);
64 static HRESULT record_invoke(AutomationObject*,DISPID,REFIID,LCID,WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*,UINT*);
65 static HRESULT session_invoke(AutomationObject*,DISPID,REFIID,LCID,WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*,UINT*);
66 static HRESULT list_invoke(AutomationObject*,DISPID,REFIID,LCID,WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*,UINT*);
67 static void    list_free(AutomationObject*);
68 static HRESULT summaryinfo_invoke(AutomationObject*,DISPID,REFIID,LCID,WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*,UINT*);
69 static HRESULT view_invoke(AutomationObject*,DISPID,REFIID,LCID,WORD,DISPPARAMS*,VARIANT*,EXCEPINFO*,UINT*);
70
71 static tid_id_t tid_ids[] = {
72     { &DIID_Database,    database_invoke },
73     { &DIID_Installer,   installer_invoke },
74     { &DIID_Record,      record_invoke },
75     { &DIID_Session,     session_invoke },
76     { &DIID_StringList,  list_invoke, list_free },
77     { &DIID_SummaryInfo, summaryinfo_invoke },
78     { &DIID_View,        view_invoke }
79 };
80
81 static ITypeLib  *typelib;
82 static ITypeInfo *typeinfos[LAST_tid];
83
84 static const IID *get_riid_from_tid(tid_t tid)
85 {
86     return tid_ids[tid].riid;
87 }
88
89 HRESULT get_typeinfo(tid_t tid, ITypeInfo **typeinfo)
90 {
91     HRESULT hr;
92
93     if (!typelib)
94     {
95         ITypeLib *lib;
96
97         hr = LoadRegTypeLib(&LIBID_WindowsInstaller, 1, 0, LOCALE_NEUTRAL, &lib);
98         if (FAILED(hr)) {
99             static const WCHAR msiserverW[] = {'m','s','i','s','e','r','v','e','r','.','t','l','b',0};
100             hr = LoadTypeLib(msiserverW, &lib);
101             if (FAILED(hr)) {
102                 ERR("Could not load msiserver.tlb\n");
103                 return hr;
104             }
105         }
106
107         if (InterlockedCompareExchangePointer((void**)&typelib, lib, NULL))
108             ITypeLib_Release(lib);
109     }
110
111     if (!typeinfos[tid])
112     {
113         ITypeInfo *ti;
114
115         hr = ITypeLib_GetTypeInfoOfGuid(typelib, get_riid_from_tid(tid), &ti);
116         if (FAILED(hr)) {
117             ERR("Could not load ITypeInfo for %s\n", debugstr_guid(get_riid_from_tid(tid)));
118             return hr;
119         }
120
121         if(InterlockedCompareExchangePointer((void**)(typeinfos+tid), ti, NULL))
122             ITypeInfo_Release(ti);
123     }
124
125     *typeinfo = typeinfos[tid];
126     return S_OK;
127 }
128
129 void release_typelib(void)
130 {
131     unsigned i;
132
133     for (i = 0; i < sizeof(typeinfos)/sizeof(*typeinfos); i++)
134         if (typeinfos[i])
135             ITypeInfo_Release(typeinfos[i]);
136
137     if (typelib)
138         ITypeLib_Release(typelib);
139 }
140
141 /*
142  * AutomationObject - "base" class for all automation objects. For each interface, we implement Invoke function
143  *                    called from AutomationObject::Invoke.
144  */
145 struct AutomationObject {
146     IDispatch IDispatch_iface;
147     IProvideMultipleClassInfo IProvideMultipleClassInfo_iface;
148     LONG ref;
149
150     /* type id for this class */
151     tid_t tid;
152
153     /* The MSI handle of the current object */
154     MSIHANDLE msiHandle;
155 };
156
157 typedef struct {
158     AutomationObject autoobj;
159     int count;
160     VARIANT *data;
161 } ListObject;
162
163 static HRESULT create_database(MSIHANDLE, IDispatch**);
164 static HRESULT create_list_enumerator(ListObject*, void**);
165 static HRESULT create_summaryinfo(MSIHANDLE, IDispatch**);
166 static HRESULT create_view(MSIHANDLE, IDispatch**);
167
168 /* ListEnumerator - IEnumVARIANT implementation for MSI automation lists */
169 typedef struct {
170     IEnumVARIANT IEnumVARIANT_iface;
171     LONG ref;
172
173     /* Current position and pointer to AutomationObject that stores actual data */
174     ULONG pos;
175     ListObject *list;
176 } ListEnumerator;
177
178 typedef struct {
179     AutomationObject autoobj;
180     IDispatch *installer;
181 } SessionObject;
182
183 static inline AutomationObject *impl_from_IProvideMultipleClassInfo( IProvideMultipleClassInfo *iface )
184 {
185     return CONTAINING_RECORD(iface, AutomationObject, IProvideMultipleClassInfo_iface);
186 }
187
188 static inline AutomationObject *impl_from_IDispatch( IDispatch *iface )
189 {
190     return CONTAINING_RECORD(iface, AutomationObject, IDispatch_iface);
191 }
192
193 /* AutomationObject methods */
194 static HRESULT WINAPI AutomationObject_QueryInterface(IDispatch* iface, REFIID riid, void** ppvObject)
195 {
196     AutomationObject *This = impl_from_IDispatch(iface);
197
198     TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject);
199
200     if (ppvObject == NULL)
201       return E_INVALIDARG;
202
203     *ppvObject = 0;
204
205     if (IsEqualGUID(riid, &IID_IUnknown)  ||
206         IsEqualGUID(riid, &IID_IDispatch) ||
207         IsEqualGUID(riid, get_riid_from_tid(This->tid)))
208         *ppvObject = &This->IDispatch_iface;
209     else if (IsEqualGUID(riid, &IID_IProvideClassInfo) ||
210              IsEqualGUID(riid, &IID_IProvideClassInfo2) ||
211              IsEqualGUID(riid, &IID_IProvideMultipleClassInfo))
212         *ppvObject = &This->IProvideMultipleClassInfo_iface;
213     else
214     {
215         TRACE("() : asking for unsupported interface %s\n", debugstr_guid(riid));
216         return E_NOINTERFACE;
217     }
218
219     IDispatch_AddRef(iface);
220
221     return S_OK;
222 }
223
224 static ULONG WINAPI AutomationObject_AddRef(IDispatch* iface)
225 {
226     AutomationObject *This = impl_from_IDispatch(iface);
227
228     TRACE("(%p/%p)\n", iface, This);
229
230     return InterlockedIncrement(&This->ref);
231 }
232
233 static ULONG WINAPI AutomationObject_Release(IDispatch* iface)
234 {
235     AutomationObject *This = impl_from_IDispatch(iface);
236     ULONG ref = InterlockedDecrement(&This->ref);
237
238     TRACE("(%p/%p)\n", iface, This);
239
240     if (!ref)
241     {
242         if (tid_ids[This->tid].fn_free) tid_ids[This->tid].fn_free(This);
243         MsiCloseHandle(This->msiHandle);
244         msi_free(This);
245     }
246
247     return ref;
248 }
249
250 static HRESULT WINAPI AutomationObject_GetTypeInfoCount(
251         IDispatch* iface,
252         UINT* pctinfo)
253 {
254     AutomationObject *This = impl_from_IDispatch(iface);
255
256     TRACE("(%p/%p)->(%p)\n", iface, This, pctinfo);
257     *pctinfo = 1;
258     return S_OK;
259 }
260
261 static HRESULT WINAPI AutomationObject_GetTypeInfo(
262         IDispatch* iface,
263         UINT iTInfo,
264         LCID lcid,
265         ITypeInfo** ppTInfo)
266 {
267     AutomationObject *This = impl_from_IDispatch(iface);
268     HRESULT hr;
269
270     TRACE("(%p/%p)->(%d,%d,%p)\n", iface, This, iTInfo, lcid, ppTInfo);
271
272     hr = get_typeinfo(This->tid, ppTInfo);
273     if (FAILED(hr))
274         return hr;
275
276     ITypeInfo_AddRef(*ppTInfo);
277     return hr;
278 }
279
280 static HRESULT WINAPI AutomationObject_GetIDsOfNames(
281         IDispatch* iface,
282         REFIID riid,
283         LPOLESTR* rgszNames,
284         UINT cNames,
285         LCID lcid,
286         DISPID* rgDispId)
287 {
288     AutomationObject *This = impl_from_IDispatch(iface);
289     ITypeInfo *ti;
290     HRESULT hr;
291
292     TRACE("(%p/%p)->(%p,%p,%d,%d,%p)\n", iface, This, riid, rgszNames, cNames, lcid, rgDispId);
293
294     if (!IsEqualGUID(riid, &IID_NULL)) return E_INVALIDARG;
295
296     hr = get_typeinfo(This->tid, &ti);
297     if (FAILED(hr))
298         return hr;
299
300     hr = ITypeInfo_GetIDsOfNames(ti, rgszNames, cNames, rgDispId);
301     if (hr == DISP_E_UNKNOWNNAME)
302     {
303         UINT idx;
304         for (idx=0; idx<cNames; idx++)
305         {
306             if (rgDispId[idx] == DISPID_UNKNOWN)
307                 FIXME("Unknown member %s, clsid %s\n", debugstr_w(rgszNames[idx]), debugstr_guid(get_riid_from_tid(This->tid)));
308         }
309     }
310     return hr;
311 }
312
313 /* Maximum number of allowed function parameters+1 */
314 #define MAX_FUNC_PARAMS 20
315
316 /* Some error checking is done here to simplify individual object function invocation */
317 static HRESULT WINAPI AutomationObject_Invoke(
318         IDispatch* iface,
319         DISPID dispIdMember,
320         REFIID riid,
321         LCID lcid,
322         WORD wFlags,
323         DISPPARAMS* pDispParams,
324         VARIANT* pVarResult,
325         EXCEPINFO* pExcepInfo,
326         UINT* puArgErr)
327 {
328     AutomationObject *This = impl_from_IDispatch(iface);
329     HRESULT hr;
330     unsigned int uArgErr;
331     VARIANT varResultDummy;
332     BSTR bstrName = NULL;
333     ITypeInfo *ti;
334
335     TRACE("(%p/%p)->(%d,%p,%d,%d,%p,%p,%p,%p)\n", iface, This, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
336
337     if (!IsEqualIID(riid, &IID_NULL))
338     {
339         ERR("riid was %s instead of IID_NULL\n", debugstr_guid(riid));
340         return DISP_E_UNKNOWNNAME;
341     }
342
343     if (wFlags & DISPATCH_PROPERTYGET && !pVarResult)
344     {
345         ERR("NULL pVarResult not allowed when DISPATCH_PROPERTYGET specified\n");
346         return DISP_E_PARAMNOTOPTIONAL;
347     }
348
349     /* This simplifies our individual object invocation functions */
350     if (puArgErr == NULL) puArgErr = &uArgErr;
351     if (pVarResult == NULL) pVarResult = &varResultDummy;
352
353     hr = get_typeinfo(This->tid, &ti);
354     if (FAILED(hr))
355         return hr;
356
357     /* Assume return type is void unless determined otherwise */
358     VariantInit(pVarResult);
359
360     /* If we are tracing, we want to see the name of the member we are invoking */
361     if (TRACE_ON(msi))
362     {
363         ITypeInfo_GetDocumentation(ti, dispIdMember, &bstrName, NULL, NULL, NULL);
364         TRACE("Method %d, %s\n", dispIdMember, debugstr_w(bstrName));
365     }
366
367     hr = tid_ids[This->tid].fn_invoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
368
369     if (hr == DISP_E_MEMBERNOTFOUND) {
370         if (bstrName == NULL) ITypeInfo_GetDocumentation(ti, dispIdMember, &bstrName, NULL, NULL, NULL);
371         FIXME("Method %d, %s wflags %d not implemented, clsid %s\n", dispIdMember, debugstr_w(bstrName), wFlags,
372             debugstr_guid(get_riid_from_tid(This->tid)));
373     }
374     else if (pExcepInfo &&
375              (hr == DISP_E_PARAMNOTFOUND ||
376               hr == DISP_E_EXCEPTION)) {
377         static const WCHAR szComma[] = { ',',0 };
378         static const WCHAR szExceptionSource[] = {'M','s','i',' ','A','P','I',' ','E','r','r','o','r',0};
379         WCHAR szExceptionDescription[MAX_PATH];
380         BSTR bstrParamNames[MAX_FUNC_PARAMS];
381         unsigned namesNo, i;
382         BOOL bFirst = TRUE;
383
384         if (FAILED(ITypeInfo_GetNames(ti, dispIdMember, bstrParamNames,
385                                       MAX_FUNC_PARAMS, &namesNo)))
386         {
387             TRACE("Failed to retrieve names for dispIdMember %d\n", dispIdMember);
388         }
389         else
390         {
391             memset(szExceptionDescription, 0, sizeof(szExceptionDescription));
392             for (i=0; i<namesNo; i++)
393             {
394                 if (bFirst) bFirst = FALSE;
395                 else {
396                     lstrcpyW(&szExceptionDescription[lstrlenW(szExceptionDescription)], szComma);
397                 }
398                 lstrcpyW(&szExceptionDescription[lstrlenW(szExceptionDescription)], bstrParamNames[i]);
399                 SysFreeString(bstrParamNames[i]);
400             }
401
402             memset(pExcepInfo, 0, sizeof(EXCEPINFO));
403             pExcepInfo->wCode = 1000;
404             pExcepInfo->bstrSource = SysAllocString(szExceptionSource);
405             pExcepInfo->bstrDescription = SysAllocString(szExceptionDescription);
406             hr = DISP_E_EXCEPTION;
407         }
408     }
409
410     /* Make sure we free the return variant if it is our dummy variant */
411     if (pVarResult == &varResultDummy) VariantClear(pVarResult);
412
413     /* Free function name if we retrieved it */
414     SysFreeString(bstrName);
415
416     TRACE("Returning 0x%08x, %s\n", hr, SUCCEEDED(hr) ? "ok" : "not ok");
417
418     return hr;
419 }
420
421 static const struct IDispatchVtbl AutomationObjectVtbl =
422 {
423     AutomationObject_QueryInterface,
424     AutomationObject_AddRef,
425     AutomationObject_Release,
426     AutomationObject_GetTypeInfoCount,
427     AutomationObject_GetTypeInfo,
428     AutomationObject_GetIDsOfNames,
429     AutomationObject_Invoke
430 };
431
432 /*
433  * IProvideMultipleClassInfo methods
434  */
435
436 static HRESULT WINAPI ProvideMultipleClassInfo_QueryInterface(
437   IProvideMultipleClassInfo* iface,
438   REFIID     riid,
439   VOID**     ppvoid)
440 {
441     AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
442     return IDispatch_QueryInterface(&This->IDispatch_iface, riid, ppvoid);
443 }
444
445 static ULONG WINAPI ProvideMultipleClassInfo_AddRef(IProvideMultipleClassInfo* iface)
446 {
447     AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
448     return IDispatch_AddRef(&This->IDispatch_iface);
449 }
450
451 static ULONG WINAPI ProvideMultipleClassInfo_Release(IProvideMultipleClassInfo* iface)
452 {
453     AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
454     return IDispatch_Release(&This->IDispatch_iface);
455 }
456
457 static HRESULT WINAPI ProvideMultipleClassInfo_GetClassInfo(IProvideMultipleClassInfo* iface, ITypeInfo** ppTI)
458 {
459     AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
460     HRESULT hr;
461
462     TRACE("(%p/%p)->(%p)\n", iface, This, ppTI);
463
464     hr = get_typeinfo(This->tid, ppTI);
465     if (SUCCEEDED(hr))
466         ITypeInfo_AddRef(*ppTI);
467
468     return hr;
469 }
470
471 static HRESULT WINAPI ProvideMultipleClassInfo_GetGUID(IProvideMultipleClassInfo* iface, DWORD dwGuidKind, GUID* pGUID)
472 {
473     AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
474     TRACE("(%p/%p)->(%d,%s)\n", iface, This, dwGuidKind, debugstr_guid(pGUID));
475
476     if (dwGuidKind != GUIDKIND_DEFAULT_SOURCE_DISP_IID)
477         return E_INVALIDARG;
478     else {
479         *pGUID = *get_riid_from_tid(This->tid);
480         return S_OK;
481     }
482 }
483
484 static HRESULT WINAPI ProvideMultipleClassInfo_GetMultiTypeInfoCount(IProvideMultipleClassInfo* iface, ULONG* pcti)
485 {
486     AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
487
488     TRACE("(%p/%p)->(%p)\n", iface, This, pcti);
489     *pcti = 1;
490     return S_OK;
491 }
492
493 static HRESULT WINAPI ProvideMultipleClassInfo_GetInfoOfIndex(IProvideMultipleClassInfo* iface,
494         ULONG iti,
495         DWORD dwFlags,
496         ITypeInfo** ti,
497         DWORD* pdwTIFlags,
498         ULONG* pcdispidReserved,
499         IID* piidPrimary,
500         IID* piidSource)
501 {
502     AutomationObject *This = impl_from_IProvideMultipleClassInfo(iface);
503
504     TRACE("(%p/%p)->(%d,%d,%p,%p,%p,%p,%p)\n", iface, This, iti, dwFlags, ti, pdwTIFlags, pcdispidReserved, piidPrimary, piidSource);
505
506     if (iti != 0)
507         return E_INVALIDARG;
508
509     if (dwFlags & MULTICLASSINFO_GETTYPEINFO)
510     {
511         HRESULT hr = get_typeinfo(This->tid, ti);
512         if (FAILED(hr))
513             return hr;
514
515         ITypeInfo_AddRef(*ti);
516     }
517
518     if (dwFlags & MULTICLASSINFO_GETNUMRESERVEDDISPIDS)
519     {
520         *pdwTIFlags = 0;
521         *pcdispidReserved = 0;
522     }
523
524     if (dwFlags & MULTICLASSINFO_GETIIDPRIMARY)
525         *piidPrimary = *get_riid_from_tid(This->tid);
526
527     if (dwFlags & MULTICLASSINFO_GETIIDSOURCE)
528         *piidSource = *get_riid_from_tid(This->tid);
529
530     return S_OK;
531 }
532
533 static const IProvideMultipleClassInfoVtbl ProvideMultipleClassInfoVtbl =
534 {
535     ProvideMultipleClassInfo_QueryInterface,
536     ProvideMultipleClassInfo_AddRef,
537     ProvideMultipleClassInfo_Release,
538     ProvideMultipleClassInfo_GetClassInfo,
539     ProvideMultipleClassInfo_GetGUID,
540     ProvideMultipleClassInfo_GetMultiTypeInfoCount,
541     ProvideMultipleClassInfo_GetInfoOfIndex
542 };
543
544 static HRESULT init_automation_object(AutomationObject *This, MSIHANDLE msiHandle, tid_t tid)
545 {
546     TRACE("(%p, %d, %s)\n", This, msiHandle, debugstr_guid(get_riid_from_tid(tid)));
547
548     This->IDispatch_iface.lpVtbl = &AutomationObjectVtbl;
549     This->IProvideMultipleClassInfo_iface.lpVtbl = &ProvideMultipleClassInfoVtbl;
550     This->ref = 1;
551
552     This->msiHandle = msiHandle;
553     This->tid = tid;
554
555     return S_OK;
556 }
557
558 /*
559  * ListEnumerator methods
560  */
561
562 static inline ListEnumerator *impl_from_IEnumVARIANT(IEnumVARIANT* iface)
563 {
564     return CONTAINING_RECORD(iface, ListEnumerator, IEnumVARIANT_iface);
565 }
566
567 static HRESULT WINAPI ListEnumerator_QueryInterface(IEnumVARIANT* iface, REFIID riid,
568         void** ppvObject)
569 {
570     ListEnumerator *This = impl_from_IEnumVARIANT(iface);
571
572     TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject);
573
574     if (ppvObject == NULL)
575       return E_INVALIDARG;
576
577     *ppvObject = 0;
578
579     if (IsEqualGUID(riid, &IID_IUnknown) ||
580         IsEqualGUID(riid, &IID_IEnumVARIANT))
581     {
582         *ppvObject = &This->IEnumVARIANT_iface;
583     }
584     else
585     {
586         TRACE("() : asking for unsupported interface %s\n",debugstr_guid(riid));
587         return E_NOINTERFACE;
588     }
589
590     IEnumVARIANT_AddRef(iface);
591     return S_OK;
592 }
593
594 static ULONG WINAPI ListEnumerator_AddRef(IEnumVARIANT* iface)
595 {
596     ListEnumerator *This = impl_from_IEnumVARIANT(iface);
597
598     TRACE("(%p/%p)\n", iface, This);
599
600     return InterlockedIncrement(&This->ref);
601 }
602
603 static ULONG WINAPI ListEnumerator_Release(IEnumVARIANT* iface)
604 {
605     ListEnumerator *This = impl_from_IEnumVARIANT(iface);
606     ULONG ref = InterlockedDecrement(&This->ref);
607
608     TRACE("(%p/%p)\n", iface, This);
609
610     if (!ref)
611     {
612         if (This->list) IDispatch_Release(&This->list->autoobj.IDispatch_iface);
613         msi_free(This);
614     }
615
616     return ref;
617 }
618
619 static HRESULT WINAPI ListEnumerator_Next(IEnumVARIANT* iface, ULONG celt, VARIANT* rgVar,
620         ULONG* fetched)
621 {
622     ListEnumerator *This = impl_from_IEnumVARIANT(iface);
623     ULONG i, local;
624
625     TRACE("(%p, %uld, %p, %p)\n", iface, celt, rgVar, fetched);
626
627     if (fetched) *fetched = 0;
628
629     if (!rgVar)
630         return S_FALSE;
631
632     for (local = 0; local < celt; local++)
633         VariantInit(&rgVar[local]);
634
635     for (i = This->pos, local = 0; i < This->list->count && local < celt; i++, local++)
636         VariantCopy(&rgVar[local], &This->list->data[i]);
637
638     if (fetched) *fetched = local;
639     This->pos = i;
640
641     return (local < celt) ? S_FALSE : S_OK;
642 }
643
644 static HRESULT WINAPI ListEnumerator_Skip(IEnumVARIANT* iface, ULONG celt)
645 {
646     ListEnumerator *This = impl_from_IEnumVARIANT(iface);
647
648     TRACE("(%p,%uld)\n", iface, celt);
649
650     This->pos += celt;
651     if (This->pos >= This->list->count)
652     {
653         This->pos = This->list->count;
654         return S_FALSE;
655     }
656
657     return S_OK;
658 }
659
660 static HRESULT WINAPI ListEnumerator_Reset(IEnumVARIANT* iface)
661 {
662     ListEnumerator *This = impl_from_IEnumVARIANT(iface);
663
664     TRACE("(%p)\n", iface);
665
666     This->pos = 0;
667     return S_OK;
668 }
669
670 static HRESULT WINAPI ListEnumerator_Clone(IEnumVARIANT* iface, IEnumVARIANT **ppEnum)
671 {
672     ListEnumerator *This = impl_from_IEnumVARIANT(iface);
673     HRESULT hr;
674
675     TRACE("(%p,%p)\n", iface, ppEnum);
676
677     if (ppEnum == NULL)
678         return S_FALSE;
679
680     *ppEnum = NULL;
681     hr = create_list_enumerator(This->list, (LPVOID *)ppEnum);
682     if (FAILED(hr))
683     {
684         if (*ppEnum) IEnumVARIANT_Release(*ppEnum);
685         return hr;
686     }
687
688     return S_OK;
689 }
690
691 static const struct IEnumVARIANTVtbl ListEnumerator_Vtbl =
692 {
693     ListEnumerator_QueryInterface,
694     ListEnumerator_AddRef,
695     ListEnumerator_Release,
696     ListEnumerator_Next,
697     ListEnumerator_Skip,
698     ListEnumerator_Reset,
699     ListEnumerator_Clone
700 };
701
702 /* Create a list enumerator, placing the result in the pointer ppObj.  */
703 static HRESULT create_list_enumerator(ListObject *list, void **ppObj)
704 {
705     ListEnumerator *object;
706
707     TRACE("(%p, %p)\n", list, ppObj);
708
709     object = msi_alloc(sizeof(ListEnumerator));
710
711     /* Set all the VTable references */
712     object->IEnumVARIANT_iface.lpVtbl = &ListEnumerator_Vtbl;
713     object->ref = 1;
714
715     /* Store data that was passed */
716     object->pos = 0;
717     object->list = list;
718     if (list) IDispatch_AddRef(&list->autoobj.IDispatch_iface);
719
720     *ppObj = object;
721     return S_OK;
722 }
723
724 /*
725  * Individual Object Invocation Functions
726  */
727
728 /* Helper function that copies a passed parameter instead of using VariantChangeType like the actual DispGetParam.
729    This function is only for VARIANT type parameters that have several types that cannot be properly discriminated
730    using DispGetParam/VariantChangeType. */
731 static HRESULT DispGetParam_CopyOnly(
732         DISPPARAMS *pdispparams, /* [in] Parameter list */
733         UINT        *position,    /* [in] Position of parameter to copy in pdispparams; on return will contain calculated position */
734         VARIANT    *pvarResult)  /* [out] Destination for resulting variant */
735 {
736     /* position is counted backwards */
737     UINT pos;
738
739     TRACE("position=%d, cArgs=%d, cNamedArgs=%d\n",
740           *position, pdispparams->cArgs, pdispparams->cNamedArgs);
741     if (*position < pdispparams->cArgs) {
742       /* positional arg? */
743       pos = pdispparams->cArgs - *position - 1;
744     } else {
745       /* FIXME: is this how to handle named args? */
746       for (pos=0; pos<pdispparams->cNamedArgs; pos++)
747         if (pdispparams->rgdispidNamedArgs[pos] == *position) break;
748
749       if (pos==pdispparams->cNamedArgs)
750         return DISP_E_PARAMNOTFOUND;
751     }
752     *position = pos;
753     return VariantCopyInd(pvarResult,
754                         &pdispparams->rgvarg[pos]);
755 }
756
757 static HRESULT summaryinfo_invoke(
758         AutomationObject* This,
759         DISPID dispIdMember,
760         REFIID riid,
761         LCID lcid,
762         WORD wFlags,
763         DISPPARAMS* pDispParams,
764         VARIANT* pVarResult,
765         EXCEPINFO* pExcepInfo,
766         UINT* puArgErr)
767 {
768     UINT ret;
769     VARIANTARG varg0, varg1;
770     FILETIME ft, ftlocal;
771     SYSTEMTIME st;
772     HRESULT hr;
773
774     VariantInit(&varg0);
775     VariantInit(&varg1);
776
777     switch (dispIdMember)
778     {
779         case DISPID_SUMMARYINFO_PROPERTY:
780             if (wFlags & DISPATCH_PROPERTYGET)
781             {
782                 UINT type;
783                 INT value;
784                 DWORD size = 0;
785                 DATE date;
786                 LPWSTR str;
787
788                 static WCHAR szEmpty[] = {0};
789
790                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
791                 if (FAILED(hr)) return hr;
792                 ret = MsiSummaryInfoGetPropertyW(This->msiHandle, V_I4(&varg0), &type, &value,
793                                                  &ft, szEmpty, &size);
794                 if (ret != ERROR_SUCCESS &&
795                     ret != ERROR_MORE_DATA)
796                 {
797                     ERR("MsiSummaryInfoGetProperty returned %d\n", ret);
798                     return DISP_E_EXCEPTION;
799                 }
800
801                 switch (type)
802                 {
803                     case VT_EMPTY:
804                         break;
805
806                     case VT_I2:
807                     case VT_I4:
808                         V_VT(pVarResult) = VT_I4;
809                         V_I4(pVarResult) = value;
810                         break;
811
812                     case VT_LPSTR:
813                         if (!(str = msi_alloc(++size * sizeof(WCHAR))))
814                             ERR("Out of memory\n");
815                         else if ((ret = MsiSummaryInfoGetPropertyW(This->msiHandle, V_I4(&varg0), &type, NULL,
816                                                                    NULL, str, &size)) != ERROR_SUCCESS)
817                             ERR("MsiSummaryInfoGetProperty returned %d\n", ret);
818                         else
819                         {
820                             V_VT(pVarResult) = VT_BSTR;
821                             V_BSTR(pVarResult) = SysAllocString(str);
822                         }
823                         msi_free(str);
824                         break;
825
826                     case VT_FILETIME:
827                         FileTimeToLocalFileTime(&ft, &ftlocal);
828                         FileTimeToSystemTime(&ftlocal, &st);
829                         SystemTimeToVariantTime(&st, &date);
830
831                         V_VT(pVarResult) = VT_DATE;
832                         V_DATE(pVarResult) = date;
833                         break;
834
835                     default:
836                         ERR("Unhandled variant type %d\n", type);
837                 }
838             }
839             else if (wFlags & DISPATCH_PROPERTYPUT)
840             {
841                 UINT posValue = DISPID_PROPERTYPUT;
842
843                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
844                 if (FAILED(hr)) return hr;
845                 hr = DispGetParam_CopyOnly(pDispParams, &posValue, &varg1);
846                 if (FAILED(hr))
847                 {
848                     *puArgErr = posValue;
849                     return hr;
850                 }
851
852                 switch (V_VT(&varg1))
853                 {
854                     case VT_I2:
855                     case VT_I4:
856                         ret = MsiSummaryInfoSetPropertyW(This->msiHandle, V_I4(&varg0), V_VT(&varg1), V_I4(&varg1), NULL, NULL);
857                         break;
858
859                     case VT_DATE:
860                         VariantTimeToSystemTime(V_DATE(&varg1), &st);
861                         SystemTimeToFileTime(&st, &ftlocal);
862                         LocalFileTimeToFileTime(&ftlocal, &ft);
863                         ret = MsiSummaryInfoSetPropertyW(This->msiHandle, V_I4(&varg0), VT_FILETIME, 0, &ft, NULL);
864                         break;
865
866                     case VT_BSTR:
867                         ret = MsiSummaryInfoSetPropertyW(This->msiHandle, V_I4(&varg0), VT_LPSTR, 0, NULL, V_BSTR(&varg1));
868                         break;
869
870                     default:
871                         FIXME("Unhandled variant type %d\n", V_VT(&varg1));
872                         VariantClear(&varg1);
873                         return DISP_E_EXCEPTION;
874                 }
875
876                 if (ret != ERROR_SUCCESS)
877                 {
878                     ERR("MsiSummaryInfoSetPropertyW returned %d\n", ret);
879                     return DISP_E_EXCEPTION;
880                 }
881             }
882             else return DISP_E_MEMBERNOTFOUND;
883             break;
884
885         case DISPID_SUMMARYINFO_PROPERTYCOUNT:
886             if (wFlags & DISPATCH_PROPERTYGET) {
887                 UINT count;
888                 if ((ret = MsiSummaryInfoGetPropertyCount(This->msiHandle, &count)) != ERROR_SUCCESS)
889                     ERR("MsiSummaryInfoGetPropertyCount returned %d\n", ret);
890                 else
891                 {
892                     V_VT(pVarResult) = VT_I4;
893                     V_I4(pVarResult) = count;
894                 }
895             }
896             else return DISP_E_MEMBERNOTFOUND;
897             break;
898
899         default:
900             return DISP_E_MEMBERNOTFOUND;
901     }
902
903     VariantClear(&varg1);
904     VariantClear(&varg0);
905
906     return S_OK;
907 }
908
909 static HRESULT record_invoke(
910         AutomationObject* This,
911         DISPID dispIdMember,
912         REFIID riid,
913         LCID lcid,
914         WORD wFlags,
915         DISPPARAMS* pDispParams,
916         VARIANT* pVarResult,
917         EXCEPINFO* pExcepInfo,
918         UINT* puArgErr)
919 {
920     WCHAR *szString;
921     DWORD dwLen = 0;
922     UINT ret;
923     VARIANTARG varg0, varg1;
924     HRESULT hr;
925
926     VariantInit(&varg0);
927     VariantInit(&varg1);
928
929     switch (dispIdMember)
930     {
931         case DISPID_RECORD_FIELDCOUNT:
932             if (wFlags & DISPATCH_PROPERTYGET) {
933                 V_VT(pVarResult) = VT_I4;
934                 V_I4(pVarResult) = MsiRecordGetFieldCount(This->msiHandle);
935             }
936             else return DISP_E_MEMBERNOTFOUND;
937             break;
938
939         case DISPID_RECORD_STRINGDATA:
940             if (wFlags & DISPATCH_PROPERTYGET) {
941                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
942                 if (FAILED(hr)) return hr;
943                 V_VT(pVarResult) = VT_BSTR;
944                 V_BSTR(pVarResult) = NULL;
945                 if ((ret = MsiRecordGetStringW(This->msiHandle, V_I4(&varg0), NULL, &dwLen)) == ERROR_SUCCESS)
946                 {
947                     if (!(szString = msi_alloc((++dwLen)*sizeof(WCHAR))))
948                         ERR("Out of memory\n");
949                     else if ((ret = MsiRecordGetStringW(This->msiHandle, V_I4(&varg0), szString, &dwLen)) == ERROR_SUCCESS)
950                         V_BSTR(pVarResult) = SysAllocString(szString);
951                     msi_free(szString);
952                 }
953                 if (ret != ERROR_SUCCESS)
954                     ERR("MsiRecordGetString returned %d\n", ret);
955             } else if (wFlags & DISPATCH_PROPERTYPUT) {
956                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
957                 if (FAILED(hr)) return hr;
958                 hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr);
959                 if (FAILED(hr)) return hr;
960                 if ((ret = MsiRecordSetStringW(This->msiHandle, V_I4(&varg0), V_BSTR(&varg1))) != ERROR_SUCCESS)
961                 {
962                     VariantClear(&varg1);
963                     ERR("MsiRecordSetString returned %d\n", ret);
964                     return DISP_E_EXCEPTION;
965                 }
966             }
967             else return DISP_E_MEMBERNOTFOUND;
968             break;
969
970         case DISPID_RECORD_INTEGERDATA:
971             if (wFlags & DISPATCH_PROPERTYGET) {
972                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
973                 if (FAILED(hr)) return hr;
974                 V_VT(pVarResult) = VT_I4;
975                 V_I4(pVarResult) = MsiRecordGetInteger(This->msiHandle, V_I4(&varg0));
976             } else if (wFlags & DISPATCH_PROPERTYPUT) {
977                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
978                 if (FAILED(hr)) return hr;
979                 hr = DispGetParam(pDispParams, 1, VT_I4, &varg1, puArgErr);
980                 if (FAILED(hr)) return hr;
981                 if ((ret = MsiRecordSetInteger(This->msiHandle, V_I4(&varg0), V_I4(&varg1))) != ERROR_SUCCESS)
982                 {
983                     ERR("MsiRecordSetInteger returned %d\n", ret);
984                     return DISP_E_EXCEPTION;
985                 }
986             }
987             else return DISP_E_MEMBERNOTFOUND;
988             break;
989
990          default:
991             return DISP_E_MEMBERNOTFOUND;
992     }
993
994     VariantClear(&varg1);
995     VariantClear(&varg0);
996
997     return S_OK;
998 }
999
1000 static HRESULT create_record(MSIHANDLE msiHandle, IDispatch **disp)
1001 {
1002     AutomationObject *record;
1003     HRESULT hr;
1004
1005     record = msi_alloc(sizeof(*record));
1006     if (!record) return E_OUTOFMEMORY;
1007
1008     hr = init_automation_object(record, msiHandle, Record_tid);
1009     if (hr != S_OK)
1010     {
1011         msi_free(record);
1012         return hr;
1013     }
1014
1015     *disp = &record->IDispatch_iface;
1016
1017     return hr;
1018 }
1019
1020 static HRESULT list_invoke(
1021         AutomationObject* This,
1022         DISPID dispIdMember,
1023         REFIID riid,
1024         LCID lcid,
1025         WORD wFlags,
1026         DISPPARAMS* pDispParams,
1027         VARIANT* pVarResult,
1028         EXCEPINFO* pExcepInfo,
1029         UINT* puArgErr)
1030 {
1031     ListObject *list = (ListObject*)This;
1032     IUnknown *pUnk = NULL;
1033     HRESULT hr;
1034
1035     switch (dispIdMember)
1036     {
1037          case DISPID_LIST__NEWENUM:
1038              if (wFlags & DISPATCH_METHOD) {
1039                  V_VT(pVarResult) = VT_UNKNOWN;
1040                  if (SUCCEEDED(hr = create_list_enumerator(list, (LPVOID *)&pUnk)))
1041                      V_UNKNOWN(pVarResult) = pUnk;
1042                  else
1043                      ERR("Failed to create IEnumVARIANT object, hresult 0x%08x\n", hr);
1044              }
1045              else return DISP_E_MEMBERNOTFOUND;
1046              break;
1047
1048          case DISPID_LIST_ITEM:
1049              if (wFlags & DISPATCH_PROPERTYGET) {
1050                 VARIANTARG index;
1051
1052                 VariantInit(&index);
1053                 hr = DispGetParam(pDispParams, 0, VT_I4, &index, puArgErr);
1054                 if (FAILED(hr)) return hr;
1055                 if (V_I4(&index) < 0 || V_I4(&index) >= list->count)
1056                     return DISP_E_BADINDEX;
1057                 VariantCopy(pVarResult, &list->data[V_I4(&index)]);
1058             }
1059             else return DISP_E_MEMBERNOTFOUND;
1060             break;
1061
1062          case DISPID_LIST_COUNT:
1063             if (wFlags & DISPATCH_PROPERTYGET) {
1064                 V_VT(pVarResult) = VT_I4;
1065                 V_I4(pVarResult) = list->count;
1066             }
1067             else return DISP_E_MEMBERNOTFOUND;
1068             break;
1069
1070          default:
1071             return DISP_E_MEMBERNOTFOUND;
1072     }
1073
1074     return S_OK;
1075 }
1076
1077 static void list_free(AutomationObject *This)
1078 {
1079     ListObject *list = (ListObject*)This;
1080     int i;
1081
1082     for (i = 0; i < list->count; i++)
1083         VariantClear(&list->data[i]);
1084     msi_free(list->data);
1085 }
1086
1087 static HRESULT get_products_count(const WCHAR *product, int *len)
1088 {
1089     int i = 0;
1090
1091     while (1)
1092     {
1093         WCHAR dataW[GUID_SIZE];
1094         UINT ret;
1095
1096         /* all or related only */
1097         if (product)
1098             ret = MsiEnumRelatedProductsW(product, 0, i, dataW);
1099         else
1100             ret = MsiEnumProductsW(i, dataW);
1101
1102         if (ret == ERROR_NO_MORE_ITEMS) break;
1103
1104         if (ret != ERROR_SUCCESS)
1105             return DISP_E_EXCEPTION;
1106
1107         i++;
1108     }
1109
1110     *len = i;
1111
1112     return S_OK;
1113 }
1114
1115 static HRESULT create_list(const WCHAR *product, IDispatch **dispatch)
1116 {
1117     ListObject *list;
1118     HRESULT hr;
1119     int i;
1120
1121     list = msi_alloc_zero(sizeof(ListObject));
1122     if (!list) return E_OUTOFMEMORY;
1123
1124     hr = init_automation_object(&list->autoobj, 0, StringList_tid);
1125     if (hr != S_OK)
1126     {
1127         msi_free(list);
1128         return hr;
1129     }
1130
1131     *dispatch = &list->autoobj.IDispatch_iface;
1132
1133     hr = get_products_count(product, &list->count);
1134     if (hr != S_OK)
1135     {
1136         IDispatch_Release(*dispatch);
1137         return hr;
1138     }
1139
1140     list->data = msi_alloc(list->count*sizeof(VARIANT));
1141     if (!list->data)
1142     {
1143         IDispatch_Release(*dispatch);
1144         return E_OUTOFMEMORY;
1145     }
1146
1147     for (i = 0; i < list->count; i++)
1148     {
1149         WCHAR dataW[GUID_SIZE];
1150         UINT ret;
1151
1152         /* all or related only */
1153         if (product)
1154             ret = MsiEnumRelatedProductsW(product, 0, i, dataW);
1155         else
1156             ret = MsiEnumProductsW(i, dataW);
1157
1158         if (ret == ERROR_NO_MORE_ITEMS) break;
1159
1160         V_VT(&list->data[i]) = VT_BSTR;
1161         V_BSTR(&list->data[i]) = SysAllocString(dataW);
1162     }
1163
1164     return S_OK;
1165 }
1166
1167 static HRESULT view_invoke(
1168         AutomationObject* This,
1169         DISPID dispIdMember,
1170         REFIID riid,
1171         LCID lcid,
1172         WORD wFlags,
1173         DISPPARAMS* pDispParams,
1174         VARIANT* pVarResult,
1175         EXCEPINFO* pExcepInfo,
1176         UINT* puArgErr)
1177 {
1178     MSIHANDLE msiHandle;
1179     UINT ret;
1180     VARIANTARG varg0, varg1;
1181     HRESULT hr;
1182
1183     VariantInit(&varg0);
1184     VariantInit(&varg1);
1185
1186     switch (dispIdMember)
1187     {
1188         case DISPID_VIEW_EXECUTE:
1189             if (wFlags & DISPATCH_METHOD)
1190             {
1191                 hr = DispGetParam(pDispParams, 0, VT_DISPATCH, &varg0, puArgErr);
1192                 if (SUCCEEDED(hr) && V_DISPATCH(&varg0) != NULL)
1193                     MsiViewExecute(This->msiHandle, ((AutomationObject *)V_DISPATCH(&varg0))->msiHandle);
1194                 else
1195                     MsiViewExecute(This->msiHandle, 0);
1196             }
1197             else return DISP_E_MEMBERNOTFOUND;
1198             break;
1199
1200         case DISPID_VIEW_FETCH:
1201             if (wFlags & DISPATCH_METHOD)
1202             {
1203                 V_VT(pVarResult) = VT_DISPATCH;
1204                 if ((ret = MsiViewFetch(This->msiHandle, &msiHandle)) == ERROR_SUCCESS)
1205                 {
1206                     if (FAILED(hr = create_record(msiHandle, &V_DISPATCH(pVarResult))))
1207                         ERR("Failed to create Record object, hresult 0x%08x\n", hr);
1208                 }
1209                 else if (ret == ERROR_NO_MORE_ITEMS)
1210                     V_DISPATCH(pVarResult) = NULL;
1211                 else
1212                 {
1213                     ERR("MsiViewFetch returned %d\n", ret);
1214                     return DISP_E_EXCEPTION;
1215                 }
1216             }
1217             else return DISP_E_MEMBERNOTFOUND;
1218             break;
1219
1220         case DISPID_VIEW_MODIFY:
1221             if (wFlags & DISPATCH_METHOD)
1222             {
1223                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1224                 if (FAILED(hr)) return hr;
1225                 hr = DispGetParam(pDispParams, 1, VT_DISPATCH, &varg1, puArgErr);
1226                 if (FAILED(hr)) return hr;
1227                 if (!V_DISPATCH(&varg1)) return DISP_E_EXCEPTION;
1228                 if ((ret = MsiViewModify(This->msiHandle, V_I4(&varg0), ((AutomationObject *)V_DISPATCH(&varg1))->msiHandle)) != ERROR_SUCCESS)
1229                 {
1230                     VariantClear(&varg1);
1231                     ERR("MsiViewModify returned %d\n", ret);
1232                     return DISP_E_EXCEPTION;
1233                 }
1234             }
1235             else return DISP_E_MEMBERNOTFOUND;
1236             break;
1237
1238         case DISPID_VIEW_CLOSE:
1239             if (wFlags & DISPATCH_METHOD)
1240             {
1241                 MsiViewClose(This->msiHandle);
1242             }
1243             else return DISP_E_MEMBERNOTFOUND;
1244             break;
1245
1246          default:
1247             return DISP_E_MEMBERNOTFOUND;
1248     }
1249
1250     VariantClear(&varg1);
1251     VariantClear(&varg0);
1252
1253     return S_OK;
1254 }
1255
1256 static HRESULT DatabaseImpl_LastErrorRecord(WORD wFlags,
1257                                             DISPPARAMS* pDispParams,
1258                                             VARIANT* pVarResult,
1259                                             EXCEPINFO* pExcepInfo,
1260                                             UINT* puArgErr)
1261 {
1262     if (!(wFlags & DISPATCH_METHOD))
1263         return DISP_E_MEMBERNOTFOUND;
1264
1265     FIXME("\n");
1266
1267     VariantInit(pVarResult);
1268     return S_OK;
1269 }
1270
1271 HRESULT database_invoke(
1272         AutomationObject* This,
1273         DISPID dispIdMember,
1274         REFIID riid,
1275         LCID lcid,
1276         WORD wFlags,
1277         DISPPARAMS* pDispParams,
1278         VARIANT* pVarResult,
1279         EXCEPINFO* pExcepInfo,
1280         UINT* puArgErr)
1281 {
1282     IDispatch *dispatch = NULL;
1283     MSIHANDLE msiHandle;
1284     UINT ret;
1285     VARIANTARG varg0, varg1;
1286     HRESULT hr;
1287
1288     VariantInit(&varg0);
1289     VariantInit(&varg1);
1290
1291     switch (dispIdMember)
1292     {
1293         case DISPID_DATABASE_SUMMARYINFORMATION:
1294             if (wFlags & DISPATCH_PROPERTYGET)
1295             {
1296                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1297                 if (FAILED(hr))
1298                     V_I4(&varg0) = 0;
1299
1300                 V_VT(pVarResult) = VT_DISPATCH;
1301                 if ((ret = MsiGetSummaryInformationW(This->msiHandle, NULL, V_I4(&varg0), &msiHandle)) == ERROR_SUCCESS)
1302                 {
1303                     hr = create_summaryinfo(msiHandle, &dispatch);
1304                     if (SUCCEEDED(hr))
1305                         V_DISPATCH(pVarResult) = dispatch;
1306                     else
1307                         ERR("Failed to create SummaryInfo object: 0x%08x\n", hr);
1308                 }
1309                 else
1310                 {
1311                     ERR("MsiGetSummaryInformation returned %d\n", ret);
1312                     return DISP_E_EXCEPTION;
1313                 }
1314             }
1315             else return DISP_E_MEMBERNOTFOUND;
1316             break;
1317
1318         case DISPID_DATABASE_OPENVIEW:
1319             if (wFlags & DISPATCH_METHOD)
1320             {
1321                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1322                 if (FAILED(hr)) return hr;
1323                 V_VT(pVarResult) = VT_DISPATCH;
1324                 if ((ret = MsiDatabaseOpenViewW(This->msiHandle, V_BSTR(&varg0), &msiHandle)) == ERROR_SUCCESS)
1325                 {
1326                     if (SUCCEEDED(hr = create_view(msiHandle, &dispatch)))
1327                         V_DISPATCH(pVarResult) = dispatch;
1328                     else
1329                         ERR("Failed to create View object, hresult 0x%08x\n", hr);
1330                 }
1331                 else
1332                 {
1333                     VariantClear(&varg0);
1334                     ERR("MsiDatabaseOpenView returned %d\n", ret);
1335                     return DISP_E_EXCEPTION;
1336                 }
1337             }
1338             else return DISP_E_MEMBERNOTFOUND;
1339             break;
1340
1341         case DISPID_INSTALLER_LASTERRORRECORD:
1342             return DatabaseImpl_LastErrorRecord(wFlags, pDispParams,
1343                                                 pVarResult, pExcepInfo,
1344                                                 puArgErr);
1345
1346          default:
1347             return DISP_E_MEMBERNOTFOUND;
1348     }
1349
1350     VariantClear(&varg1);
1351     VariantClear(&varg0);
1352
1353     return S_OK;
1354 }
1355
1356 static HRESULT session_invoke(
1357         AutomationObject* This,
1358         DISPID dispIdMember,
1359         REFIID riid,
1360         LCID lcid,
1361         WORD wFlags,
1362         DISPPARAMS* pDispParams,
1363         VARIANT* pVarResult,
1364         EXCEPINFO* pExcepInfo,
1365         UINT* puArgErr)
1366 {
1367     SessionObject *session = (SessionObject*)This;
1368     WCHAR *szString;
1369     DWORD dwLen;
1370     MSIHANDLE msiHandle;
1371     LANGID langId;
1372     UINT ret;
1373     INSTALLSTATE iInstalled, iAction;
1374     VARIANTARG varg0, varg1;
1375     HRESULT hr;
1376
1377     VariantInit(&varg0);
1378     VariantInit(&varg1);
1379
1380     switch (dispIdMember)
1381     {
1382         case DISPID_SESSION_INSTALLER:
1383             if (wFlags & DISPATCH_PROPERTYGET) {
1384                 V_VT(pVarResult) = VT_DISPATCH;
1385                 IDispatch_AddRef(session->installer);
1386                 V_DISPATCH(pVarResult) = session->installer;
1387             }
1388             else return DISP_E_MEMBERNOTFOUND;
1389             break;
1390
1391         case DISPID_SESSION_PROPERTY:
1392             if (wFlags & DISPATCH_PROPERTYGET) {
1393                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1394                 if (FAILED(hr)) return hr;
1395                 V_VT(pVarResult) = VT_BSTR;
1396                 V_BSTR(pVarResult) = NULL;
1397                 if ((ret = MsiGetPropertyW(This->msiHandle, V_BSTR(&varg0), NULL, &dwLen)) == ERROR_SUCCESS)
1398                 {
1399                     if (!(szString = msi_alloc((++dwLen)*sizeof(WCHAR))))
1400                         ERR("Out of memory\n");
1401                     else if ((ret = MsiGetPropertyW(This->msiHandle, V_BSTR(&varg0), szString, &dwLen)) == ERROR_SUCCESS)
1402                         V_BSTR(pVarResult) = SysAllocString(szString);
1403                     msi_free(szString);
1404                 }
1405                 if (ret != ERROR_SUCCESS)
1406                     ERR("MsiGetProperty returned %d\n", ret);
1407             } else if (wFlags & DISPATCH_PROPERTYPUT) {
1408                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1409                 if (FAILED(hr)) return hr;
1410                 hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr);
1411                 if (FAILED(hr)) {
1412                     VariantClear(&varg0);
1413                     return hr;
1414                 }
1415                 if ((ret = MsiSetPropertyW(This->msiHandle, V_BSTR(&varg0), V_BSTR(&varg1))) != ERROR_SUCCESS)
1416                 {
1417                     VariantClear(&varg0);
1418                     VariantClear(&varg1);
1419                     ERR("MsiSetProperty returned %d\n", ret);
1420                     return DISP_E_EXCEPTION;
1421                 }
1422             }
1423             else return DISP_E_MEMBERNOTFOUND;
1424             break;
1425
1426         case DISPID_SESSION_LANGUAGE:
1427             if (wFlags & DISPATCH_PROPERTYGET) {
1428                 langId = MsiGetLanguage(This->msiHandle);
1429                 V_VT(pVarResult) = VT_I4;
1430                 V_I4(pVarResult) = langId;
1431             }
1432             else return DISP_E_MEMBERNOTFOUND;
1433             break;
1434
1435         case DISPID_SESSION_MODE:
1436             if (wFlags & DISPATCH_PROPERTYGET) {
1437                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1438                 if (FAILED(hr)) return hr;
1439                 V_VT(pVarResult) = VT_BOOL;
1440                 V_BOOL(pVarResult) = MsiGetMode(This->msiHandle, V_I4(&varg0));
1441             } else if (wFlags & DISPATCH_PROPERTYPUT) {
1442                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1443                 if (FAILED(hr)) return hr;
1444                 hr = DispGetParam(pDispParams, 1, VT_BOOL, &varg1, puArgErr);
1445                 if (FAILED(hr)) return hr;
1446                 if ((ret = MsiSetMode(This->msiHandle, V_I4(&varg0), V_BOOL(&varg1))) != ERROR_SUCCESS)
1447                 {
1448                     ERR("MsiSetMode returned %d\n", ret);
1449                     return DISP_E_EXCEPTION;
1450                 }
1451             }
1452             else return DISP_E_MEMBERNOTFOUND;
1453             break;
1454
1455         case DISPID_SESSION_DATABASE:
1456             if (wFlags & DISPATCH_PROPERTYGET) {
1457                 V_VT(pVarResult) = VT_DISPATCH;
1458                 if ((msiHandle = MsiGetActiveDatabase(This->msiHandle)))
1459                 {
1460                     IDispatch *dispatch;
1461
1462                     if (SUCCEEDED(hr = create_database(msiHandle, &dispatch)))
1463                         V_DISPATCH(pVarResult) = dispatch;
1464                     else
1465                         ERR("Failed to create Database object, hresult 0x%08x\n", hr);
1466                 }
1467                 else
1468                 {
1469                     ERR("MsiGetActiveDatabase failed\n");
1470                     return DISP_E_EXCEPTION;
1471                 }
1472             }
1473             else return DISP_E_MEMBERNOTFOUND;
1474             break;
1475
1476         case DISPID_SESSION_DOACTION:
1477             if (wFlags & DISPATCH_METHOD) {
1478                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1479                 if (FAILED(hr)) return hr;
1480                 ret = MsiDoActionW(This->msiHandle, V_BSTR(&varg0));
1481                 V_VT(pVarResult) = VT_I4;
1482                 switch (ret)
1483                 {
1484                     case ERROR_FUNCTION_NOT_CALLED:
1485                         V_I4(pVarResult) = msiDoActionStatusNoAction;
1486                         break;
1487                     case ERROR_SUCCESS:
1488                         V_I4(pVarResult) = msiDoActionStatusSuccess;
1489                         break;
1490                     case ERROR_INSTALL_USEREXIT:
1491                         V_I4(pVarResult) = msiDoActionStatusUserExit;
1492                         break;
1493                     case ERROR_INSTALL_FAILURE:
1494                         V_I4(pVarResult) = msiDoActionStatusFailure;
1495                         break;
1496                     case ERROR_INSTALL_SUSPEND:
1497                         V_I4(pVarResult) = msiDoActionStatusSuspend;
1498                         break;
1499                     case ERROR_MORE_DATA:
1500                         V_I4(pVarResult) = msiDoActionStatusFinished;
1501                         break;
1502                     case ERROR_INVALID_HANDLE_STATE:
1503                         V_I4(pVarResult) = msiDoActionStatusWrongState;
1504                         break;
1505                     case ERROR_INVALID_DATA:
1506                         V_I4(pVarResult) = msiDoActionStatusBadActionData;
1507                         break;
1508                     default:
1509                         VariantClear(&varg0);
1510                         FIXME("MsiDoAction returned unhandled value %d\n", ret);
1511                         return DISP_E_EXCEPTION;
1512                 }
1513             }
1514             else return DISP_E_MEMBERNOTFOUND;
1515             break;
1516
1517         case DISPID_SESSION_EVALUATECONDITION:
1518             if (wFlags & DISPATCH_METHOD) {
1519                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1520                 if (FAILED(hr)) return hr;
1521                 V_VT(pVarResult) = VT_I4;
1522                 V_I4(pVarResult) = MsiEvaluateConditionW(This->msiHandle, V_BSTR(&varg0));
1523             }
1524             else return DISP_E_MEMBERNOTFOUND;
1525             break;
1526
1527         case DISPID_SESSION_MESSAGE:
1528             if(!(wFlags & DISPATCH_METHOD))
1529                 return DISP_E_MEMBERNOTFOUND;
1530
1531             hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1532             if (FAILED(hr)) return hr;
1533             hr = DispGetParam(pDispParams, 1, VT_DISPATCH, &varg1, puArgErr);
1534             if (FAILED(hr)) return hr;
1535
1536             V_VT(pVarResult) = VT_I4;
1537             V_I4(pVarResult) =
1538                 MsiProcessMessage(This->msiHandle, V_I4(&varg0), ((AutomationObject *)V_DISPATCH(&varg1))->msiHandle);
1539             break;
1540
1541         case DISPID_SESSION_SETINSTALLLEVEL:
1542             if (wFlags & DISPATCH_METHOD) {
1543                 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1544                 if (FAILED(hr)) return hr;
1545                 if ((ret = MsiSetInstallLevel(This->msiHandle, V_I4(&varg0))) != ERROR_SUCCESS)
1546                 {
1547                     ERR("MsiSetInstallLevel returned %d\n", ret);
1548                     return DISP_E_EXCEPTION;
1549                 }
1550             }
1551             else return DISP_E_MEMBERNOTFOUND;
1552             break;
1553
1554         case DISPID_SESSION_FEATURECURRENTSTATE:
1555             if (wFlags & DISPATCH_PROPERTYGET) {
1556                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1557                 if (FAILED(hr)) return hr;
1558                 V_VT(pVarResult) = VT_I4;
1559                 if ((ret = MsiGetFeatureStateW(This->msiHandle, V_BSTR(&varg0), &iInstalled, &iAction)) == ERROR_SUCCESS)
1560                     V_I4(pVarResult) = iInstalled;
1561                 else
1562                 {
1563                     ERR("MsiGetFeatureState returned %d\n", ret);
1564                     V_I4(pVarResult) = msiInstallStateUnknown;
1565                 }
1566             }
1567             else return DISP_E_MEMBERNOTFOUND;
1568             break;
1569
1570         case DISPID_SESSION_FEATUREREQUESTSTATE:
1571             if (wFlags & DISPATCH_PROPERTYGET) {
1572                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1573                 if (FAILED(hr)) return hr;
1574                 V_VT(pVarResult) = VT_I4;
1575                 if ((ret = MsiGetFeatureStateW(This->msiHandle, V_BSTR(&varg0), &iInstalled, &iAction)) == ERROR_SUCCESS)
1576                     V_I4(pVarResult) = iAction;
1577                 else
1578                 {
1579                     ERR("MsiGetFeatureState returned %d\n", ret);
1580                     V_I4(pVarResult) = msiInstallStateUnknown;
1581                 }
1582             } else if (wFlags & DISPATCH_PROPERTYPUT) {
1583                 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1584                 if (FAILED(hr)) return hr;
1585                 hr = DispGetParam(pDispParams, 1, VT_I4, &varg1, puArgErr);
1586                 if (FAILED(hr)) {
1587                     VariantClear(&varg0);
1588                     return hr;
1589                 }
1590                 if ((ret = MsiSetFeatureStateW(This->msiHandle, V_BSTR(&varg0), V_I4(&varg1))) != ERROR_SUCCESS)
1591                 {
1592                     VariantClear(&varg0);
1593                     ERR("MsiSetFeatureState returned %d\n", ret);
1594                     return DISP_E_EXCEPTION;
1595                 }
1596             }
1597             else return DISP_E_MEMBERNOTFOUND;
1598             break;
1599
1600          default:
1601             return DISP_E_MEMBERNOTFOUND;
1602     }
1603
1604     VariantClear(&varg1);
1605     VariantClear(&varg0);
1606
1607     return S_OK;
1608 }
1609
1610 /* Fill the variant pointed to by pVarResult with the value & size returned by RegQueryValueEx as dictated by the
1611  * registry value type. Used by Installer::RegistryValue. */
1612 static void variant_from_registry_value(VARIANT *pVarResult, DWORD dwType, LPBYTE lpData, DWORD dwSize)
1613 {
1614     static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 };
1615     static const WCHAR szREG_[] = { '(','R','E','G','_','?','?',')',0 };
1616     WCHAR *szString = (WCHAR *)lpData;
1617     LPWSTR szNewString = NULL;
1618     DWORD dwNewSize = 0;
1619     int idx;
1620
1621     switch (dwType)
1622     {
1623         /* Registry strings may not be null terminated so we must use SysAllocStringByteLen/Len */
1624         case REG_MULTI_SZ: /* Multi SZ change internal null characters to newlines */
1625             idx = (dwSize/sizeof(WCHAR))-1;
1626             while (idx >= 0 && !szString[idx]) idx--;
1627             for (; idx >= 0; idx--)
1628                 if (!szString[idx]) szString[idx] = '\n';
1629             /* fall through */
1630         case REG_SZ:
1631             V_VT(pVarResult) = VT_BSTR;
1632             V_BSTR(pVarResult) = SysAllocStringByteLen((LPCSTR)szString, dwSize);
1633             break;
1634
1635         case REG_EXPAND_SZ:
1636             if (!(dwNewSize = ExpandEnvironmentStringsW(szString, szNewString, dwNewSize)))
1637                 ERR("ExpandEnvironmentStrings returned error %d\n", GetLastError());
1638             else if (!(szNewString = msi_alloc(dwNewSize * sizeof(WCHAR))))
1639                 ERR("Out of memory\n");
1640             else if (!(dwNewSize = ExpandEnvironmentStringsW(szString, szNewString, dwNewSize)))
1641                 ERR("ExpandEnvironmentStrings returned error %d\n", GetLastError());
1642             else
1643             {
1644                 V_VT(pVarResult) = VT_BSTR;
1645                 V_BSTR(pVarResult) = SysAllocStringLen(szNewString, dwNewSize);
1646             }
1647             msi_free(szNewString);
1648             break;
1649
1650         case REG_DWORD:
1651             V_VT(pVarResult) = VT_I4;
1652             V_I4(pVarResult) = *((DWORD *)lpData);
1653             break;
1654
1655         case REG_QWORD:
1656             V_VT(pVarResult) = VT_BSTR;
1657             V_BSTR(pVarResult) = SysAllocString(szREG_);   /* Weird string, don't know why native returns it */
1658             break;
1659
1660         case REG_BINARY:
1661             V_VT(pVarResult) = VT_BSTR;
1662             V_BSTR(pVarResult) = SysAllocString(szREG_BINARY);
1663             break;
1664
1665         case REG_NONE:
1666             V_VT(pVarResult) = VT_EMPTY;
1667             break;
1668
1669         default:
1670             FIXME("Unhandled registry value type %d\n", dwType);
1671     }
1672 }
1673
1674 static HRESULT InstallerImpl_CreateRecord(WORD wFlags,
1675                                           DISPPARAMS* pDispParams,
1676                                           VARIANT* pVarResult,
1677                                           EXCEPINFO* pExcepInfo,
1678                                           UINT* puArgErr)
1679 {
1680     HRESULT hr;
1681     VARIANTARG varg0;
1682     MSIHANDLE hrec;
1683
1684     if (!(wFlags & DISPATCH_METHOD))
1685         return DISP_E_MEMBERNOTFOUND;
1686
1687     VariantInit(&varg0);
1688     hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1689     if (FAILED(hr))
1690         return hr;
1691
1692     V_VT(pVarResult) = VT_DISPATCH;
1693
1694     hrec = MsiCreateRecord(V_I4(&varg0));
1695     if (!hrec)
1696         return DISP_E_EXCEPTION;
1697
1698     return create_record(hrec, &V_DISPATCH(pVarResult));
1699 }
1700
1701 static HRESULT InstallerImpl_OpenPackage(AutomationObject* This,
1702                                          WORD wFlags,
1703                                          DISPPARAMS* pDispParams,
1704                                          VARIANT* pVarResult,
1705                                          EXCEPINFO* pExcepInfo,
1706                                          UINT* puArgErr)
1707 {
1708     UINT ret;
1709     HRESULT hr;
1710     MSIHANDLE hpkg;
1711     IDispatch* dispatch;
1712     VARIANTARG varg0, varg1;
1713
1714     if (!(wFlags & DISPATCH_METHOD))
1715         return DISP_E_MEMBERNOTFOUND;
1716
1717     if (pDispParams->cArgs == 0)
1718         return DISP_E_TYPEMISMATCH;
1719
1720     if (V_VT(&pDispParams->rgvarg[pDispParams->cArgs - 1]) != VT_BSTR)
1721         return DISP_E_TYPEMISMATCH;
1722
1723     VariantInit(&varg0);
1724     hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1725     if (FAILED(hr))
1726         return hr;
1727
1728     VariantInit(&varg1);
1729     if (pDispParams->cArgs == 2)
1730     {
1731         hr = DispGetParam(pDispParams, 1, VT_I4, &varg1, puArgErr);
1732         if (FAILED(hr))
1733             goto done;
1734     }
1735     else
1736     {
1737         V_VT(&varg1) = VT_I4;
1738         V_I4(&varg1) = 0;
1739     }
1740
1741     V_VT(pVarResult) = VT_DISPATCH;
1742
1743     ret = MsiOpenPackageExW(V_BSTR(&varg0), V_I4(&varg1), &hpkg);
1744     if (ret != ERROR_SUCCESS)
1745     {
1746         hr = DISP_E_EXCEPTION;
1747         goto done;
1748     }
1749
1750     hr = create_session(hpkg, &This->IDispatch_iface, &dispatch);
1751     if (SUCCEEDED(hr))
1752         V_DISPATCH(pVarResult) = dispatch;
1753
1754 done:
1755     VariantClear(&varg0);
1756     VariantClear(&varg1);
1757     return hr;
1758 }
1759
1760 static HRESULT InstallerImpl_OpenProduct(WORD wFlags,
1761                                          DISPPARAMS* pDispParams,
1762                                          VARIANT* pVarResult,
1763                                          EXCEPINFO* pExcepInfo,
1764                                          UINT* puArgErr)
1765 {
1766     HRESULT hr;
1767     VARIANTARG varg0;
1768
1769     if (!(wFlags & DISPATCH_METHOD))
1770         return DISP_E_MEMBERNOTFOUND;
1771
1772     VariantInit(&varg0);
1773     hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1774     if (FAILED(hr))
1775         return hr;
1776
1777     FIXME("%s\n", debugstr_w(V_BSTR(&varg0)));
1778
1779     VariantInit(pVarResult);
1780
1781     VariantClear(&varg0);
1782     return S_OK;
1783 }
1784
1785 static HRESULT InstallerImpl_OpenDatabase(WORD wFlags,
1786                                           DISPPARAMS* pDispParams,
1787                                           VARIANT* pVarResult,
1788                                           EXCEPINFO* pExcepInfo,
1789                                           UINT* puArgErr)
1790 {
1791     UINT ret;
1792     HRESULT hr;
1793     MSIHANDLE hdb;
1794     IDispatch* dispatch;
1795     VARIANTARG varg0, varg1;
1796
1797     if (!(wFlags & DISPATCH_METHOD))
1798         return DISP_E_MEMBERNOTFOUND;
1799
1800     VariantInit(&varg0);
1801     hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1802     if (FAILED(hr))
1803         return hr;
1804
1805     VariantInit(&varg1);
1806     hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr);
1807     if (FAILED(hr))
1808         goto done;
1809
1810     V_VT(pVarResult) = VT_DISPATCH;
1811
1812     ret = MsiOpenDatabaseW(V_BSTR(&varg0), V_BSTR(&varg1), &hdb);
1813     if (ret != ERROR_SUCCESS)
1814     {
1815         hr = DISP_E_EXCEPTION;
1816         goto done;
1817     }
1818
1819     hr = create_database(hdb, &dispatch);
1820     if (SUCCEEDED(hr))
1821         V_DISPATCH(pVarResult) = dispatch;
1822
1823 done:
1824     VariantClear(&varg0);
1825     VariantClear(&varg1);
1826     return hr;
1827 }
1828
1829 static HRESULT InstallerImpl_SummaryInformation(WORD wFlags,
1830                                                 DISPPARAMS* pDispParams,
1831                                                 VARIANT* pVarResult,
1832                                                 EXCEPINFO* pExcepInfo,
1833                                                 UINT* puArgErr)
1834 {
1835     if (!(wFlags & DISPATCH_METHOD))
1836         return DISP_E_MEMBERNOTFOUND;
1837
1838     FIXME("\n");
1839
1840     VariantInit(pVarResult);
1841     return S_OK;
1842 }
1843
1844 static HRESULT InstallerImpl_UILevel(WORD wFlags,
1845                                      DISPPARAMS* pDispParams,
1846                                      VARIANT* pVarResult,
1847                                      EXCEPINFO* pExcepInfo,
1848                                      UINT* puArgErr)
1849 {
1850     HRESULT hr;
1851     VARIANTARG varg0;
1852     INSTALLUILEVEL ui;
1853
1854     if (!(wFlags & DISPATCH_PROPERTYPUT) && !(wFlags & DISPATCH_PROPERTYGET))
1855         return DISP_E_MEMBERNOTFOUND;
1856
1857     if (wFlags & DISPATCH_PROPERTYPUT)
1858     {
1859         VariantInit(&varg0);
1860         hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1861         if (FAILED(hr))
1862             return hr;
1863
1864         ui = MsiSetInternalUI(V_I4(&varg0), NULL);
1865         if (ui == INSTALLUILEVEL_NOCHANGE)
1866             return DISP_E_EXCEPTION;
1867     }
1868     else if (wFlags & DISPATCH_PROPERTYGET)
1869     {
1870         ui = MsiSetInternalUI(INSTALLUILEVEL_NOCHANGE, NULL);
1871         if (ui == INSTALLUILEVEL_NOCHANGE)
1872             return DISP_E_EXCEPTION;
1873
1874         V_VT(pVarResult) = VT_I4;
1875         V_I4(pVarResult) = ui;
1876     }
1877
1878     return S_OK;
1879 }
1880
1881 static HRESULT InstallerImpl_EnableLog(WORD wFlags,
1882                                        DISPPARAMS* pDispParams,
1883                                        VARIANT* pVarResult,
1884                                        EXCEPINFO* pExcepInfo,
1885                                        UINT* puArgErr)
1886 {
1887     if (!(wFlags & DISPATCH_METHOD))
1888         return DISP_E_MEMBERNOTFOUND;
1889
1890     FIXME("\n");
1891
1892     VariantInit(pVarResult);
1893     return S_OK;
1894 }
1895
1896 static HRESULT InstallerImpl_InstallProduct(WORD wFlags,
1897                                             DISPPARAMS* pDispParams,
1898                                             VARIANT* pVarResult,
1899                                             EXCEPINFO* pExcepInfo,
1900                                             UINT* puArgErr)
1901 {
1902     UINT ret;
1903     HRESULT hr;
1904     VARIANTARG varg0, varg1;
1905
1906     if (!(wFlags & DISPATCH_METHOD))
1907         return DISP_E_MEMBERNOTFOUND;
1908
1909     VariantInit(&varg0);
1910     hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1911     if (FAILED(hr))
1912         return hr;
1913
1914     VariantInit(&varg1);
1915     hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr);
1916     if (FAILED(hr))
1917         goto done;
1918
1919     ret = MsiInstallProductW(V_BSTR(&varg0), V_BSTR(&varg1));
1920     if (ret != ERROR_SUCCESS)
1921     {
1922         hr = DISP_E_EXCEPTION;
1923         goto done;
1924     }
1925
1926 done:
1927     VariantClear(&varg0);
1928     VariantClear(&varg1);
1929     return hr;
1930 }
1931
1932 static HRESULT InstallerImpl_Version(WORD wFlags,
1933                                      VARIANT* pVarResult,
1934                                      EXCEPINFO* pExcepInfo,
1935                                      UINT* puArgErr)
1936 {
1937     HRESULT hr;
1938     DLLVERSIONINFO verinfo;
1939     WCHAR version[MAX_PATH];
1940
1941     static const WCHAR format[] = {
1942         '%','d','.','%','d','.','%','d','.','%','d',0};
1943
1944     if (!(wFlags & DISPATCH_PROPERTYGET))
1945         return DISP_E_MEMBERNOTFOUND;
1946
1947     verinfo.cbSize = sizeof(DLLVERSIONINFO);
1948     hr = DllGetVersion(&verinfo);
1949     if (FAILED(hr))
1950         return hr;
1951
1952     sprintfW(version, format, verinfo.dwMajorVersion, verinfo.dwMinorVersion,
1953              verinfo.dwBuildNumber, verinfo.dwPlatformID);
1954
1955     V_VT(pVarResult) = VT_BSTR;
1956     V_BSTR(pVarResult) = SysAllocString(version);
1957     return S_OK;
1958 }
1959
1960 static HRESULT InstallerImpl_LastErrorRecord(WORD wFlags,
1961                                              DISPPARAMS* pDispParams,
1962                                              VARIANT* pVarResult,
1963                                              EXCEPINFO* pExcepInfo,
1964                                              UINT* puArgErr)
1965 {
1966     if (!(wFlags & DISPATCH_METHOD))
1967         return DISP_E_MEMBERNOTFOUND;
1968
1969     FIXME("\n");
1970
1971     VariantInit(pVarResult);
1972     return S_OK;
1973 }
1974
1975 static HRESULT InstallerImpl_RegistryValue(WORD wFlags,
1976                                            DISPPARAMS* pDispParams,
1977                                            VARIANT* pVarResult,
1978                                            EXCEPINFO* pExcepInfo,
1979                                            UINT* puArgErr)
1980 {
1981     UINT ret;
1982     HKEY hkey = NULL;
1983     HRESULT hr;
1984     UINT posValue;
1985     DWORD type, size;
1986     LPWSTR szString = NULL;
1987     VARIANTARG varg0, varg1, varg2;
1988
1989     if (!(wFlags & DISPATCH_METHOD))
1990         return DISP_E_MEMBERNOTFOUND;
1991
1992     VariantInit(&varg0);
1993     hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1994     if (FAILED(hr))
1995         return hr;
1996
1997     VariantInit(&varg1);
1998     hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr);
1999     if (FAILED(hr))
2000         goto done;
2001
2002     /* Save valuePos so we can save puArgErr if we are unable to do our type
2003      * conversions.
2004      */
2005     posValue = 2;
2006     VariantInit(&varg2);
2007     hr = DispGetParam_CopyOnly(pDispParams, &posValue, &varg2);
2008     if (FAILED(hr))
2009         goto done;
2010
2011     if (V_I4(&varg0) >= REG_INDEX_CLASSES_ROOT &&
2012         V_I4(&varg0) <= REG_INDEX_DYN_DATA)
2013     {
2014         V_I4(&varg0) |= (UINT_PTR)HKEY_CLASSES_ROOT;
2015     }
2016
2017     ret = RegOpenKeyW((HKEY)(UINT_PTR)V_I4(&varg0), V_BSTR(&varg1), &hkey);
2018
2019     /* Only VT_EMPTY case can do anything if the key doesn't exist. */
2020     if (ret != ERROR_SUCCESS && V_VT(&varg2) != VT_EMPTY)
2021     {
2022         hr = DISP_E_BADINDEX;
2023         goto done;
2024     }
2025
2026     /* Third parameter can be VT_EMPTY, VT_I4, or VT_BSTR */
2027     switch (V_VT(&varg2))
2028     {
2029         /* Return VT_BOOL clarifying whether registry key exists or not. */
2030         case VT_EMPTY:
2031             V_VT(pVarResult) = VT_BOOL;
2032             V_BOOL(pVarResult) = (ret == ERROR_SUCCESS);
2033             break;
2034
2035         /* Return the value of specified key if it exists. */
2036         case VT_BSTR:
2037             ret = RegQueryValueExW(hkey, V_BSTR(&varg2),
2038                                    NULL, NULL, NULL, &size);
2039             if (ret != ERROR_SUCCESS)
2040             {
2041                 hr = DISP_E_BADINDEX;
2042                 goto done;
2043             }
2044
2045             szString = msi_alloc(size);
2046             if (!szString)
2047             {
2048                 hr = E_OUTOFMEMORY;
2049                 goto done;
2050             }
2051
2052             ret = RegQueryValueExW(hkey, V_BSTR(&varg2), NULL,
2053                                    &type, (LPBYTE)szString, &size);
2054             if (ret != ERROR_SUCCESS)
2055             {
2056                 msi_free(szString);
2057                 hr = DISP_E_BADINDEX;
2058                 goto done;
2059             }
2060
2061             variant_from_registry_value(pVarResult, type,
2062                                         (LPBYTE)szString, size);
2063             msi_free(szString);
2064             break;
2065
2066         /* Try to make it into VT_I4, can use VariantChangeType for this. */
2067         default:
2068             hr = VariantChangeType(&varg2, &varg2, 0, VT_I4);
2069             if (FAILED(hr))
2070             {
2071                 if (hr == DISP_E_TYPEMISMATCH)
2072                     *puArgErr = posValue;
2073
2074                 goto done;
2075             }
2076
2077             /* Retrieve class name or maximum value name or subkey name size. */
2078             if (!V_I4(&varg2))
2079                 ret = RegQueryInfoKeyW(hkey, NULL, &size, NULL, NULL, NULL,
2080                                        NULL, NULL, NULL, NULL, NULL, NULL);
2081             else if (V_I4(&varg2) > 0)
2082                 ret = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL,
2083                                        NULL, NULL, &size, NULL, NULL, NULL);
2084             else /* V_I4(&varg2) < 0 */
2085                 ret = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, &size,
2086                                        NULL, NULL, NULL, NULL, NULL, NULL);
2087
2088             if (ret != ERROR_SUCCESS)
2089                 goto done;
2090
2091             szString = msi_alloc(++size * sizeof(WCHAR));
2092             if (!szString)
2093             {
2094                 hr = E_OUTOFMEMORY;
2095                 goto done;
2096             }
2097
2098             if (!V_I4(&varg2))
2099                 ret = RegQueryInfoKeyW(hkey, szString, &size,NULL, NULL, NULL,
2100                                        NULL, NULL, NULL, NULL, NULL, NULL);
2101             else if (V_I4(&varg2) > 0)
2102                 ret = RegEnumValueW(hkey, V_I4(&varg2)-1, szString,
2103                                     &size, 0, 0, NULL, NULL);
2104             else /* V_I4(&varg2) < 0 */
2105                 ret = RegEnumKeyW(hkey, -1 - V_I4(&varg2), szString, size);
2106
2107             if (ret == ERROR_SUCCESS)
2108             {
2109                 V_VT(pVarResult) = VT_BSTR;
2110                 V_BSTR(pVarResult) = SysAllocString(szString);
2111             }
2112
2113             msi_free(szString);
2114     }
2115
2116 done:
2117     VariantClear(&varg0);
2118     VariantClear(&varg1);
2119     VariantClear(&varg2);
2120     RegCloseKey(hkey);
2121     return hr;
2122 }
2123
2124 static HRESULT InstallerImpl_Environment(WORD wFlags,
2125                                          DISPPARAMS* pDispParams,
2126                                          VARIANT* pVarResult,
2127                                          EXCEPINFO* pExcepInfo,
2128                                          UINT* puArgErr)
2129 {
2130     if (!(wFlags & DISPATCH_METHOD))
2131         return DISP_E_MEMBERNOTFOUND;
2132
2133     FIXME("\n");
2134
2135     VariantInit(pVarResult);
2136     return S_OK;
2137 }
2138
2139 static HRESULT InstallerImpl_FileAttributes(WORD wFlags,
2140                                             DISPPARAMS* pDispParams,
2141                                             VARIANT* pVarResult,
2142                                             EXCEPINFO* pExcepInfo,
2143                                             UINT* puArgErr)
2144 {
2145     if (!(wFlags & DISPATCH_METHOD))
2146         return DISP_E_MEMBERNOTFOUND;
2147
2148     FIXME("\n");
2149
2150     VariantInit(pVarResult);
2151     return S_OK;
2152 }
2153
2154 static HRESULT InstallerImpl_FileSize(WORD wFlags,
2155                                       DISPPARAMS* pDispParams,
2156                                       VARIANT* pVarResult,
2157                                       EXCEPINFO* pExcepInfo,
2158                                       UINT* puArgErr)
2159 {
2160     if (!(wFlags & DISPATCH_METHOD))
2161         return DISP_E_MEMBERNOTFOUND;
2162
2163     FIXME("\n");
2164
2165     VariantInit(pVarResult);
2166     return S_OK;
2167 }
2168
2169 static HRESULT InstallerImpl_FileVersion(WORD wFlags,
2170                                          DISPPARAMS* pDispParams,
2171                                          VARIANT* pVarResult,
2172                                          EXCEPINFO* pExcepInfo,
2173                                          UINT* puArgErr)
2174 {
2175     if (!(wFlags & DISPATCH_METHOD))
2176         return DISP_E_MEMBERNOTFOUND;
2177
2178     FIXME("\n");
2179
2180     VariantInit(pVarResult);
2181     return S_OK;
2182 }
2183
2184 static HRESULT InstallerImpl_ProductState(WORD wFlags,
2185                                           DISPPARAMS* pDispParams,
2186                                           VARIANT* pVarResult,
2187                                           EXCEPINFO* pExcepInfo,
2188                                           UINT* puArgErr)
2189 {
2190     HRESULT hr;
2191     VARIANTARG varg0;
2192
2193     if (!(wFlags & DISPATCH_PROPERTYGET))
2194         return DISP_E_MEMBERNOTFOUND;
2195
2196     VariantInit(&varg0);
2197     hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
2198     if (FAILED(hr))
2199         return hr;
2200
2201     V_VT(pVarResult) = VT_I4;
2202     V_I4(pVarResult) = MsiQueryProductStateW(V_BSTR(&varg0));
2203
2204     VariantClear(&varg0);
2205     return S_OK;
2206 }
2207
2208 static HRESULT InstallerImpl_ProductInfo(WORD wFlags,
2209                                          DISPPARAMS* pDispParams,
2210                                          VARIANT* pVarResult,
2211                                          EXCEPINFO* pExcepInfo,
2212                                          UINT* puArgErr)
2213 {
2214     UINT ret;
2215     HRESULT hr;
2216     DWORD size;
2217     LPWSTR str = NULL;
2218     VARIANTARG varg0, varg1;
2219
2220     if (!(wFlags & DISPATCH_PROPERTYGET))
2221         return DISP_E_MEMBERNOTFOUND;
2222
2223     VariantInit(&varg0);
2224     hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
2225     if (FAILED(hr))
2226         return hr;
2227
2228     VariantInit(&varg1);
2229     hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr);
2230     if (FAILED(hr))
2231         goto done;
2232
2233     V_VT(pVarResult) = VT_BSTR;
2234     V_BSTR(pVarResult) = NULL;
2235
2236     ret = MsiGetProductInfoW(V_BSTR(&varg0), V_BSTR(&varg1), NULL, &size);
2237     if (ret != ERROR_SUCCESS)
2238     {
2239         hr = DISP_E_EXCEPTION;
2240         goto done;
2241     }
2242
2243     str = msi_alloc(++size * sizeof(WCHAR));
2244     if (!str)
2245     {
2246         hr = E_OUTOFMEMORY;
2247         goto done;
2248     }
2249
2250     ret = MsiGetProductInfoW(V_BSTR(&varg0), V_BSTR(&varg1), str, &size);
2251     if (ret != ERROR_SUCCESS)
2252     {
2253         hr = DISP_E_EXCEPTION;
2254         goto done;
2255     }
2256
2257     V_BSTR(pVarResult) = SysAllocString(str);
2258     hr = S_OK;
2259
2260 done:
2261     msi_free(str);
2262     VariantClear(&varg0);
2263     VariantClear(&varg1);
2264     return hr;
2265 }
2266
2267 static HRESULT InstallerImpl_Products(WORD flags,
2268                                       DISPPARAMS* pDispParams,
2269                                       VARIANT* result,
2270                                       EXCEPINFO* pExcepInfo,
2271                                       UINT* puArgErr)
2272 {
2273     IDispatch *dispatch;
2274     HRESULT hr;
2275
2276     if (!(flags & DISPATCH_PROPERTYGET))
2277         return DISP_E_MEMBERNOTFOUND;
2278
2279     hr = create_list(NULL, &dispatch);
2280     if (FAILED(hr))
2281         return hr;
2282
2283     V_VT(result) = VT_DISPATCH;
2284     V_DISPATCH(result) = dispatch;
2285
2286     return hr;
2287 }
2288
2289 static HRESULT InstallerImpl_RelatedProducts(WORD flags,
2290                                              DISPPARAMS* pDispParams,
2291                                              VARIANT* result,
2292                                              EXCEPINFO* pExcepInfo,
2293                                              UINT* puArgErr)
2294 {
2295     IDispatch* dispatch;
2296     VARIANTARG related;
2297     HRESULT hr;
2298
2299     if (!(flags & DISPATCH_PROPERTYGET))
2300         return DISP_E_MEMBERNOTFOUND;
2301
2302     VariantInit(&related);
2303     hr = DispGetParam(pDispParams, 0, VT_BSTR, &related, puArgErr);
2304     if (FAILED(hr))
2305         return hr;
2306
2307     hr = create_list(V_BSTR(&related), &dispatch);
2308     VariantClear(&related);
2309
2310     V_VT(result) = VT_DISPATCH;
2311     V_DISPATCH(result) = dispatch;
2312
2313     return hr;
2314 }
2315
2316 static HRESULT installer_invoke(
2317         AutomationObject* This,
2318         DISPID dispIdMember,
2319         REFIID riid,
2320         LCID lcid,
2321         WORD wFlags,
2322         DISPPARAMS* pDispParams,
2323         VARIANT* pVarResult,
2324         EXCEPINFO* pExcepInfo,
2325         UINT* puArgErr)
2326 {
2327     switch (dispIdMember)
2328     {
2329         case DISPID_INSTALLER_CREATERECORD:
2330             return InstallerImpl_CreateRecord(wFlags, pDispParams,
2331                                               pVarResult, pExcepInfo, puArgErr);
2332
2333         case DISPID_INSTALLER_OPENPACKAGE:
2334             return InstallerImpl_OpenPackage(This, wFlags, pDispParams,
2335                                              pVarResult, pExcepInfo, puArgErr);
2336
2337         case DISPID_INSTALLER_OPENPRODUCT:
2338             return InstallerImpl_OpenProduct(wFlags, pDispParams,
2339                                              pVarResult, pExcepInfo, puArgErr);
2340
2341         case DISPID_INSTALLER_OPENDATABASE:
2342             return InstallerImpl_OpenDatabase(wFlags, pDispParams,
2343                                               pVarResult, pExcepInfo, puArgErr);
2344
2345         case DISPID_INSTALLER_SUMMARYINFORMATION:
2346             return InstallerImpl_SummaryInformation(wFlags, pDispParams,
2347                                                     pVarResult, pExcepInfo,
2348                                                     puArgErr);
2349
2350         case DISPID_INSTALLER_UILEVEL:
2351             return InstallerImpl_UILevel(wFlags, pDispParams,
2352                                          pVarResult, pExcepInfo, puArgErr);
2353
2354         case DISPID_INSTALLER_ENABLELOG:
2355             return InstallerImpl_EnableLog(wFlags, pDispParams,
2356                                            pVarResult, pExcepInfo, puArgErr);
2357
2358         case DISPID_INSTALLER_INSTALLPRODUCT:
2359             return InstallerImpl_InstallProduct(wFlags, pDispParams,
2360                                                 pVarResult, pExcepInfo,
2361                                                 puArgErr);
2362
2363         case DISPID_INSTALLER_VERSION:
2364             return InstallerImpl_Version(wFlags, pVarResult,
2365                                          pExcepInfo, puArgErr);
2366
2367         case DISPID_INSTALLER_LASTERRORRECORD:
2368             return InstallerImpl_LastErrorRecord(wFlags, pDispParams,
2369                                                  pVarResult, pExcepInfo,
2370                                                  puArgErr);
2371
2372         case DISPID_INSTALLER_REGISTRYVALUE:
2373             return InstallerImpl_RegistryValue(wFlags, pDispParams,
2374                                                pVarResult, pExcepInfo,
2375                                                puArgErr);
2376
2377         case DISPID_INSTALLER_ENVIRONMENT:
2378             return InstallerImpl_Environment(wFlags, pDispParams,
2379                                              pVarResult, pExcepInfo, puArgErr);
2380
2381         case DISPID_INSTALLER_FILEATTRIBUTES:
2382             return InstallerImpl_FileAttributes(wFlags, pDispParams,
2383                                                 pVarResult, pExcepInfo,
2384                                                 puArgErr);
2385
2386         case DISPID_INSTALLER_FILESIZE:
2387             return InstallerImpl_FileSize(wFlags, pDispParams,
2388                                           pVarResult, pExcepInfo, puArgErr);
2389
2390         case DISPID_INSTALLER_FILEVERSION:
2391             return InstallerImpl_FileVersion(wFlags, pDispParams,
2392                                              pVarResult, pExcepInfo, puArgErr);
2393
2394         case DISPID_INSTALLER_PRODUCTSTATE:
2395             return InstallerImpl_ProductState(wFlags, pDispParams,
2396                                               pVarResult, pExcepInfo, puArgErr);
2397
2398         case DISPID_INSTALLER_PRODUCTINFO:
2399             return InstallerImpl_ProductInfo(wFlags, pDispParams,
2400                                              pVarResult, pExcepInfo, puArgErr);
2401
2402         case DISPID_INSTALLER_PRODUCTS:
2403             return InstallerImpl_Products(wFlags, pDispParams,
2404                                           pVarResult, pExcepInfo, puArgErr);
2405
2406         case DISPID_INSTALLER_RELATEDPRODUCTS:
2407             return InstallerImpl_RelatedProducts(wFlags, pDispParams,
2408                                                  pVarResult, pExcepInfo,
2409                                                  puArgErr);
2410
2411         default:
2412             return DISP_E_MEMBERNOTFOUND;
2413     }
2414 }
2415
2416 HRESULT create_msiserver(IUnknown *outer, void **ppObj)
2417 {
2418     AutomationObject *installer;
2419     HRESULT hr;
2420
2421     TRACE("(%p %p)\n", outer, ppObj);
2422
2423     if (outer)
2424         return CLASS_E_NOAGGREGATION;
2425
2426     installer = msi_alloc(sizeof(AutomationObject));
2427     if (!installer) return E_OUTOFMEMORY;
2428
2429     hr = init_automation_object(installer, 0, Installer_tid);
2430     if (hr != S_OK)
2431     {
2432         msi_free(installer);
2433         return hr;
2434     }
2435
2436     *ppObj = &installer->IDispatch_iface;
2437
2438     return hr;
2439 }
2440
2441 HRESULT create_session(MSIHANDLE msiHandle, IDispatch *installer, IDispatch **disp)
2442 {
2443     SessionObject *session;
2444     HRESULT hr;
2445
2446     session = msi_alloc(sizeof(SessionObject));
2447     if (!session) return E_OUTOFMEMORY;
2448
2449     hr = init_automation_object(&session->autoobj, msiHandle, Session_tid);
2450     if (hr != S_OK)
2451     {
2452         msi_free(session);
2453         return hr;
2454     }
2455
2456     session->installer = installer;
2457     *disp = &session->autoobj.IDispatch_iface;
2458
2459     return hr;
2460 }
2461
2462 static HRESULT create_database(MSIHANDLE msiHandle, IDispatch **dispatch)
2463 {
2464     AutomationObject *database;
2465     HRESULT hr;
2466
2467     TRACE("(%d %p)\n", msiHandle, dispatch);
2468
2469     database = msi_alloc(sizeof(AutomationObject));
2470     if (!database) return E_OUTOFMEMORY;
2471
2472     hr = init_automation_object(database, msiHandle, Database_tid);
2473     if (hr != S_OK)
2474     {
2475         msi_free(database);
2476         return hr;
2477     }
2478
2479     *dispatch = &database->IDispatch_iface;
2480
2481     return hr;
2482 }
2483
2484 static HRESULT create_view(MSIHANDLE msiHandle, IDispatch **dispatch)
2485 {
2486     AutomationObject *view;
2487     HRESULT hr;
2488
2489     TRACE("(%d %p)\n", msiHandle, dispatch);
2490
2491     view = msi_alloc(sizeof(AutomationObject));
2492     if (!view) return E_OUTOFMEMORY;
2493
2494     hr = init_automation_object(view, msiHandle, View_tid);
2495     if (hr != S_OK)
2496     {
2497         msi_free(view);
2498         return hr;
2499     }
2500
2501     *dispatch = &view->IDispatch_iface;
2502
2503     return hr;
2504 }
2505
2506 static HRESULT create_summaryinfo(MSIHANDLE msiHandle, IDispatch **disp)
2507 {
2508     AutomationObject *info;
2509     HRESULT hr;
2510
2511     info = msi_alloc(sizeof(*info));
2512     if (!info) return E_OUTOFMEMORY;
2513
2514     hr = init_automation_object(info, msiHandle, SummaryInfo_tid);
2515     if (hr != S_OK)
2516     {
2517         msi_free(info);
2518         return hr;
2519     }
2520
2521     *disp = &info->IDispatch_iface;
2522
2523     return hr;
2524 }