2 * Implementation of OLE Automation for Microsoft Installer (msi.dll)
4 * Copyright 2007 Misha Koshelev
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.
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.
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
33 #include "wine/debug.h"
34 #include "wine/unicode.h"
36 #include "msiserver.h"
37 #include "msiserver_dispids.h"
39 WINE_DEFAULT_DEBUG_CHANNEL(msi);
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.
46 typedef interface AutomationObject AutomationObject;
48 interface AutomationObject {
50 * VTables - We provide IDispatch, IProvideClassInfo, IProvideClassInfo2, IProvideMultipleClassInfo
52 const IDispatchVtbl *lpVtbl;
53 const IProvideMultipleClassInfoVtbl *lpvtblIProvideMultipleClassInfo;
55 /* Object reference count */
58 /* Clsid for this class and it's appropriate ITypeInfo object */
62 /* The MSI handle of the current object */
65 /* A function that is called from AutomationObject::Invoke, specific to this type of object. */
66 HRESULT (STDMETHODCALLTYPE *funcInvoke)(
67 AutomationObject* This,
72 DISPPARAMS* pDispParams,
74 EXCEPINFO* pExcepInfo,
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);
83 * Structures for additional data required by specific automation objects
92 /* The parent Installer object */
93 IDispatch *pInstaller;
97 static const struct IDispatchVtbl AutomationObject_Vtbl;
98 static const struct IProvideMultipleClassInfoVtbl AutomationObject_IProvideMultipleClassInfo_Vtbl;
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)
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'};
108 TRACE("(%p)->(%s,%d)\n", iface, debugstr_guid(clsid), lcid);
110 /* Load registered type library */
111 hr = LoadRegTypeLib(&LIBID_WindowsInstaller, 1, 0, lcid, &pLib);
113 hr = LoadTypeLib(szMsiServer, &pLib);
115 ERR("Could not load msiserver.tlb\n");
120 /* Get type information for object */
121 hr = ITypeLib_GetTypeInfoOfGuid(pLib, clsid, &pInfo);
122 ITypeLib_Release(pLib);
124 ERR("Could not load ITypeInfo for %s\n", debugstr_guid(clsid));
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)
139 AutomationObject *object;
142 TRACE("(%ld,%p,%p,%s,%p,%p,%ld)\n", (unsigned long)msiHandle, pUnkOuter, ppObj, debugstr_guid(clsid), funcInvoke, funcFree, sizetPrivateData);
145 return CLASS_E_NOAGGREGATION;
147 object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AutomationObject)+sizetPrivateData);
149 /* Set all the VTable references */
150 object->lpVtbl = &AutomationObject_Vtbl;
151 object->lpvtblIProvideMultipleClassInfo = &AutomationObject_IProvideMultipleClassInfo_Vtbl;
154 /* Store data that was passed */
155 object->msiHandle = msiHandle;
156 object->clsid = (LPCLSID)clsid;
157 object->funcInvoke = funcInvoke;
158 object->funcFree = funcFree;
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);
164 HeapFree(GetProcessHeap(), 0, object);
173 /* Macros to get pointer to AutomationObject from the other VTables. */
174 static inline AutomationObject *obj_from_IProvideMultipleClassInfo( IProvideMultipleClassInfo *iface )
176 return (AutomationObject *)((char*)iface - FIELD_OFFSET(AutomationObject, lpvtblIProvideMultipleClassInfo));
179 /* Macro to get pointer to private object data */
180 static inline void *private_data( AutomationObject *This )
186 * AutomationObject methods
189 /*** IUnknown methods ***/
190 static HRESULT WINAPI AutomationObject_QueryInterface(IDispatch* iface, REFIID riid, void** ppvObject)
192 AutomationObject *This = (AutomationObject *)iface;
194 TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject);
196 if (ppvObject == NULL)
201 if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IDispatch) || IsEqualGUID(riid, This->clsid))
203 else if (IsEqualGUID(riid, &IID_IProvideClassInfo) ||
204 IsEqualGUID(riid, &IID_IProvideClassInfo2) ||
205 IsEqualGUID(riid, &IID_IProvideMultipleClassInfo))
206 *ppvObject = &This->lpvtblIProvideMultipleClassInfo;
209 TRACE("() : asking for unsupported interface %s\n",debugstr_guid(riid));
210 return E_NOINTERFACE;
214 * Query Interface always increases the reference count by one when it is
217 IClassFactory_AddRef(iface);
222 static ULONG WINAPI AutomationObject_AddRef(IDispatch* iface)
224 AutomationObject *This = (AutomationObject *)iface;
226 TRACE("(%p/%p)\n", iface, This);
228 return InterlockedIncrement(&This->ref);
231 static ULONG WINAPI AutomationObject_Release(IDispatch* iface)
233 AutomationObject *This = (AutomationObject *)iface;
234 ULONG ref = InterlockedDecrement(&This->ref);
236 TRACE("(%p/%p)\n", iface, This);
240 if (This->funcFree) This->funcFree(This);
241 MsiCloseHandle(This->msiHandle);
242 HeapFree(GetProcessHeap(), 0, This);
248 /*** IDispatch methods ***/
249 static HRESULT WINAPI AutomationObject_GetTypeInfoCount(
253 AutomationObject *This = (AutomationObject *)iface;
255 TRACE("(%p/%p)->(%p)\n", iface, This, pctinfo);
260 static HRESULT WINAPI AutomationObject_GetTypeInfo(
266 AutomationObject *This = (AutomationObject *)iface;
267 TRACE("(%p/%p)->(%d,%d,%p)\n", iface, This, iTInfo, lcid, ppTInfo);
269 ITypeInfo_AddRef(This->iTypeInfo);
270 *ppTInfo = This->iTypeInfo;
274 static HRESULT WINAPI AutomationObject_GetIDsOfNames(
282 AutomationObject *This = (AutomationObject *)iface;
284 TRACE("(%p/%p)->(%p,%p,%d,%d,%p)\n", iface, This, riid, rgszNames, cNames, lcid, rgDispId);
286 if (!IsEqualGUID(riid, &IID_NULL)) return E_INVALIDARG;
287 hr = ITypeInfo_GetIDsOfNames(This->iTypeInfo, rgszNames, cNames, rgDispId);
288 if (hr == DISP_E_UNKNOWNNAME)
291 for (idx=0; idx<cNames; idx++)
293 if (rgDispId[idx] == DISPID_UNKNOWN)
294 FIXME("Unknown member %s, clsid %s\n", debugstr_w(rgszNames[idx]), debugstr_guid(This->clsid));
300 /* Maximum number of allowed function parameters+1 */
301 #define MAX_FUNC_PARAMS 20
303 /* Some error checking is done here to simplify individual object function invocation */
304 static HRESULT WINAPI AutomationObject_Invoke(
310 DISPPARAMS* pDispParams,
312 EXCEPINFO* pExcepInfo,
315 AutomationObject *This = (AutomationObject *)iface;
317 unsigned int uArgErr;
318 VARIANT varResultDummy;
319 BSTR bstrName = NULL;
321 TRACE("(%p/%p)->(%d,%p,%d,%d,%p,%p,%p,%p)\n", iface, This, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
323 if (!IsEqualIID(riid, &IID_NULL))
325 ERR("riid was %s instead of IID_NULL\n", debugstr_guid(riid));
326 return DISP_E_UNKNOWNNAME;
329 if (wFlags & DISPATCH_PROPERTYGET && !pVarResult)
331 ERR("NULL pVarResult not allowed when DISPATCH_PROPERTYGET specified\n");
332 return DISP_E_PARAMNOTOPTIONAL;
335 /* This simplifies our individual object invocation functions */
336 if (puArgErr == NULL) puArgErr = &uArgErr;
337 if (pVarResult == NULL) pVarResult = &varResultDummy;
339 /* Assume return type is void unless determined otherwise */
340 VariantInit(pVarResult);
342 /* If we are tracing, we want to see the name of the member we are invoking */
345 ITypeInfo_GetDocumentation(This->iTypeInfo, dispIdMember, &bstrName, NULL, NULL, NULL);
346 TRACE("Method %d, %s\n", dispIdMember, debugstr_w(bstrName));
349 hr = This->funcInvoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
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));
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];
365 if (FAILED(ITypeInfo_GetNames(This->iTypeInfo, dispIdMember, bstrParamNames,
366 MAX_FUNC_PARAMS, &namesNo)))
368 TRACE("Failed to retrieve names for dispIdMember %d\n", dispIdMember);
372 memset(szExceptionDescription, 0, sizeof(szExceptionDescription));
373 for (i=0; i<namesNo; i++)
375 if (bFirst) bFirst = FALSE;
377 lstrcpyW(&szExceptionDescription[lstrlenW(szExceptionDescription)], szComma);
379 lstrcpyW(&szExceptionDescription[lstrlenW(szExceptionDescription)], bstrParamNames[i]);
380 SysFreeString(bstrParamNames[i]);
383 memset(pExcepInfo, 0, sizeof(EXCEPINFO));
384 pExcepInfo->wCode = 1000;
385 pExcepInfo->bstrSource = szExceptionSource;
386 pExcepInfo->bstrDescription = szExceptionDescription;
387 hr = DISP_E_EXCEPTION;
391 /* Make sure we free the return variant if it is our dummy variant */
392 if (pVarResult == &varResultDummy) VariantClear(pVarResult);
394 /* Free function name if we retrieved it */
395 if (bstrName) SysFreeString(bstrName);
397 TRACE("Returning 0x%08x, %s\n", hr, SUCCEEDED(hr) ? "ok" : "not ok");
402 static const struct IDispatchVtbl AutomationObject_Vtbl =
404 AutomationObject_QueryInterface,
405 AutomationObject_AddRef,
406 AutomationObject_Release,
407 AutomationObject_GetTypeInfoCount,
408 AutomationObject_GetTypeInfo,
409 AutomationObject_GetIDsOfNames,
410 AutomationObject_Invoke
414 * IProvideMultipleClassInfo methods
417 static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_QueryInterface(
418 IProvideMultipleClassInfo* iface,
422 AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
423 return AutomationObject_QueryInterface((IDispatch *)This, riid, ppvoid);
426 static ULONG WINAPI AutomationObject_IProvideMultipleClassInfo_AddRef(IProvideMultipleClassInfo* iface)
428 AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
429 return AutomationObject_AddRef((IDispatch *)This);
432 static ULONG WINAPI AutomationObject_IProvideMultipleClassInfo_Release(IProvideMultipleClassInfo* iface)
434 AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
435 return AutomationObject_Release((IDispatch *)This);
438 static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_GetClassInfo(IProvideMultipleClassInfo* iface, ITypeInfo** ppTI)
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);
445 static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_GetGUID(IProvideMultipleClassInfo* iface, DWORD dwGuidKind, GUID* pGUID)
447 AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
448 TRACE("(%p/%p)->(%d,%s)\n", iface, This, dwGuidKind, debugstr_guid(pGUID));
450 if (dwGuidKind != GUIDKIND_DEFAULT_SOURCE_DISP_IID)
453 *pGUID = *This->clsid;
458 static HRESULT WINAPI AutomationObject_GetMultiTypeInfoCount(IProvideMultipleClassInfo* iface, ULONG* pcti)
460 AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
462 TRACE("(%p/%p)->(%p)\n", iface, This, pcti);
467 static HRESULT WINAPI AutomationObject_GetInfoOfIndex(IProvideMultipleClassInfo* iface,
470 ITypeInfo** pptiCoClass,
472 ULONG* pcdispidReserved,
476 AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
478 TRACE("(%p/%p)->(%d,%d,%p,%p,%p,%p,%p)\n", iface, This, iti, dwFlags, pptiCoClass, pdwTIFlags, pcdispidReserved, piidPrimary, piidSource);
483 if (dwFlags & MULTICLASSINFO_GETTYPEINFO)
484 load_type_info((IDispatch *)This, pptiCoClass, This->clsid, 0);
486 if (dwFlags & MULTICLASSINFO_GETNUMRESERVEDDISPIDS)
489 *pcdispidReserved = 0;
492 if (dwFlags & MULTICLASSINFO_GETIIDPRIMARY){
493 *piidPrimary = *This->clsid;
496 if (dwFlags & MULTICLASSINFO_GETIIDSOURCE){
497 *piidSource = *This->clsid;
503 static const IProvideMultipleClassInfoVtbl AutomationObject_IProvideMultipleClassInfo_Vtbl =
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
515 * Individual Object Invocation Functions
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 */
526 /* position is counted backwards */
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;
535 /* FIXME: is this how to handle named args? */
536 for (pos=0; pos<pdispparams->cNamedArgs; pos++)
537 if (pdispparams->rgdispidNamedArgs[pos] == *position) break;
539 if (pos==pdispparams->cNamedArgs)
540 return DISP_E_PARAMNOTFOUND;
543 return VariantCopyInd(pvarResult,
544 &pdispparams->rgvarg[pos]);
547 static HRESULT WINAPI RecordImpl_Invoke(
548 AutomationObject* This,
553 DISPPARAMS* pDispParams,
555 EXCEPINFO* pExcepInfo,
561 VARIANTARG varg0, varg1;
567 switch (dispIdMember)
569 case DISPID_RECORD_FIELDCOUNT:
570 if (wFlags & DISPATCH_PROPERTYGET) {
571 V_VT(pVarResult) = VT_I4;
572 V_I4(pVarResult) = MsiRecordGetFieldCount(This->msiHandle);
574 else return DISP_E_MEMBERNOTFOUND;
577 case DISPID_RECORD_STRINGDATA:
578 if (wFlags & DISPATCH_PROPERTYGET) {
579 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
580 if (FAILED(hr)) return hr;
581 V_VT(pVarResult) = VT_BSTR;
582 V_BSTR(pVarResult) = NULL;
583 if ((ret = MsiRecordGetStringW(This->msiHandle, V_I4(&varg0), NULL, &dwLen)) == ERROR_SUCCESS)
585 if (!(szString = msi_alloc((++dwLen)*sizeof(WCHAR))))
586 ERR("Out of memory\n");
587 else if ((ret = MsiRecordGetStringW(This->msiHandle, V_I4(&varg0), szString, &dwLen)) == ERROR_SUCCESS)
588 V_BSTR(pVarResult) = SysAllocString(szString);
591 if (ret != ERROR_SUCCESS)
592 ERR("MsiRecordGetString returned %d\n", ret);
593 } else if (wFlags & DISPATCH_PROPERTYPUT) {
594 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
595 if (FAILED(hr)) return hr;
596 hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_BSTR, &varg1, puArgErr);
597 if (FAILED(hr)) return hr;
598 if ((ret = MsiRecordSetStringW(This->msiHandle, V_I4(&varg0), V_BSTR(&varg1))) != ERROR_SUCCESS)
600 VariantClear(&varg1);
601 ERR("MsiRecordSetString returned %d\n", ret);
602 return DISP_E_EXCEPTION;
605 else return DISP_E_MEMBERNOTFOUND;
608 case DISPID_RECORD_INTEGERDATA:
609 if (wFlags & DISPATCH_PROPERTYGET) {
610 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
611 if (FAILED(hr)) return hr;
612 V_VT(pVarResult) = VT_I4;
613 V_I4(pVarResult) = MsiRecordGetInteger(This->msiHandle, V_I4(&varg0));
614 } else if (wFlags & DISPATCH_PROPERTYPUT) {
615 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
616 if (FAILED(hr)) return hr;
617 hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_I4, &varg1, puArgErr);
618 if (FAILED(hr)) return hr;
619 if ((ret = MsiRecordSetInteger(This->msiHandle, V_I4(&varg0), V_I4(&varg1))) != ERROR_SUCCESS)
621 ERR("MsiRecordSetInteger returned %d\n", ret);
622 return DISP_E_EXCEPTION;
625 else return DISP_E_MEMBERNOTFOUND;
629 return DISP_E_MEMBERNOTFOUND;
632 VariantClear(&varg1);
633 VariantClear(&varg0);
638 static HRESULT WINAPI StringListImpl_Invoke(
639 AutomationObject* This,
644 DISPPARAMS* pDispParams,
646 EXCEPINFO* pExcepInfo,
649 StringListData *data = (StringListData *)private_data(This);
655 switch (dispIdMember)
657 case DISPID_STRINGLIST_ITEM:
658 if (wFlags & DISPATCH_PROPERTYGET) {
659 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
660 if (FAILED(hr)) return hr;
661 if (V_I4(&varg0) < 0 || V_I4(&varg0) >= data->iCount)
662 return DISP_E_BADINDEX;
663 V_VT(pVarResult) = VT_BSTR;
664 V_BSTR(pVarResult) = SysAllocString(data->pszStrings[V_I4(&varg0)]);
666 else return DISP_E_MEMBERNOTFOUND;
669 case DISPID_STRINGLIST_COUNT:
670 if (wFlags & DISPATCH_PROPERTYGET) {
671 V_VT(pVarResult) = VT_I4;
672 V_I4(pVarResult) = data->iCount;
674 else return DISP_E_MEMBERNOTFOUND;
678 return DISP_E_MEMBERNOTFOUND;
681 VariantClear(&varg0);
686 static void WINAPI StringListImpl_Free(AutomationObject *This)
688 StringListData *data = private_data(This);
691 for (idx=0; idx<data->iCount; idx++)
692 SysFreeString(data->pszStrings[idx]);
693 HeapFree(GetProcessHeap(), 0, data->pszStrings);
696 static HRESULT WINAPI ViewImpl_Invoke(
697 AutomationObject* This,
702 DISPPARAMS* pDispParams,
704 EXCEPINFO* pExcepInfo,
708 IDispatch *pDispatch = NULL;
710 VARIANTARG varg0, varg1;
716 switch (dispIdMember)
718 case DISPID_VIEW_EXECUTE:
719 if (wFlags & DISPATCH_METHOD)
721 hr = DispGetParam(pDispParams, 0, VT_DISPATCH, &varg0, puArgErr);
722 if (SUCCEEDED(hr) && V_DISPATCH(&varg0) != NULL)
723 MsiViewExecute(This->msiHandle, ((AutomationObject *)V_DISPATCH(&varg0))->msiHandle);
725 MsiViewExecute(This->msiHandle, 0);
727 else return DISP_E_MEMBERNOTFOUND;
730 case DISPID_VIEW_FETCH:
731 if (wFlags & DISPATCH_METHOD)
733 V_VT(pVarResult) = VT_DISPATCH;
734 if ((ret = MsiViewFetch(This->msiHandle, &msiHandle)) == ERROR_SUCCESS)
736 if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_Record, RecordImpl_Invoke, NULL, 0)))
738 IDispatch_AddRef(pDispatch);
739 V_DISPATCH(pVarResult) = pDispatch;
742 ERR("Failed to create Record object, hresult 0x%08x\n", hr);
744 else if (ret == ERROR_NO_MORE_ITEMS)
745 V_DISPATCH(pVarResult) = NULL;
748 ERR("MsiViewFetch returned %d\n", ret);
749 return DISP_E_EXCEPTION;
752 else return DISP_E_MEMBERNOTFOUND;
755 case DISPID_VIEW_MODIFY:
756 if (wFlags & DISPATCH_METHOD)
758 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
759 if (FAILED(hr)) return hr;
760 hr = DispGetParam(pDispParams, 1, VT_DISPATCH, &varg1, puArgErr);
761 if (FAILED(hr)) return hr;
762 if (!V_DISPATCH(&varg1)) return DISP_E_EXCEPTION;
763 if ((ret = MsiViewModify(This->msiHandle, V_I4(&varg0), ((AutomationObject *)V_DISPATCH(&varg1))->msiHandle)) != ERROR_SUCCESS)
765 VariantClear(&varg1);
766 ERR("MsiViewModify returned %d\n", ret);
767 return DISP_E_EXCEPTION;
770 else return DISP_E_MEMBERNOTFOUND;
773 case DISPID_VIEW_CLOSE:
774 if (wFlags & DISPATCH_METHOD)
776 MsiViewClose(This->msiHandle);
778 else return DISP_E_MEMBERNOTFOUND;
782 return DISP_E_MEMBERNOTFOUND;
785 VariantClear(&varg1);
786 VariantClear(&varg0);
791 static HRESULT WINAPI DatabaseImpl_Invoke(
792 AutomationObject* This,
797 DISPPARAMS* pDispParams,
799 EXCEPINFO* pExcepInfo,
803 IDispatch *pDispatch = NULL;
805 VARIANTARG varg0, varg1;
811 switch (dispIdMember)
813 case DISPID_DATABASE_OPENVIEW:
814 if (wFlags & DISPATCH_METHOD)
816 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
817 if (FAILED(hr)) return hr;
818 V_VT(pVarResult) = VT_DISPATCH;
819 if ((ret = MsiDatabaseOpenViewW(This->msiHandle, V_BSTR(&varg0), &msiHandle)) == ERROR_SUCCESS)
821 if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_View, ViewImpl_Invoke, NULL, 0)))
823 IDispatch_AddRef(pDispatch);
824 V_DISPATCH(pVarResult) = pDispatch;
827 ERR("Failed to create View object, hresult 0x%08x\n", hr);
831 VariantClear(&varg0);
832 ERR("MsiDatabaseOpenView returned %d\n", ret);
833 return DISP_E_EXCEPTION;
836 else return DISP_E_MEMBERNOTFOUND;
840 return DISP_E_MEMBERNOTFOUND;
843 VariantClear(&varg1);
844 VariantClear(&varg0);
849 static HRESULT WINAPI SessionImpl_Invoke(
850 AutomationObject* This,
855 DISPPARAMS* pDispParams,
857 EXCEPINFO* pExcepInfo,
860 SessionData *data = private_data(This);
863 IDispatch *pDispatch = NULL;
867 INSTALLSTATE iInstalled, iAction;
868 VARIANTARG varg0, varg1;
874 switch (dispIdMember)
876 case DISPID_SESSION_INSTALLER:
877 if (wFlags & DISPATCH_PROPERTYGET) {
878 V_VT(pVarResult) = VT_DISPATCH;
879 IDispatch_AddRef(data->pInstaller);
880 V_DISPATCH(pVarResult) = data->pInstaller;
882 else return DISP_E_MEMBERNOTFOUND;
885 case DISPID_SESSION_PROPERTY:
886 if (wFlags & DISPATCH_PROPERTYGET) {
887 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
888 if (FAILED(hr)) return hr;
889 V_VT(pVarResult) = VT_BSTR;
890 V_BSTR(pVarResult) = NULL;
891 if ((ret = MsiGetPropertyW(This->msiHandle, V_BSTR(&varg0), NULL, &dwLen)) == ERROR_SUCCESS)
893 if (!(szString = msi_alloc((++dwLen)*sizeof(WCHAR))))
894 ERR("Out of memory\n");
895 else if ((ret = MsiGetPropertyW(This->msiHandle, V_BSTR(&varg0), szString, &dwLen)) == ERROR_SUCCESS)
896 V_BSTR(pVarResult) = SysAllocString(szString);
899 if (ret != ERROR_SUCCESS)
900 ERR("MsiGetProperty returned %d\n", ret);
901 } else if (wFlags & DISPATCH_PROPERTYPUT) {
902 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
903 if (FAILED(hr)) return hr;
904 hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_BSTR, &varg1, puArgErr);
906 VariantClear(&varg0);
909 if ((ret = MsiSetPropertyW(This->msiHandle, V_BSTR(&varg0), V_BSTR(&varg1))) != ERROR_SUCCESS)
911 VariantClear(&varg0);
912 VariantClear(&varg1);
913 ERR("MsiSetProperty returned %d\n", ret);
914 return DISP_E_EXCEPTION;
917 else return DISP_E_MEMBERNOTFOUND;
920 case DISPID_SESSION_LANGUAGE:
921 if (wFlags & DISPATCH_PROPERTYGET) {
922 langId = MsiGetLanguage(This->msiHandle);
923 V_VT(pVarResult) = VT_I4;
924 V_I4(pVarResult) = langId;
926 else return DISP_E_MEMBERNOTFOUND;
929 case DISPID_SESSION_MODE:
930 if (wFlags & DISPATCH_PROPERTYGET) {
931 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
932 if (FAILED(hr)) return hr;
933 V_VT(pVarResult) = VT_BOOL;
934 V_BOOL(pVarResult) = MsiGetMode(This->msiHandle, V_I4(&varg0));
935 } else if (wFlags & DISPATCH_PROPERTYPUT) {
936 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
937 if (FAILED(hr)) return hr;
938 hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_BOOL, &varg1, puArgErr);
939 if (FAILED(hr)) return hr;
940 if ((ret = MsiSetMode(This->msiHandle, V_I4(&varg0), V_BOOL(&varg1))) != ERROR_SUCCESS)
942 ERR("MsiSetMode returned %d\n", ret);
943 return DISP_E_EXCEPTION;
946 else return DISP_E_MEMBERNOTFOUND;
949 case DISPID_SESSION_DATABASE:
950 if (wFlags & DISPATCH_PROPERTYGET) {
951 V_VT(pVarResult) = VT_DISPATCH;
952 if ((msiHandle = MsiGetActiveDatabase(This->msiHandle)))
954 if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_Database, DatabaseImpl_Invoke, NULL, 0)))
956 IDispatch_AddRef(pDispatch);
957 V_DISPATCH(pVarResult) = pDispatch;
960 ERR("Failed to create Database object, hresult 0x%08x\n", hr);
964 ERR("MsiGetActiveDatabase failed\n");
965 return DISP_E_EXCEPTION;
968 else return DISP_E_MEMBERNOTFOUND;
971 case DISPID_SESSION_DOACTION:
972 if (wFlags & DISPATCH_METHOD) {
973 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
974 if (FAILED(hr)) return hr;
975 ret = MsiDoActionW(This->msiHandle, V_BSTR(&varg0));
976 V_VT(pVarResult) = VT_I4;
979 case ERROR_FUNCTION_NOT_CALLED:
980 V_I4(pVarResult) = msiDoActionStatusNoAction;
983 V_I4(pVarResult) = msiDoActionStatusSuccess;
985 case ERROR_INSTALL_USEREXIT:
986 V_I4(pVarResult) = msiDoActionStatusUserExit;
988 case ERROR_INSTALL_FAILURE:
989 V_I4(pVarResult) = msiDoActionStatusFailure;
991 case ERROR_INSTALL_SUSPEND:
992 V_I4(pVarResult) = msiDoActionStatusSuspend;
994 case ERROR_MORE_DATA:
995 V_I4(pVarResult) = msiDoActionStatusFinished;
997 case ERROR_INVALID_HANDLE_STATE:
998 V_I4(pVarResult) = msiDoActionStatusWrongState;
1000 case ERROR_INVALID_DATA:
1001 V_I4(pVarResult) = msiDoActionStatusBadActionData;
1004 VariantClear(&varg0);
1005 FIXME("MsiDoAction returned unhandled value %d\n", ret);
1006 return DISP_E_EXCEPTION;
1009 else return DISP_E_MEMBERNOTFOUND;
1012 case DISPID_SESSION_EVALUATECONDITION:
1013 if (wFlags & DISPATCH_METHOD) {
1014 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1015 if (FAILED(hr)) return hr;
1016 V_VT(pVarResult) = VT_I4;
1017 V_I4(pVarResult) = MsiEvaluateConditionW(This->msiHandle, V_BSTR(&varg0));
1019 else return DISP_E_MEMBERNOTFOUND;
1022 case DISPID_SESSION_SETINSTALLLEVEL:
1023 if (wFlags & DISPATCH_METHOD) {
1024 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1025 if (FAILED(hr)) return hr;
1026 if ((ret = MsiSetInstallLevel(This->msiHandle, V_I4(&varg0))) != ERROR_SUCCESS)
1028 ERR("MsiSetInstallLevel returned %d\n", ret);
1029 return DISP_E_EXCEPTION;
1032 else return DISP_E_MEMBERNOTFOUND;
1035 case DISPID_SESSION_FEATURECURRENTSTATE:
1036 if (wFlags & DISPATCH_PROPERTYGET) {
1037 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1038 if (FAILED(hr)) return hr;
1039 V_VT(pVarResult) = VT_I4;
1040 if ((ret = MsiGetFeatureStateW(This->msiHandle, V_BSTR(&varg0), &iInstalled, &iAction)) == ERROR_SUCCESS)
1041 V_I4(pVarResult) = iInstalled;
1044 ERR("MsiGetFeatureState returned %d\n", ret);
1045 V_I4(pVarResult) = msiInstallStateUnknown;
1048 else return DISP_E_MEMBERNOTFOUND;
1051 case DISPID_SESSION_FEATUREREQUESTSTATE:
1052 if (wFlags & DISPATCH_PROPERTYGET) {
1053 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1054 if (FAILED(hr)) return hr;
1055 V_VT(pVarResult) = VT_I4;
1056 if ((ret = MsiGetFeatureStateW(This->msiHandle, V_BSTR(&varg0), &iInstalled, &iAction)) == ERROR_SUCCESS)
1057 V_I4(pVarResult) = iAction;
1060 ERR("MsiGetFeatureState returned %d\n", ret);
1061 V_I4(pVarResult) = msiInstallStateUnknown;
1063 } else if (wFlags & DISPATCH_PROPERTYPUT) {
1064 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1065 if (FAILED(hr)) return hr;
1066 hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_I4, &varg1, puArgErr);
1068 VariantClear(&varg0);
1071 if ((ret = MsiSetFeatureStateW(This->msiHandle, V_BSTR(&varg0), V_I4(&varg1))) != ERROR_SUCCESS)
1073 VariantClear(&varg0);
1074 ERR("MsiSetFeatureState returned %d\n", ret);
1075 return DISP_E_EXCEPTION;
1078 else return DISP_E_MEMBERNOTFOUND;
1082 return DISP_E_MEMBERNOTFOUND;
1085 VariantClear(&varg1);
1086 VariantClear(&varg0);
1091 /* Fill the variant pointed to by pVarResult with the value & size returned by RegQueryValueEx as dictated by the
1092 * registry value type. Used by Installer::RegistryValue. */
1093 static void variant_from_registry_value(VARIANT *pVarResult, DWORD dwType, LPBYTE lpData, DWORD dwSize)
1095 static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 };
1096 static const WCHAR szREG_[] = { '(','R','E','G','_',']',0 };
1097 WCHAR *szString = (WCHAR *)lpData;
1098 LPWSTR szNewString = NULL;
1099 DWORD dwNewSize = 0;
1104 /* Registry strings may not be null terminated so we must use SysAllocStringByteLen/Len */
1105 case REG_MULTI_SZ: /* Multi SZ change internal null characters to newlines */
1106 idx = (dwSize/sizeof(WCHAR))-1;
1107 while (idx >= 0 && !szString[idx]) idx--;
1108 for (; idx >= 0; idx--)
1109 if (!szString[idx]) szString[idx] = '\n';
1111 V_VT(pVarResult) = VT_BSTR;
1112 V_BSTR(pVarResult) = SysAllocStringByteLen((LPCSTR)szString, dwSize);
1116 if (!(dwNewSize = ExpandEnvironmentStringsW(szString, szNewString, dwNewSize)))
1117 ERR("ExpandEnvironmentStrings returned error %d\n", GetLastError());
1118 else if (!(szNewString = msi_alloc(dwNewSize)))
1119 ERR("Out of memory\n");
1120 else if (!(dwNewSize = ExpandEnvironmentStringsW(szString, szNewString, dwNewSize)))
1121 ERR("ExpandEnvironmentStrings returned error %d\n", GetLastError());
1124 V_VT(pVarResult) = VT_BSTR;
1125 V_BSTR(pVarResult) = SysAllocStringLen(szNewString, dwNewSize);
1127 msi_free(szNewString);
1131 V_VT(pVarResult) = VT_I4;
1132 V_I4(pVarResult) = *((DWORD *)lpData);
1136 V_VT(pVarResult) = VT_BSTR;
1137 V_BSTR(pVarResult) = SysAllocString(szREG_); /* Weird string, don't know why native returns it */
1141 V_VT(pVarResult) = VT_BSTR;
1142 V_BSTR(pVarResult) = SysAllocString(szREG_BINARY);
1146 V_VT(pVarResult) = VT_EMPTY;
1150 FIXME("Unhandled registry value type %d\n", dwType);
1154 static HRESULT WINAPI InstallerImpl_Invoke(
1155 AutomationObject* This,
1156 DISPID dispIdMember,
1160 DISPPARAMS* pDispParams,
1161 VARIANT* pVarResult,
1162 EXCEPINFO* pExcepInfo,
1165 MSIHANDLE msiHandle;
1166 IDispatch *pDispatch = NULL;
1168 VARIANTARG varg0, varg1, varg2;
1171 VariantInit(&varg0);
1172 VariantInit(&varg1);
1173 VariantInit(&varg2);
1175 switch (dispIdMember)
1177 case DISPID_INSTALLER_CREATERECORD:
1178 if (wFlags & DISPATCH_METHOD)
1180 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1181 if (FAILED(hr)) return hr;
1182 V_VT(pVarResult) = VT_DISPATCH;
1183 if ((msiHandle = MsiCreateRecord(V_I4(&varg0))))
1185 if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_Record, RecordImpl_Invoke, NULL, 0)))
1187 IDispatch_AddRef(pDispatch);
1188 V_DISPATCH(pVarResult) = pDispatch;
1191 ERR("Failed to create Record object, hresult 0x%08x\n", hr);
1195 ERR("MsiCreateRecord failed\n");
1196 return DISP_E_EXCEPTION;
1199 else return DISP_E_MEMBERNOTFOUND;
1202 case DISPID_INSTALLER_OPENPACKAGE:
1203 if (wFlags & DISPATCH_METHOD)
1205 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1206 if (FAILED(hr)) return hr;
1207 hr = DispGetParam(pDispParams, 1, VT_I4, &varg1, puArgErr);
1210 VariantClear(&varg0);
1213 V_VT(pVarResult) = VT_DISPATCH;
1214 if ((ret = MsiOpenPackageExW(V_BSTR(&varg0), V_I4(&varg1), &msiHandle)) == ERROR_SUCCESS)
1216 if (SUCCEEDED(hr = create_session(msiHandle, (IDispatch *)This, &pDispatch)))
1218 IDispatch_AddRef(pDispatch);
1219 V_DISPATCH(pVarResult) = pDispatch;
1222 ERR("Failed to create Session object, hresult 0x%08x\n", hr);
1226 VariantClear(&varg0);
1227 ERR("MsiOpenPackageEx returned %d\n", ret);
1228 return DISP_E_EXCEPTION;
1231 else return DISP_E_MEMBERNOTFOUND;
1234 case DISPID_INSTALLER_INSTALLPRODUCT:
1235 if (wFlags & DISPATCH_METHOD)
1237 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1238 if (FAILED(hr)) return hr;
1239 hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr);
1242 VariantClear(&varg0);
1245 if ((ret = MsiInstallProductW(V_BSTR(&varg0), V_BSTR(&varg1))) != ERROR_SUCCESS)
1247 VariantClear(&varg1);
1248 VariantClear(&varg0);
1249 ERR("MsiInstallProduct returned %d\n", ret);
1250 return DISP_E_EXCEPTION;
1253 else return DISP_E_MEMBERNOTFOUND;
1256 case DISPID_INSTALLER_REGISTRYVALUE:
1257 if (wFlags & DISPATCH_METHOD) {
1259 LPWSTR szString = NULL;
1260 DWORD dwSize = 0, dwType;
1261 UINT posValue = 2; /* Save valuePos so we can save puArgErr if we are unable to do our type conversions */
1263 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1264 if (FAILED(hr)) return hr;
1265 hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr);
1266 if (FAILED(hr)) return hr;
1267 hr = DispGetParam_CopyOnly(pDispParams, &posValue, &varg2);
1270 VariantClear(&varg1);
1273 ret = RegOpenKeyW((HKEY)V_I4(&varg0), V_BSTR(&varg1), &hkey);
1275 /* Third parameter can be VT_EMPTY, VT_I4, or VT_BSTR */
1276 switch (V_VT(&varg2))
1278 case VT_EMPTY: /* Return VT_BOOL as to whether or not registry key exists */
1279 V_VT(pVarResult) = VT_BOOL;
1280 V_BOOL(pVarResult) = (ret == ERROR_SUCCESS);
1283 case VT_BSTR: /* Return value of specified key if it exists */
1284 if (ret == ERROR_SUCCESS &&
1285 (ret = RegQueryValueExW(hkey, V_BSTR(&varg2), NULL, NULL, NULL, &dwSize)) == ERROR_SUCCESS)
1287 if (!(szString = msi_alloc(dwSize)))
1288 ERR("Out of memory\n");
1289 else if ((ret = RegQueryValueExW(hkey, V_BSTR(&varg2), NULL, &dwType, (LPBYTE)szString, &dwSize)) == ERROR_SUCCESS)
1290 variant_from_registry_value(pVarResult, dwType, (LPBYTE)szString, dwSize);
1293 if (ret != ERROR_SUCCESS)
1296 VariantClear(&varg2);
1297 VariantClear(&varg1);
1298 return DISP_E_BADINDEX;
1302 default: /* Try to make it into VT_I4, can use VariantChangeType for this */
1303 hr = VariantChangeType(&varg2, &varg2, 0, VT_I4);
1304 if (SUCCEEDED(hr) && ret != ERROR_SUCCESS) hr = DISP_E_BADINDEX; /* Conversion fine, but couldn't find key */
1307 if (hr == DISP_E_TYPEMISMATCH) *puArgErr = posValue;
1308 VariantClear(&varg2); /* Unknown type, so let's clear it */
1309 VariantClear(&varg1);
1313 /* Retrieve class name or maximum value name or subkey name size */
1315 ret = RegQueryInfoKeyW(hkey, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1316 else if (V_I4(&varg2) > 0)
1317 ret = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL);
1318 else /* V_I4(&varg2) < 0 */
1319 ret = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
1321 if (ret == ERROR_SUCCESS)
1323 if (!(szString = msi_alloc(++dwSize * sizeof(WCHAR))))
1324 ERR("Out of memory\n");
1325 else if (!V_I4(&varg2))
1326 ret = RegQueryInfoKeyW(hkey, szString, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1327 else if (V_I4(&varg2) > 0)
1328 ret = RegEnumValueW(hkey, V_I4(&varg2)-1, szString, &dwSize, 0, 0, NULL, NULL);
1329 else /* V_I4(&varg2) < 0 */
1330 ret = RegEnumKeyW(hkey, -1 - V_I4(&varg2), szString, dwSize);
1332 if (szString && ret == ERROR_SUCCESS)
1334 V_VT(pVarResult) = VT_BSTR;
1335 V_BSTR(pVarResult) = SysAllocString(szString);
1343 else return DISP_E_MEMBERNOTFOUND;
1346 case DISPID_INSTALLER_PRODUCTSTATE:
1347 if (wFlags & DISPATCH_PROPERTYGET) {
1348 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1349 if (FAILED(hr)) return hr;
1350 V_VT(pVarResult) = VT_I4;
1351 V_I4(pVarResult) = MsiQueryProductStateW(V_BSTR(&varg0));
1353 else return DISP_E_MEMBERNOTFOUND;
1356 case DISPID_INSTALLER_PRODUCTS:
1357 if (wFlags & DISPATCH_PROPERTYGET)
1359 StringListData *sldata = NULL;
1361 WCHAR szProductBuf[GUID_SIZE];
1363 /* Find number of products */
1364 while ((ret = MsiEnumProductsW(idx, szProductBuf)) == ERROR_SUCCESS) idx++;
1365 if (ret != ERROR_NO_MORE_ITEMS)
1367 ERR("MsiEnumProducts returned %d\n", ret);
1368 return DISP_E_EXCEPTION;
1371 V_VT(pVarResult) = VT_DISPATCH;
1372 if (SUCCEEDED(hr = create_automation_object(0, NULL, (LPVOID*)&pDispatch, &DIID_StringList, StringListImpl_Invoke, StringListImpl_Free, sizeof(StringListData))))
1374 IDispatch_AddRef(pDispatch);
1375 V_DISPATCH(pVarResult) = pDispatch;
1377 /* Save product strings */
1378 sldata = (StringListData *)private_data((AutomationObject *)pDispatch);
1379 if (!(sldata->pszStrings = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LPWSTR)*sldata->iCount)))
1380 ERR("Out of memory\n");
1383 sldata->iCount = idx;
1384 for (idx = 0; idx < sldata->iCount; idx++)
1386 ret = MsiEnumProductsW(idx, szProductBuf);
1387 sldata->pszStrings[idx] = SysAllocString(szProductBuf);
1392 ERR("Failed to create StringList object, hresult 0x%08x\n", hr);
1394 else return DISP_E_MEMBERNOTFOUND;
1397 case DISPID_INSTALLER_RELATEDPRODUCTS:
1398 if (wFlags & DISPATCH_PROPERTYGET)
1400 StringListData *sldata = NULL;
1402 WCHAR szProductBuf[GUID_SIZE];
1404 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1405 if (FAILED(hr)) return hr;
1407 /* Find number of related products */
1408 while ((ret = MsiEnumRelatedProductsW(V_BSTR(&varg0), 0, idx, szProductBuf)) == ERROR_SUCCESS) idx++;
1409 if (ret != ERROR_NO_MORE_ITEMS)
1411 VariantClear(&varg0);
1412 ERR("MsiEnumRelatedProducts returned %d\n", ret);
1413 return DISP_E_EXCEPTION;
1416 V_VT(pVarResult) = VT_DISPATCH;
1417 if (SUCCEEDED(hr = create_automation_object(0, NULL, (LPVOID*)&pDispatch, &DIID_StringList, StringListImpl_Invoke, StringListImpl_Free, sizeof(StringListData))))
1419 IDispatch_AddRef(pDispatch);
1420 V_DISPATCH(pVarResult) = pDispatch;
1422 /* Save product strings */
1423 sldata = (StringListData *)private_data((AutomationObject *)pDispatch);
1424 if (!(sldata->pszStrings = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LPWSTR)*sldata->iCount)))
1425 ERR("Out of memory\n");
1428 sldata->iCount = idx;
1429 for (idx = 0; idx < sldata->iCount; idx++)
1431 ret = MsiEnumRelatedProductsW(V_BSTR(&varg0), 0, idx, szProductBuf);
1432 sldata->pszStrings[idx] = SysAllocString(szProductBuf);
1437 ERR("Failed to create StringList object, hresult 0x%08x\n", hr);
1439 else return DISP_E_MEMBERNOTFOUND;
1443 return DISP_E_MEMBERNOTFOUND;
1446 VariantClear(&varg2);
1447 VariantClear(&varg1);
1448 VariantClear(&varg0);
1453 /* Wrapper around create_automation_object to create an installer object. */
1454 HRESULT create_msiserver(IUnknown *pOuter, LPVOID *ppObj)
1456 return create_automation_object(0, pOuter, ppObj, &DIID_Installer, InstallerImpl_Invoke, NULL, 0);
1459 /* Wrapper around create_automation_object to create a session object. */
1460 HRESULT create_session(MSIHANDLE msiHandle, IDispatch *pInstaller, IDispatch **pDispatch)
1462 HRESULT hr = create_automation_object(msiHandle, NULL, (LPVOID)pDispatch, &DIID_Session, SessionImpl_Invoke, NULL, sizeof(SessionData));
1463 if (SUCCEEDED(hr) && pDispatch && *pDispatch)
1464 ((SessionData *)private_data((AutomationObject *)*pDispatch))->pInstaller = pInstaller;