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 ret
= MsiRecordGetStringW(This
->msiHandle
, V_I4(&varg0
), NULL
, &dwLen
);
584 if (ret
== ERROR_SUCCESS
)
586 szString
= msi_alloc((++dwLen
)*sizeof(WCHAR
));
589 if ((ret
= MsiRecordGetStringW(This
->msiHandle
, V_I4(&varg0
), szString
, &dwLen
)) == ERROR_SUCCESS
)
590 V_BSTR(pVarResult
) = SysAllocString(szString
);
594 if (ret
!= ERROR_SUCCESS
)
595 ERR("MsiRecordGetString returned %d\n", ret
);
596 } else if (wFlags
& DISPATCH_PROPERTYPUT
) {
597 hr
= DispGetParam(pDispParams
, 0, VT_I4
, &varg0
, puArgErr
);
598 if (FAILED(hr
)) return hr
;
599 hr
= DispGetParam(pDispParams
, DISPID_PROPERTYPUT
, VT_BSTR
, &varg1
, puArgErr
);
600 if (FAILED(hr
)) return hr
;
601 if ((ret
= MsiRecordSetStringW(This
->msiHandle
, V_I4(&varg0
), V_BSTR(&varg1
))) != ERROR_SUCCESS
)
603 VariantClear(&varg1
);
604 ERR("MsiRecordSetString returned %d\n", ret
);
605 return DISP_E_EXCEPTION
;
608 else return DISP_E_MEMBERNOTFOUND
;
612 return DISP_E_MEMBERNOTFOUND
;
615 VariantClear(&varg1
);
616 VariantClear(&varg0
);
621 static HRESULT WINAPI
StringListImpl_Invoke(
622 AutomationObject
* This
,
627 DISPPARAMS
* pDispParams
,
629 EXCEPINFO
* pExcepInfo
,
632 StringListData
*data
= (StringListData
*)private_data(This
);
638 switch (dispIdMember
)
640 case DISPID_STRINGLIST_ITEM
:
641 if (wFlags
& DISPATCH_PROPERTYGET
) {
642 hr
= DispGetParam(pDispParams
, 0, VT_I4
, &varg0
, puArgErr
);
643 if (FAILED(hr
)) return hr
;
644 if (V_I4(&varg0
) < 0 || V_I4(&varg0
) >= data
->iCount
)
645 return DISP_E_BADINDEX
;
646 V_VT(pVarResult
) = VT_BSTR
;
647 V_BSTR(pVarResult
) = SysAllocString(data
->pszStrings
[V_I4(&varg0
)]);
649 else return DISP_E_MEMBERNOTFOUND
;
652 case DISPID_STRINGLIST_COUNT
:
653 if (wFlags
& DISPATCH_PROPERTYGET
) {
654 V_VT(pVarResult
) = VT_I4
;
655 V_I4(pVarResult
) = data
->iCount
;
657 else return DISP_E_MEMBERNOTFOUND
;
661 return DISP_E_MEMBERNOTFOUND
;
664 VariantClear(&varg0
);
669 static void WINAPI
StringListImpl_Free(AutomationObject
*This
)
671 StringListData
*data
= private_data(This
);
674 for (idx
=0; idx
<data
->iCount
; idx
++)
675 SysFreeString(data
->pszStrings
[idx
]);
676 HeapFree(GetProcessHeap(), 0, data
->pszStrings
);
679 static HRESULT WINAPI
ViewImpl_Invoke(
680 AutomationObject
* This
,
685 DISPPARAMS
* pDispParams
,
687 EXCEPINFO
* pExcepInfo
,
691 IDispatch
*pDispatch
= NULL
;
693 VARIANTARG varg0
, varg1
;
699 switch (dispIdMember
)
701 case DISPID_VIEW_EXECUTE
:
702 if (wFlags
& DISPATCH_METHOD
)
704 hr
= DispGetParam(pDispParams
, 0, VT_DISPATCH
, &varg0
, puArgErr
);
705 if (SUCCEEDED(hr
) && V_DISPATCH(&varg0
) != NULL
)
706 MsiViewExecute(This
->msiHandle
, ((AutomationObject
*)V_DISPATCH(&varg0
))->msiHandle
);
708 MsiViewExecute(This
->msiHandle
, 0);
710 else return DISP_E_MEMBERNOTFOUND
;
713 case DISPID_VIEW_FETCH
:
714 if (wFlags
& DISPATCH_METHOD
)
716 V_VT(pVarResult
) = VT_DISPATCH
;
717 if ((ret
= MsiViewFetch(This
->msiHandle
, &msiHandle
)) == ERROR_SUCCESS
)
719 if (SUCCEEDED(hr
= create_automation_object(msiHandle
, NULL
, (LPVOID
*)&pDispatch
, &DIID_Record
, RecordImpl_Invoke
, NULL
, 0)))
721 IDispatch_AddRef(pDispatch
);
722 V_DISPATCH(pVarResult
) = pDispatch
;
725 ERR("Failed to create Record object, hresult 0x%08x\n", hr
);
727 else if (ret
== ERROR_NO_MORE_ITEMS
)
728 V_DISPATCH(pVarResult
) = NULL
;
731 ERR("MsiViewFetch returned %d\n", ret
);
732 return DISP_E_EXCEPTION
;
735 else return DISP_E_MEMBERNOTFOUND
;
738 case DISPID_VIEW_CLOSE
:
739 if (wFlags
& DISPATCH_METHOD
)
741 MsiViewClose(This
->msiHandle
);
743 else return DISP_E_MEMBERNOTFOUND
;
747 return DISP_E_MEMBERNOTFOUND
;
750 VariantClear(&varg1
);
751 VariantClear(&varg0
);
756 static HRESULT WINAPI
DatabaseImpl_Invoke(
757 AutomationObject
* This
,
762 DISPPARAMS
* pDispParams
,
764 EXCEPINFO
* pExcepInfo
,
768 IDispatch
*pDispatch
= NULL
;
770 VARIANTARG varg0
, varg1
;
776 switch (dispIdMember
)
778 case DISPID_DATABASE_OPENVIEW
:
779 if (wFlags
& DISPATCH_METHOD
)
781 hr
= DispGetParam(pDispParams
, 0, VT_BSTR
, &varg0
, puArgErr
);
782 if (FAILED(hr
)) return hr
;
783 V_VT(pVarResult
) = VT_DISPATCH
;
784 if ((ret
= MsiDatabaseOpenViewW(This
->msiHandle
, V_BSTR(&varg0
), &msiHandle
)) == ERROR_SUCCESS
)
786 if (SUCCEEDED(hr
= create_automation_object(msiHandle
, NULL
, (LPVOID
*)&pDispatch
, &DIID_View
, ViewImpl_Invoke
, NULL
, 0)))
788 IDispatch_AddRef(pDispatch
);
789 V_DISPATCH(pVarResult
) = pDispatch
;
792 ERR("Failed to create View object, hresult 0x%08x\n", hr
);
796 VariantClear(&varg0
);
797 ERR("MsiDatabaseOpenView returned %d\n", ret
);
798 return DISP_E_EXCEPTION
;
801 else return DISP_E_MEMBERNOTFOUND
;
805 return DISP_E_MEMBERNOTFOUND
;
808 VariantClear(&varg1
);
809 VariantClear(&varg0
);
814 static HRESULT WINAPI
SessionImpl_Invoke(
815 AutomationObject
* This
,
820 DISPPARAMS
* pDispParams
,
822 EXCEPINFO
* pExcepInfo
,
825 SessionData
*data
= private_data(This
);
828 IDispatch
*pDispatch
= NULL
;
832 INSTALLSTATE iInstalled
, iAction
;
833 VARIANTARG varg0
, varg1
;
839 switch (dispIdMember
)
841 case DISPID_SESSION_INSTALLER
:
842 if (wFlags
& DISPATCH_PROPERTYGET
) {
843 V_VT(pVarResult
) = VT_DISPATCH
;
844 IDispatch_AddRef(data
->pInstaller
);
845 V_DISPATCH(pVarResult
) = data
->pInstaller
;
847 else return DISP_E_MEMBERNOTFOUND
;
850 case DISPID_SESSION_PROPERTY
:
851 if (wFlags
& DISPATCH_PROPERTYGET
) {
852 hr
= DispGetParam(pDispParams
, 0, VT_BSTR
, &varg0
, puArgErr
);
853 if (FAILED(hr
)) return hr
;
854 V_VT(pVarResult
) = VT_BSTR
;
855 V_BSTR(pVarResult
) = NULL
;
856 ret
= MsiGetPropertyW(This
->msiHandle
, V_BSTR(&varg0
), NULL
, &dwLen
);
857 if (ret
== ERROR_SUCCESS
)
859 szString
= msi_alloc((++dwLen
)*sizeof(WCHAR
));
862 if ((ret
= MsiGetPropertyW(This
->msiHandle
, V_BSTR(&varg0
), szString
, &dwLen
)) == ERROR_SUCCESS
)
863 V_BSTR(pVarResult
) = SysAllocString(szString
);
867 if (ret
!= ERROR_SUCCESS
)
868 ERR("MsiGetProperty returned %d\n", ret
);
869 } else if (wFlags
& DISPATCH_PROPERTYPUT
) {
870 hr
= DispGetParam(pDispParams
, 0, VT_BSTR
, &varg0
, puArgErr
);
871 if (FAILED(hr
)) return hr
;
872 hr
= DispGetParam(pDispParams
, DISPID_PROPERTYPUT
, VT_BSTR
, &varg1
, puArgErr
);
874 VariantClear(&varg0
);
877 if ((ret
= MsiSetPropertyW(This
->msiHandle
, V_BSTR(&varg0
), V_BSTR(&varg1
))) != ERROR_SUCCESS
)
879 VariantClear(&varg0
);
880 VariantClear(&varg1
);
881 ERR("MsiSetProperty returned %d\n", ret
);
882 return DISP_E_EXCEPTION
;
885 else return DISP_E_MEMBERNOTFOUND
;
888 case DISPID_SESSION_LANGUAGE
:
889 if (wFlags
& DISPATCH_PROPERTYGET
) {
890 langId
= MsiGetLanguage(This
->msiHandle
);
891 V_VT(pVarResult
) = VT_I4
;
892 V_I4(pVarResult
) = langId
;
894 else return DISP_E_MEMBERNOTFOUND
;
897 case DISPID_SESSION_MODE
:
898 if (wFlags
& DISPATCH_PROPERTYGET
) {
899 hr
= DispGetParam(pDispParams
, 0, VT_I4
, &varg0
, puArgErr
);
900 if (FAILED(hr
)) return hr
;
901 V_VT(pVarResult
) = VT_BOOL
;
902 V_BOOL(pVarResult
) = MsiGetMode(This
->msiHandle
, V_I4(&varg0
));
903 } else if (wFlags
& DISPATCH_PROPERTYPUT
) {
904 hr
= DispGetParam(pDispParams
, 0, VT_I4
, &varg0
, puArgErr
);
905 if (FAILED(hr
)) return hr
;
906 hr
= DispGetParam(pDispParams
, DISPID_PROPERTYPUT
, VT_BOOL
, &varg1
, puArgErr
);
907 if (FAILED(hr
)) return hr
;
908 if ((ret
= MsiSetMode(This
->msiHandle
, V_I4(&varg0
), V_BOOL(&varg1
))) != ERROR_SUCCESS
)
910 ERR("MsiSetMode returned %d\n", ret
);
911 return DISP_E_EXCEPTION
;
914 else return DISP_E_MEMBERNOTFOUND
;
917 case DISPID_SESSION_DATABASE
:
918 if (wFlags
& DISPATCH_PROPERTYGET
) {
919 V_VT(pVarResult
) = VT_DISPATCH
;
920 if ((msiHandle
= MsiGetActiveDatabase(This
->msiHandle
)))
922 if (SUCCEEDED(hr
= create_automation_object(msiHandle
, NULL
, (LPVOID
*)&pDispatch
, &DIID_Database
, DatabaseImpl_Invoke
, NULL
, 0)))
924 IDispatch_AddRef(pDispatch
);
925 V_DISPATCH(pVarResult
) = pDispatch
;
928 ERR("Failed to create Database object, hresult 0x%08x\n", hr
);
932 ERR("MsiGetActiveDatabase failed\n");
933 return DISP_E_EXCEPTION
;
936 else return DISP_E_MEMBERNOTFOUND
;
939 case DISPID_SESSION_DOACTION
:
940 if (wFlags
& DISPATCH_METHOD
) {
941 hr
= DispGetParam(pDispParams
, 0, VT_BSTR
, &varg0
, puArgErr
);
942 if (FAILED(hr
)) return hr
;
943 ret
= MsiDoActionW(This
->msiHandle
, V_BSTR(&varg0
));
944 V_VT(pVarResult
) = VT_I4
;
947 case ERROR_FUNCTION_NOT_CALLED
:
948 V_I4(pVarResult
) = msiDoActionStatusNoAction
;
951 V_I4(pVarResult
) = msiDoActionStatusSuccess
;
953 case ERROR_INSTALL_USEREXIT
:
954 V_I4(pVarResult
) = msiDoActionStatusUserExit
;
956 case ERROR_INSTALL_FAILURE
:
957 V_I4(pVarResult
) = msiDoActionStatusFailure
;
959 case ERROR_INSTALL_SUSPEND
:
960 V_I4(pVarResult
) = msiDoActionStatusSuspend
;
962 case ERROR_MORE_DATA
:
963 V_I4(pVarResult
) = msiDoActionStatusFinished
;
965 case ERROR_INVALID_HANDLE_STATE
:
966 V_I4(pVarResult
) = msiDoActionStatusWrongState
;
968 case ERROR_INVALID_DATA
:
969 V_I4(pVarResult
) = msiDoActionStatusBadActionData
;
972 VariantClear(&varg0
);
973 FIXME("MsiDoAction returned unhandled value %d\n", ret
);
974 return DISP_E_EXCEPTION
;
977 else return DISP_E_MEMBERNOTFOUND
;
980 case DISPID_SESSION_EVALUATECONDITION
:
981 if (wFlags
& DISPATCH_METHOD
) {
982 hr
= DispGetParam(pDispParams
, 0, VT_BSTR
, &varg0
, puArgErr
);
983 if (FAILED(hr
)) return hr
;
984 V_VT(pVarResult
) = VT_I4
;
985 V_I4(pVarResult
) = MsiEvaluateConditionW(This
->msiHandle
, V_BSTR(&varg0
));
987 else return DISP_E_MEMBERNOTFOUND
;
990 case DISPID_SESSION_SETINSTALLLEVEL
:
991 if (wFlags
& DISPATCH_METHOD
) {
992 hr
= DispGetParam(pDispParams
, 0, VT_I4
, &varg0
, puArgErr
);
993 if (FAILED(hr
)) return hr
;
994 if ((ret
= MsiSetInstallLevel(This
->msiHandle
, V_I4(&varg0
))) != ERROR_SUCCESS
)
996 ERR("MsiSetInstallLevel returned %d\n", ret
);
997 return DISP_E_EXCEPTION
;
1000 else return DISP_E_MEMBERNOTFOUND
;
1003 case DISPID_SESSION_FEATURECURRENTSTATE
:
1004 if (wFlags
& DISPATCH_PROPERTYGET
) {
1005 hr
= DispGetParam(pDispParams
, 0, VT_BSTR
, &varg0
, puArgErr
);
1006 if (FAILED(hr
)) return hr
;
1007 V_VT(pVarResult
) = VT_I4
;
1008 if ((ret
= MsiGetFeatureStateW(This
->msiHandle
, V_BSTR(&varg0
), &iInstalled
, &iAction
)) == ERROR_SUCCESS
)
1009 V_I4(pVarResult
) = iInstalled
;
1012 ERR("MsiGetFeatureState returned %d\n", ret
);
1013 V_I4(pVarResult
) = msiInstallStateUnknown
;
1016 else return DISP_E_MEMBERNOTFOUND
;
1019 case DISPID_SESSION_FEATUREREQUESTSTATE
:
1020 if (wFlags
& DISPATCH_PROPERTYGET
) {
1021 hr
= DispGetParam(pDispParams
, 0, VT_BSTR
, &varg0
, puArgErr
);
1022 if (FAILED(hr
)) return hr
;
1023 V_VT(pVarResult
) = VT_I4
;
1024 if ((ret
= MsiGetFeatureStateW(This
->msiHandle
, V_BSTR(&varg0
), &iInstalled
, &iAction
)) == ERROR_SUCCESS
)
1025 V_I4(pVarResult
) = iAction
;
1028 ERR("MsiGetFeatureState returned %d\n", ret
);
1029 V_I4(pVarResult
) = msiInstallStateUnknown
;
1031 } else if (wFlags
& DISPATCH_PROPERTYPUT
) {
1032 hr
= DispGetParam(pDispParams
, 0, VT_BSTR
, &varg0
, puArgErr
);
1033 if (FAILED(hr
)) return hr
;
1034 hr
= DispGetParam(pDispParams
, DISPID_PROPERTYPUT
, VT_I4
, &varg1
, puArgErr
);
1036 VariantClear(&varg0
);
1039 if ((ret
= MsiSetFeatureStateW(This
->msiHandle
, V_BSTR(&varg0
), V_I4(&varg1
))) != ERROR_SUCCESS
)
1041 VariantClear(&varg0
);
1042 ERR("MsiSetFeatureState returned %d\n", ret
);
1043 return DISP_E_EXCEPTION
;
1046 else return DISP_E_MEMBERNOTFOUND
;
1050 return DISP_E_MEMBERNOTFOUND
;
1053 VariantClear(&varg1
);
1054 VariantClear(&varg0
);
1059 /* Fill the variant pointed to by pVarResult with the value & size returned by RegQueryValueEx as dictated by the
1060 * registry value type. Used by Installer::RegistryValue. */
1061 static void variant_from_registry_value(VARIANT
*pVarResult
, DWORD dwType
, LPBYTE lpData
, DWORD dwSize
)
1063 static const WCHAR szREG_BINARY
[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 };
1064 static const WCHAR szREG_
[] = { '(','R','E','G','_',']',0 };
1065 WCHAR
*szString
= (WCHAR
*)lpData
;
1066 LPWSTR szNewString
= NULL
;
1067 DWORD dwNewSize
= 0;
1072 /* Registry strings may not be null terminated so we must use SysAllocStringByteLen/Len */
1073 case REG_MULTI_SZ
: /* Multi SZ change internal null characters to newlines */
1074 idx
= (dwSize
/sizeof(WCHAR
))-1;
1075 while (idx
>= 0 && !szString
[idx
]) idx
--;
1076 for (; idx
>= 0; idx
--)
1077 if (!szString
[idx
]) szString
[idx
] = '\n';
1079 V_VT(pVarResult
) = VT_BSTR
;
1080 V_BSTR(pVarResult
) = SysAllocStringByteLen((LPCSTR
)szString
, dwSize
);
1084 if (!(dwNewSize
= ExpandEnvironmentStringsW(szString
, szNewString
, dwNewSize
)))
1085 ERR("ExpandEnvironmentStrings returned error %d\n", GetLastError());
1086 else if (!(szNewString
= msi_alloc(dwNewSize
)))
1087 ERR("Out of memory\n");
1088 else if (!(dwNewSize
= ExpandEnvironmentStringsW(szString
, szNewString
, dwNewSize
)))
1089 ERR("ExpandEnvironmentStrings returned error %d\n", GetLastError());
1092 V_VT(pVarResult
) = VT_BSTR
;
1093 V_BSTR(pVarResult
) = SysAllocStringLen(szNewString
, dwNewSize
);
1095 msi_free(szNewString
);
1099 V_VT(pVarResult
) = VT_I4
;
1100 V_I4(pVarResult
) = *((DWORD
*)lpData
);
1104 V_VT(pVarResult
) = VT_BSTR
;
1105 V_BSTR(pVarResult
) = SysAllocString(szREG_
); /* Weird string, don't know why native returns it */
1109 V_VT(pVarResult
) = VT_BSTR
;
1110 V_BSTR(pVarResult
) = SysAllocString(szREG_BINARY
);
1114 V_VT(pVarResult
) = VT_EMPTY
;
1118 FIXME("Unhandled registry value type %d\n", dwType
);
1122 static HRESULT WINAPI
InstallerImpl_Invoke(
1123 AutomationObject
* This
,
1124 DISPID dispIdMember
,
1128 DISPPARAMS
* pDispParams
,
1129 VARIANT
* pVarResult
,
1130 EXCEPINFO
* pExcepInfo
,
1133 MSIHANDLE msiHandle
;
1134 IDispatch
*pDispatch
= NULL
;
1136 VARIANTARG varg0
, varg1
, varg2
;
1139 VariantInit(&varg0
);
1140 VariantInit(&varg1
);
1141 VariantInit(&varg2
);
1143 switch (dispIdMember
)
1145 case DISPID_INSTALLER_CREATERECORD
:
1146 if (wFlags
& DISPATCH_METHOD
)
1148 hr
= DispGetParam(pDispParams
, 0, VT_I4
, &varg0
, puArgErr
);
1149 if (FAILED(hr
)) return hr
;
1150 V_VT(pVarResult
) = VT_DISPATCH
;
1151 if ((msiHandle
= MsiCreateRecord(V_I4(&varg0
))))
1153 if (SUCCEEDED(hr
= create_automation_object(msiHandle
, NULL
, (LPVOID
*)&pDispatch
, &DIID_Record
, RecordImpl_Invoke
, NULL
, 0)))
1155 IDispatch_AddRef(pDispatch
);
1156 V_DISPATCH(pVarResult
) = pDispatch
;
1159 ERR("Failed to create Record object, hresult 0x%08x\n", hr
);
1163 ERR("MsiCreateRecord failed\n");
1164 return DISP_E_EXCEPTION
;
1167 else return DISP_E_MEMBERNOTFOUND
;
1170 case DISPID_INSTALLER_OPENPACKAGE
:
1171 if (wFlags
& DISPATCH_METHOD
)
1173 hr
= DispGetParam(pDispParams
, 0, VT_BSTR
, &varg0
, puArgErr
);
1174 if (FAILED(hr
)) return hr
;
1175 hr
= DispGetParam(pDispParams
, 1, VT_I4
, &varg1
, puArgErr
);
1178 VariantClear(&varg0
);
1181 V_VT(pVarResult
) = VT_DISPATCH
;
1182 if ((ret
= MsiOpenPackageExW(V_BSTR(&varg0
), V_I4(&varg1
), &msiHandle
)) == ERROR_SUCCESS
)
1184 if (SUCCEEDED(hr
= create_session(msiHandle
, (IDispatch
*)This
, &pDispatch
)))
1186 IDispatch_AddRef(pDispatch
);
1187 V_DISPATCH(pVarResult
) = pDispatch
;
1190 ERR("Failed to create Session object, hresult 0x%08x\n", hr
);
1194 VariantClear(&varg0
);
1195 ERR("MsiOpenPackageEx returned %d\n", ret
);
1196 return DISP_E_EXCEPTION
;
1199 else return DISP_E_MEMBERNOTFOUND
;
1202 case DISPID_INSTALLER_REGISTRYVALUE
:
1203 if (wFlags
& DISPATCH_METHOD
) {
1205 LPWSTR szString
= NULL
;
1206 DWORD dwSize
= 0, dwType
;
1207 UINT posValue
= 2; /* Save valuePos so we can save puArgErr if we are unable to do our type conversions */
1209 hr
= DispGetParam(pDispParams
, 0, VT_I4
, &varg0
, puArgErr
);
1210 if (FAILED(hr
)) return hr
;
1211 hr
= DispGetParam(pDispParams
, 1, VT_BSTR
, &varg1
, puArgErr
);
1212 if (FAILED(hr
)) return hr
;
1213 hr
= DispGetParam_CopyOnly(pDispParams
, &posValue
, &varg2
);
1216 VariantClear(&varg1
);
1219 ret
= RegOpenKeyW((HKEY
)V_I4(&varg0
), V_BSTR(&varg1
), &hkey
);
1221 /* Third parameter can be VT_EMPTY, VT_I4, or VT_BSTR */
1222 switch (V_VT(&varg2
))
1224 case VT_EMPTY
: /* Return VT_BOOL as to whether or not registry key exists */
1225 V_VT(pVarResult
) = VT_BOOL
;
1226 V_BOOL(pVarResult
) = (ret
== ERROR_SUCCESS
);
1229 case VT_BSTR
: /* Return value of specified key if it exists */
1230 if (ret
== ERROR_SUCCESS
&&
1231 (ret
= RegQueryValueExW(hkey
, V_BSTR(&varg2
), NULL
, NULL
, NULL
, &dwSize
)) == ERROR_SUCCESS
)
1233 if (!(szString
= msi_alloc(dwSize
)))
1234 ERR("Out of memory\n");
1235 else if ((ret
= RegQueryValueExW(hkey
, V_BSTR(&varg2
), NULL
, &dwType
, (LPBYTE
)szString
, &dwSize
)) == ERROR_SUCCESS
)
1236 variant_from_registry_value(pVarResult
, dwType
, (LPBYTE
)szString
, dwSize
);
1239 if (ret
!= ERROR_SUCCESS
)
1242 VariantClear(&varg2
);
1243 VariantClear(&varg1
);
1244 return DISP_E_BADINDEX
;
1248 default: /* Try to make it into VT_I4, can use VariantChangeType for this */
1249 hr
= VariantChangeType(&varg2
, &varg2
, 0, VT_I4
);
1250 if (SUCCEEDED(hr
) && ret
!= ERROR_SUCCESS
) hr
= DISP_E_BADINDEX
; /* Conversion fine, but couldn't find key */
1253 if (hr
== DISP_E_TYPEMISMATCH
) *puArgErr
= posValue
;
1254 VariantClear(&varg2
); /* Unknown type, so let's clear it */
1255 VariantClear(&varg1
);
1259 /* Retrieve class name or maximum value name or subkey name size */
1261 ret
= RegQueryInfoKeyW(hkey
, NULL
, &dwSize
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
1262 else if (V_I4(&varg2
) > 0)
1263 ret
= RegQueryInfoKeyW(hkey
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, &dwSize
, NULL
, NULL
, NULL
);
1264 else /* V_I4(&varg2) < 0 */
1265 ret
= RegQueryInfoKeyW(hkey
, NULL
, NULL
, NULL
, NULL
, &dwSize
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
1267 if (ret
== ERROR_SUCCESS
)
1269 if (!(szString
= msi_alloc(++dwSize
* sizeof(WCHAR
))))
1270 ERR("Out of memory\n");
1271 else if (!V_I4(&varg2
))
1272 ret
= RegQueryInfoKeyW(hkey
, szString
, &dwSize
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
, NULL
);
1273 else if (V_I4(&varg2
) > 0)
1274 ret
= RegEnumValueW(hkey
, V_I4(&varg2
)-1, szString
, &dwSize
, 0, 0, NULL
, NULL
);
1275 else /* V_I4(&varg2) < 0 */
1276 ret
= RegEnumKeyW(hkey
, -1 - V_I4(&varg2
), szString
, dwSize
);
1278 if (szString
&& ret
== ERROR_SUCCESS
)
1280 V_VT(pVarResult
) = VT_BSTR
;
1281 V_BSTR(pVarResult
) = SysAllocString(szString
);
1289 else return DISP_E_MEMBERNOTFOUND
;
1292 case DISPID_INSTALLER_PRODUCTSTATE
:
1293 if (wFlags
& DISPATCH_PROPERTYGET
) {
1294 hr
= DispGetParam(pDispParams
, 0, VT_BSTR
, &varg0
, puArgErr
);
1295 if (FAILED(hr
)) return hr
;
1296 V_VT(pVarResult
) = VT_I4
;
1297 V_I4(pVarResult
) = MsiQueryProductStateW(V_BSTR(&varg0
));
1299 else return DISP_E_MEMBERNOTFOUND
;
1302 case DISPID_INSTALLER_PRODUCTS
:
1303 if (wFlags
& DISPATCH_PROPERTYGET
)
1305 StringListData
*sldata
= NULL
;
1307 WCHAR szProductBuf
[GUID_SIZE
];
1309 /* Find number of products */
1311 ret
= MsiEnumProductsW(idx
, szProductBuf
);
1312 if (ret
== ERROR_SUCCESS
) idx
++;
1313 } while (ret
== ERROR_SUCCESS
&& ret
!= ERROR_NO_MORE_ITEMS
);
1315 if (ret
!= ERROR_SUCCESS
&& ret
!= ERROR_NO_MORE_ITEMS
)
1317 ERR("MsiEnumProducts returned %d\n", ret
);
1318 return DISP_E_EXCEPTION
;
1321 V_VT(pVarResult
) = VT_DISPATCH
;
1322 if (SUCCEEDED(hr
= create_automation_object(0, NULL
, (LPVOID
*)&pDispatch
, &DIID_StringList
, StringListImpl_Invoke
, StringListImpl_Free
, sizeof(StringListData
))))
1324 IDispatch_AddRef(pDispatch
);
1325 V_DISPATCH(pVarResult
) = pDispatch
;
1327 /* Save product strings */
1328 sldata
= (StringListData
*)private_data((AutomationObject
*)pDispatch
);
1329 sldata
->iCount
= idx
;
1330 sldata
->pszStrings
= HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY
, sizeof(LPWSTR
)*sldata
->iCount
);
1331 for (idx
= 0; idx
< sldata
->iCount
; idx
++)
1333 ret
= MsiEnumProductsW(idx
, szProductBuf
);
1334 sldata
->pszStrings
[idx
] = SysAllocString(szProductBuf
);
1338 ERR("Failed to create StringList object, hresult 0x%08x\n", hr
);
1340 else return DISP_E_MEMBERNOTFOUND
;
1344 return DISP_E_MEMBERNOTFOUND
;
1347 VariantClear(&varg2
);
1348 VariantClear(&varg1
);
1349 VariantClear(&varg0
);
1354 /* Wrapper around create_automation_object to create an installer object. */
1355 HRESULT
create_msiserver(IUnknown
*pOuter
, LPVOID
*ppObj
)
1357 return create_automation_object(0, pOuter
, ppObj
, &DIID_Installer
, InstallerImpl_Invoke
, NULL
, 0);
1360 /* Wrapper around create_automation_object to create a session object. */
1361 HRESULT
create_session(MSIHANDLE msiHandle
, IDispatch
*pInstaller
, IDispatch
**pDispatch
)
1363 HRESULT hr
= create_automation_object(msiHandle
, NULL
, (LPVOID
)pDispatch
, &DIID_Session
, SessionImpl_Invoke
, NULL
, sizeof(SessionData
));
1364 if (SUCCEEDED(hr
) && pDispatch
&& *pDispatch
)
1365 ((SessionData
*)private_data((AutomationObject
*)*pDispatch
))->pInstaller
= pInstaller
;