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_STRINGDATA:
570 if (wFlags & DISPATCH_PROPERTYGET) {
571 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
572 if (FAILED(hr)) return hr;
573 V_VT(pVarResult) = VT_BSTR;
574 V_BSTR(pVarResult) = NULL;
575 ret = MsiRecordGetStringW(This->msiHandle, V_I4(&varg0), NULL, &dwLen);
576 if (ret == ERROR_SUCCESS)
578 szString = msi_alloc((++dwLen)*sizeof(WCHAR));
581 if ((ret = MsiRecordGetStringW(This->msiHandle, V_I4(&varg0), szString, &dwLen)) == ERROR_SUCCESS)
582 V_BSTR(pVarResult) = SysAllocString(szString);
586 if (ret != ERROR_SUCCESS)
587 ERR("MsiRecordGetString returned %d\n", ret);
588 } else if (wFlags & DISPATCH_PROPERTYPUT) {
589 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
590 if (FAILED(hr)) return hr;
591 hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_BSTR, &varg1, puArgErr);
592 if (FAILED(hr)) return hr;
593 if ((ret = MsiRecordSetStringW(This->msiHandle, V_I4(&varg0), V_BSTR(&varg1))) != ERROR_SUCCESS)
595 VariantClear(&varg1);
596 ERR("MsiRecordSetString returned %d\n", ret);
597 return DISP_E_EXCEPTION;
603 return DISP_E_MEMBERNOTFOUND;
606 VariantClear(&varg1);
607 VariantClear(&varg0);
612 static HRESULT WINAPI StringListImpl_Invoke(
613 AutomationObject* This,
618 DISPPARAMS* pDispParams,
620 EXCEPINFO* pExcepInfo,
623 StringListData *data = (StringListData *)private_data(This);
629 switch (dispIdMember)
631 case DISPID_STRINGLIST_ITEM:
632 if (wFlags & DISPATCH_PROPERTYGET) {
633 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
634 if (FAILED(hr)) return hr;
635 if (V_I4(&varg0) < 0 || V_I4(&varg0) >= data->iCount)
636 return DISP_E_BADINDEX;
637 V_VT(pVarResult) = VT_BSTR;
638 V_BSTR(pVarResult) = SysAllocString(data->pszStrings[V_I4(&varg0)]);
642 case DISPID_STRINGLIST_COUNT:
643 if (wFlags & DISPATCH_PROPERTYGET) {
644 V_VT(pVarResult) = VT_I4;
645 V_I4(pVarResult) = data->iCount;
650 return DISP_E_MEMBERNOTFOUND;
653 VariantClear(&varg0);
658 static void WINAPI StringListImpl_Free(AutomationObject *This)
660 StringListData *data = private_data(This);
663 for (idx=0; idx<data->iCount; idx++)
664 SysFreeString(data->pszStrings[idx]);
665 HeapFree(GetProcessHeap(), 0, data->pszStrings);
668 static HRESULT WINAPI ViewImpl_Invoke(
669 AutomationObject* This,
674 DISPPARAMS* pDispParams,
676 EXCEPINFO* pExcepInfo,
680 IDispatch *pDispatch = NULL;
682 VARIANTARG varg0, varg1;
688 switch (dispIdMember)
690 case DISPID_VIEW_EXECUTE:
691 if (wFlags & DISPATCH_METHOD)
693 hr = DispGetParam(pDispParams, 0, VT_DISPATCH, &varg0, puArgErr);
694 if (SUCCEEDED(hr) && V_DISPATCH(&varg0) != NULL)
695 MsiViewExecute(This->msiHandle, ((AutomationObject *)V_DISPATCH(&varg0))->msiHandle);
697 MsiViewExecute(This->msiHandle, 0);
701 case DISPID_VIEW_FETCH:
702 if (wFlags & DISPATCH_METHOD)
704 V_VT(pVarResult) = VT_DISPATCH;
705 if ((ret = MsiViewFetch(This->msiHandle, &msiHandle)) == ERROR_SUCCESS)
707 if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_Record, RecordImpl_Invoke, NULL, 0)))
709 IDispatch_AddRef(pDispatch);
710 V_DISPATCH(pVarResult) = pDispatch;
713 ERR("Failed to create Record object, hresult 0x%08x\n", hr);
715 else if (ret == ERROR_NO_MORE_ITEMS)
716 V_DISPATCH(pVarResult) = NULL;
719 ERR("MsiViewFetch returned %d\n", ret);
720 return DISP_E_EXCEPTION;
725 case DISPID_VIEW_CLOSE:
726 if (wFlags & DISPATCH_METHOD)
728 MsiViewClose(This->msiHandle);
733 return DISP_E_MEMBERNOTFOUND;
736 VariantClear(&varg1);
737 VariantClear(&varg0);
742 static HRESULT WINAPI DatabaseImpl_Invoke(
743 AutomationObject* This,
748 DISPPARAMS* pDispParams,
750 EXCEPINFO* pExcepInfo,
754 IDispatch *pDispatch = NULL;
756 VARIANTARG varg0, varg1;
762 switch (dispIdMember)
764 case DISPID_DATABASE_OPENVIEW:
765 if (wFlags & DISPATCH_METHOD)
767 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
768 if (FAILED(hr)) return hr;
769 V_VT(pVarResult) = VT_DISPATCH;
770 if ((ret = MsiDatabaseOpenViewW(This->msiHandle, V_BSTR(&varg0), &msiHandle)) == ERROR_SUCCESS)
772 if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_View, ViewImpl_Invoke, NULL, 0)))
774 IDispatch_AddRef(pDispatch);
775 V_DISPATCH(pVarResult) = pDispatch;
778 ERR("Failed to create View object, hresult 0x%08x\n", hr);
782 VariantClear(&varg0);
783 ERR("MsiDatabaseOpenView returned %d\n", ret);
784 return DISP_E_EXCEPTION;
790 return DISP_E_MEMBERNOTFOUND;
793 VariantClear(&varg1);
794 VariantClear(&varg0);
799 static HRESULT WINAPI SessionImpl_Invoke(
800 AutomationObject* This,
805 DISPPARAMS* pDispParams,
807 EXCEPINFO* pExcepInfo,
810 SessionData *data = private_data(This);
813 IDispatch *pDispatch = NULL;
817 INSTALLSTATE iInstalled, iAction;
818 VARIANTARG varg0, varg1;
824 switch (dispIdMember)
826 case DISPID_SESSION_INSTALLER:
827 if (wFlags & DISPATCH_PROPERTYGET) {
828 V_VT(pVarResult) = VT_DISPATCH;
829 IDispatch_AddRef(data->pInstaller);
830 V_DISPATCH(pVarResult) = data->pInstaller;
834 case DISPID_SESSION_PROPERTY:
835 if (wFlags & DISPATCH_PROPERTYGET) {
836 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
837 if (FAILED(hr)) return hr;
838 V_VT(pVarResult) = VT_BSTR;
839 V_BSTR(pVarResult) = NULL;
840 ret = MsiGetPropertyW(This->msiHandle, V_BSTR(&varg0), NULL, &dwLen);
841 if (ret == ERROR_SUCCESS)
843 szString = msi_alloc((++dwLen)*sizeof(WCHAR));
846 if ((ret = MsiGetPropertyW(This->msiHandle, V_BSTR(&varg0), szString, &dwLen)) == ERROR_SUCCESS)
847 V_BSTR(pVarResult) = SysAllocString(szString);
851 if (ret != ERROR_SUCCESS)
852 ERR("MsiGetProperty returned %d\n", ret);
853 } else if (wFlags & DISPATCH_PROPERTYPUT) {
854 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
855 if (FAILED(hr)) return hr;
856 hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_BSTR, &varg1, puArgErr);
858 VariantClear(&varg0);
861 if ((ret = MsiSetPropertyW(This->msiHandle, V_BSTR(&varg0), V_BSTR(&varg1))) != ERROR_SUCCESS)
863 VariantClear(&varg0);
864 VariantClear(&varg1);
865 ERR("MsiSetProperty returned %d\n", ret);
866 return DISP_E_EXCEPTION;
871 case DISPID_SESSION_LANGUAGE:
872 if (wFlags & DISPATCH_PROPERTYGET) {
873 langId = MsiGetLanguage(This->msiHandle);
874 V_VT(pVarResult) = VT_I4;
875 V_I4(pVarResult) = langId;
879 case DISPID_SESSION_MODE:
880 if (wFlags & DISPATCH_PROPERTYGET) {
881 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
882 if (FAILED(hr)) return hr;
883 V_VT(pVarResult) = VT_BOOL;
884 V_BOOL(pVarResult) = MsiGetMode(This->msiHandle, V_I4(&varg0));
885 } else if (wFlags & DISPATCH_PROPERTYPUT) {
886 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
887 if (FAILED(hr)) return hr;
888 hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_BOOL, &varg1, puArgErr);
889 if (FAILED(hr)) return hr;
890 if ((ret = MsiSetMode(This->msiHandle, V_I4(&varg0), V_BOOL(&varg1))) != ERROR_SUCCESS)
892 ERR("MsiSetMode returned %d\n", ret);
893 return DISP_E_EXCEPTION;
898 case DISPID_SESSION_DATABASE:
899 if (wFlags & DISPATCH_PROPERTYGET) {
900 V_VT(pVarResult) = VT_DISPATCH;
901 if ((msiHandle = MsiGetActiveDatabase(This->msiHandle)))
903 if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_Database, DatabaseImpl_Invoke, NULL, 0)))
905 IDispatch_AddRef(pDispatch);
906 V_DISPATCH(pVarResult) = pDispatch;
909 ERR("Failed to create Database object, hresult 0x%08x\n", hr);
913 ERR("MsiGetActiveDatabase failed\n");
914 return DISP_E_EXCEPTION;
919 case DISPID_SESSION_DOACTION:
920 if (wFlags & DISPATCH_METHOD) {
921 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
922 if (FAILED(hr)) return hr;
923 ret = MsiDoActionW(This->msiHandle, V_BSTR(&varg0));
924 V_VT(pVarResult) = VT_I4;
927 case ERROR_FUNCTION_NOT_CALLED:
928 V_I4(pVarResult) = msiDoActionStatusNoAction;
931 V_I4(pVarResult) = msiDoActionStatusSuccess;
933 case ERROR_INSTALL_USEREXIT:
934 V_I4(pVarResult) = msiDoActionStatusUserExit;
936 case ERROR_INSTALL_FAILURE:
937 V_I4(pVarResult) = msiDoActionStatusFailure;
939 case ERROR_INSTALL_SUSPEND:
940 V_I4(pVarResult) = msiDoActionStatusSuspend;
942 case ERROR_MORE_DATA:
943 V_I4(pVarResult) = msiDoActionStatusFinished;
945 case ERROR_INVALID_HANDLE_STATE:
946 V_I4(pVarResult) = msiDoActionStatusWrongState;
948 case ERROR_INVALID_DATA:
949 V_I4(pVarResult) = msiDoActionStatusBadActionData;
952 VariantClear(&varg0);
953 FIXME("MsiDoAction returned unhandled value %d\n", ret);
954 return DISP_E_EXCEPTION;
959 case DISPID_SESSION_EVALUATECONDITION:
960 if (wFlags & DISPATCH_METHOD) {
961 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
962 if (FAILED(hr)) return hr;
963 V_VT(pVarResult) = VT_I4;
964 V_I4(pVarResult) = MsiEvaluateConditionW(This->msiHandle, V_BSTR(&varg0));
968 case DISPID_SESSION_SETINSTALLLEVEL:
969 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
970 if (FAILED(hr)) return hr;
971 if ((ret = MsiSetInstallLevel(This->msiHandle, V_I4(&varg0))) != ERROR_SUCCESS)
973 ERR("MsiSetInstallLevel returned %d\n", ret);
974 return DISP_E_EXCEPTION;
978 case DISPID_SESSION_FEATURECURRENTSTATE:
979 if (wFlags & DISPATCH_PROPERTYGET) {
980 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
981 if (FAILED(hr)) return hr;
982 V_VT(pVarResult) = VT_I4;
983 if ((ret = MsiGetFeatureStateW(This->msiHandle, V_BSTR(&varg0), &iInstalled, &iAction)) == ERROR_SUCCESS)
984 V_I4(pVarResult) = iInstalled;
987 ERR("MsiGetFeatureState returned %d\n", ret);
988 V_I4(pVarResult) = msiInstallStateUnknown;
993 case DISPID_SESSION_FEATUREREQUESTSTATE:
994 if (wFlags & DISPATCH_PROPERTYGET) {
995 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
996 if (FAILED(hr)) return hr;
997 V_VT(pVarResult) = VT_I4;
998 if ((ret = MsiGetFeatureStateW(This->msiHandle, V_BSTR(&varg0), &iInstalled, &iAction)) == ERROR_SUCCESS)
999 V_I4(pVarResult) = iAction;
1002 ERR("MsiGetFeatureState returned %d\n", ret);
1003 V_I4(pVarResult) = msiInstallStateUnknown;
1005 } else if (wFlags & DISPATCH_PROPERTYPUT) {
1006 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1007 if (FAILED(hr)) return hr;
1008 hr = DispGetParam(pDispParams, DISPID_PROPERTYPUT, VT_I4, &varg1, puArgErr);
1010 VariantClear(&varg0);
1013 if ((ret = MsiSetFeatureStateW(This->msiHandle, V_BSTR(&varg0), V_I4(&varg1))) != ERROR_SUCCESS)
1015 VariantClear(&varg0);
1016 ERR("MsiSetFeatureState returned %d\n", ret);
1017 return DISP_E_EXCEPTION;
1023 return DISP_E_MEMBERNOTFOUND;
1026 VariantClear(&varg1);
1027 VariantClear(&varg0);
1032 /* Fill the variant pointed to by pVarResult with the value & size returned by RegQueryValueEx as dictated by the
1033 * registry value type. Used by Installer::RegistryValue. */
1034 static void variant_from_registry_value(VARIANT *pVarResult, DWORD dwType, LPBYTE lpData, DWORD dwSize)
1036 static const WCHAR szREG_BINARY[] = { '(','R','E','G','_','B','I','N','A','R','Y',')',0 };
1037 static const WCHAR szREG_[] = { '(','R','E','G','_',']',0 };
1038 WCHAR *szString = (WCHAR *)lpData;
1039 LPWSTR szNewString = NULL;
1040 DWORD dwNewSize = 0;
1045 /* Registry strings may not be null terminated so we must use SysAllocStringByteLen/Len */
1046 case REG_MULTI_SZ: /* Multi SZ change internal null characters to newlines */
1047 idx = (dwSize/sizeof(WCHAR))-1;
1048 while (idx >= 0 && !szString[idx]) idx--;
1049 for (; idx >= 0; idx--)
1050 if (!szString[idx]) szString[idx] = '\n';
1052 V_VT(pVarResult) = VT_BSTR;
1053 V_BSTR(pVarResult) = SysAllocStringByteLen((LPCSTR)szString, dwSize);
1057 if (!(dwNewSize = ExpandEnvironmentStringsW(szString, szNewString, dwNewSize)))
1058 ERR("ExpandEnvironmentStrings returned error %d\n", GetLastError());
1059 else if (!(szNewString = msi_alloc(dwNewSize)))
1060 ERR("Out of memory\n");
1061 else if (!(dwNewSize = ExpandEnvironmentStringsW(szString, szNewString, dwNewSize)))
1062 ERR("ExpandEnvironmentStrings returned error %d\n", GetLastError());
1065 V_VT(pVarResult) = VT_BSTR;
1066 V_BSTR(pVarResult) = SysAllocStringLen(szNewString, dwNewSize);
1068 msi_free(szNewString);
1072 V_VT(pVarResult) = VT_I4;
1073 V_I4(pVarResult) = *((DWORD *)lpData);
1077 V_VT(pVarResult) = VT_BSTR;
1078 V_BSTR(pVarResult) = SysAllocString(szREG_); /* Weird string, don't know why native returns it */
1082 V_VT(pVarResult) = VT_BSTR;
1083 V_BSTR(pVarResult) = SysAllocString(szREG_BINARY);
1087 V_VT(pVarResult) = VT_EMPTY;
1091 FIXME("Unhandled registry value type %d\n", dwType);
1095 static HRESULT WINAPI InstallerImpl_Invoke(
1096 AutomationObject* This,
1097 DISPID dispIdMember,
1101 DISPPARAMS* pDispParams,
1102 VARIANT* pVarResult,
1103 EXCEPINFO* pExcepInfo,
1106 MSIHANDLE msiHandle;
1107 IDispatch *pDispatch = NULL;
1109 VARIANTARG varg0, varg1, varg2;
1112 VariantInit(&varg0);
1113 VariantInit(&varg1);
1114 VariantInit(&varg2);
1116 switch (dispIdMember)
1118 case DISPID_INSTALLER_CREATERECORD:
1119 if (wFlags & DISPATCH_METHOD)
1121 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1122 if (FAILED(hr)) return hr;
1123 V_VT(pVarResult) = VT_DISPATCH;
1124 if ((msiHandle = MsiCreateRecord(V_I4(&varg0))))
1126 if (SUCCEEDED(hr = create_automation_object(msiHandle, NULL, (LPVOID*)&pDispatch, &DIID_Record, RecordImpl_Invoke, NULL, 0)))
1128 IDispatch_AddRef(pDispatch);
1129 V_DISPATCH(pVarResult) = pDispatch;
1132 ERR("Failed to create Record object, hresult 0x%08x\n", hr);
1136 ERR("MsiCreateRecord failed\n");
1137 return DISP_E_EXCEPTION;
1142 case DISPID_INSTALLER_OPENPACKAGE:
1143 if (wFlags & DISPATCH_METHOD)
1145 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1146 if (FAILED(hr)) return hr;
1147 hr = DispGetParam(pDispParams, 1, VT_I4, &varg1, puArgErr);
1150 VariantClear(&varg0);
1153 V_VT(pVarResult) = VT_DISPATCH;
1154 if ((ret = MsiOpenPackageExW(V_BSTR(&varg0), V_I4(&varg1), &msiHandle)) == ERROR_SUCCESS)
1156 if (SUCCEEDED(hr = create_session(msiHandle, (IDispatch *)This, &pDispatch)))
1158 IDispatch_AddRef(pDispatch);
1159 V_DISPATCH(pVarResult) = pDispatch;
1162 ERR("Failed to create Session object, hresult 0x%08x\n", hr);
1166 VariantClear(&varg0);
1167 ERR("MsiOpenPackageEx returned %d\n", ret);
1168 return DISP_E_EXCEPTION;
1173 case DISPID_INSTALLER_REGISTRYVALUE:
1174 if (wFlags & DISPATCH_METHOD) {
1176 LPWSTR szString = NULL;
1177 DWORD dwSize = 0, dwType;
1178 UINT posValue = 2; /* Save valuePos so we can save puArgErr if we are unable to do our type conversions */
1180 hr = DispGetParam(pDispParams, 0, VT_I4, &varg0, puArgErr);
1181 if (FAILED(hr)) return hr;
1182 hr = DispGetParam(pDispParams, 1, VT_BSTR, &varg1, puArgErr);
1183 if (FAILED(hr)) return hr;
1184 hr = DispGetParam_CopyOnly(pDispParams, &posValue, &varg2);
1187 VariantClear(&varg1);
1190 ret = RegOpenKeyW((HKEY)V_I4(&varg0), V_BSTR(&varg1), &hkey);
1192 /* Third parameter can be VT_EMPTY, VT_I4, or VT_BSTR */
1193 switch (V_VT(&varg2))
1195 case VT_EMPTY: /* Return VT_BOOL as to whether or not registry key exists */
1196 V_VT(pVarResult) = VT_BOOL;
1197 V_BOOL(pVarResult) = (ret == ERROR_SUCCESS);
1200 case VT_BSTR: /* Return value of specified key if it exists */
1201 if (ret == ERROR_SUCCESS &&
1202 (ret = RegQueryValueExW(hkey, V_BSTR(&varg2), NULL, NULL, NULL, &dwSize)) == ERROR_SUCCESS)
1204 if (!(szString = msi_alloc(dwSize)))
1205 ERR("Out of memory\n");
1206 else if ((ret = RegQueryValueExW(hkey, V_BSTR(&varg2), NULL, &dwType, (LPBYTE)szString, &dwSize)) == ERROR_SUCCESS)
1207 variant_from_registry_value(pVarResult, dwType, (LPBYTE)szString, dwSize);
1210 if (ret != ERROR_SUCCESS)
1213 VariantClear(&varg2);
1214 VariantClear(&varg1);
1215 return DISP_E_BADINDEX;
1219 default: /* Try to make it into VT_I4, can use VariantChangeType for this */
1220 hr = VariantChangeType(&varg2, &varg2, 0, VT_I4);
1221 if (SUCCEEDED(hr) && ret != ERROR_SUCCESS) hr = DISP_E_BADINDEX; /* Conversion fine, but couldn't find key */
1224 if (hr == DISP_E_TYPEMISMATCH) *puArgErr = posValue;
1225 VariantClear(&varg2); /* Unknown type, so let's clear it */
1226 VariantClear(&varg1);
1230 /* Retrieve class name or maximum value name or subkey name size */
1232 ret = RegQueryInfoKeyW(hkey, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1233 else if (V_I4(&varg2) > 0)
1234 ret = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL);
1235 else /* V_I4(&varg2) < 0 */
1236 ret = RegQueryInfoKeyW(hkey, NULL, NULL, NULL, NULL, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL);
1238 if (ret == ERROR_SUCCESS)
1240 if (!(szString = msi_alloc(++dwSize * sizeof(WCHAR))))
1241 ERR("Out of memory\n");
1242 else if (!V_I4(&varg2))
1243 ret = RegQueryInfoKeyW(hkey, szString, &dwSize, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL);
1244 else if (V_I4(&varg2) > 0)
1245 ret = RegEnumValueW(hkey, V_I4(&varg2)-1, szString, &dwSize, 0, 0, NULL, NULL);
1246 else /* V_I4(&varg2) < 0 */
1247 ret = RegEnumKeyW(hkey, -1 - V_I4(&varg2), szString, dwSize);
1249 if (szString && ret == ERROR_SUCCESS)
1251 V_VT(pVarResult) = VT_BSTR;
1252 V_BSTR(pVarResult) = SysAllocString(szString);
1262 case DISPID_INSTALLER_PRODUCTSTATE:
1263 if (wFlags & DISPATCH_PROPERTYGET) {
1264 hr = DispGetParam(pDispParams, 0, VT_BSTR, &varg0, puArgErr);
1265 if (FAILED(hr)) return hr;
1266 V_VT(pVarResult) = VT_I4;
1267 V_I4(pVarResult) = MsiQueryProductStateW(V_BSTR(&varg0));
1271 case DISPID_INSTALLER_PRODUCTS:
1272 if (wFlags & DISPATCH_PROPERTYGET)
1274 StringListData *sldata = NULL;
1276 WCHAR szProductBuf[GUID_SIZE];
1278 /* Find number of products */
1280 ret = MsiEnumProductsW(idx, szProductBuf);
1281 if (ret == ERROR_SUCCESS) idx++;
1282 } while (ret == ERROR_SUCCESS && ret != ERROR_NO_MORE_ITEMS);
1284 if (ret != ERROR_SUCCESS && ret != ERROR_NO_MORE_ITEMS)
1286 ERR("MsiEnumProducts returned %d\n", ret);
1287 return DISP_E_EXCEPTION;
1290 V_VT(pVarResult) = VT_DISPATCH;
1291 if (SUCCEEDED(hr = create_automation_object(0, NULL, (LPVOID*)&pDispatch, &DIID_StringList, StringListImpl_Invoke, StringListImpl_Free, sizeof(StringListData))))
1293 IDispatch_AddRef(pDispatch);
1294 V_DISPATCH(pVarResult) = pDispatch;
1296 /* Save product strings */
1297 sldata = (StringListData *)private_data((AutomationObject *)pDispatch);
1298 sldata->iCount = idx;
1299 sldata->pszStrings = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(LPWSTR)*sldata->iCount);
1300 for (idx = 0; idx < sldata->iCount; idx++)
1302 ret = MsiEnumProductsW(idx, szProductBuf);
1303 sldata->pszStrings[idx] = SysAllocString(szProductBuf);
1307 ERR("Failed to create StringList object, hresult 0x%08x\n", hr);
1312 return DISP_E_MEMBERNOTFOUND;
1315 VariantClear(&varg2);
1316 VariantClear(&varg1);
1317 VariantClear(&varg0);
1322 /* Wrapper around create_automation_object to create an installer object. */
1323 HRESULT create_msiserver(IUnknown *pOuter, LPVOID *ppObj)
1325 return create_automation_object(0, pOuter, ppObj, &DIID_Installer, InstallerImpl_Invoke, NULL, 0);
1328 /* Wrapper around create_automation_object to create a session object. */
1329 HRESULT create_session(MSIHANDLE msiHandle, IDispatch *pInstaller, IDispatch **pDispatch)
1331 HRESULT hr = create_automation_object(msiHandle, NULL, (LPVOID)pDispatch, &DIID_Session, SessionImpl_Invoke, NULL, sizeof(SessionData));
1332 if (SUCCEEDED(hr) && pDispatch && *pDispatch)
1333 ((SessionData *)private_data((AutomationObject *)*pDispatch))->pInstaller = pInstaller;