msi: Expand IDL file to contain some OLE automation interface functions.
[wine] / dlls / msi / automation.c
1 /*
2  * Implementation of OLE Automation for Microsoft Installer (msi.dll)
3  *
4  * Copyright 2007 Misha Koshelev
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20
21 #define COBJMACROS
22
23 #include <stdarg.h>
24 #include "windef.h"
25 #include "winbase.h"
26 #include "winerror.h"
27 #include "winuser.h"
28 #include "msidefs.h"
29 #include "msipriv.h"
30 #include "activscp.h"
31 #include "oleauto.h"
32 #include "wine/debug.h"
33 #include "wine/unicode.h"
34
35 #include "msiserver.h"
36 #include "msiserver_dispids.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(msi);
39
40 /*
41  * AutomationObject - "base" class for all automation objects. For each interface, we implement Invoke function
42  *                    called from AutomationObject::Invoke, and pass this function to create_automation_object.
43  */
44
45 typedef interface AutomationObject AutomationObject;
46
47 interface AutomationObject {
48     /*
49      * VTables - We provide IDispatch, IProvideClassInfo, IProvideClassInfo2, IProvideMultipleClassInfo
50      */
51     const IDispatchVtbl *lpVtbl;
52     const IProvideMultipleClassInfoVtbl *lpvtblIProvideMultipleClassInfo;
53
54     /* Object reference count */
55     LONG ref;
56
57     /* Clsid for this class and it's appropriate ITypeInfo object */
58     LPCLSID clsid;
59     ITypeInfo *iTypeInfo;
60
61     /* The MSI handle of the current object */
62     MSIHANDLE msiHandle;
63
64     /* A function that is called from AutomationObject::Invoke, specific to this type of object. */
65     HRESULT (STDMETHODCALLTYPE *funcInvoke)(
66         AutomationObject* This,
67         DISPID dispIdMember,
68         REFIID riid,
69         LCID lcid,
70         WORD wFlags,
71         DISPPARAMS* pDispParams,
72         VARIANT* pVarResult,
73         EXCEPINFO* pExcepInfo,
74         UINT* puArgErr);
75 };
76
77 /* VTables */
78 static const struct IDispatchVtbl AutomationObject_Vtbl;
79 static const struct IProvideMultipleClassInfoVtbl AutomationObject_IProvideMultipleClassInfo_Vtbl;
80
81 /* Load type info so we don't have to process GetIDsOfNames */
82 HRESULT load_type_info(IDispatch *iface, ITypeInfo **pptinfo, REFIID clsid, LCID lcid)
83 {
84     HRESULT hr;
85     LPTYPELIB pLib = NULL;
86     LPTYPEINFO pInfo = NULL;
87     static const WCHAR szMsiServer[] = {'m','s','i','s','e','r','v','e','r','.','t','l','b'};
88
89     TRACE("(%p)->(%s,%d)\n", iface, debugstr_guid(clsid), lcid);
90
91     /* Load registered type library */
92     hr = LoadRegTypeLib(&LIBID_WindowsInstaller, 1, 0, lcid, &pLib);
93     if (FAILED(hr)) {
94         hr = LoadTypeLib(szMsiServer, &pLib);
95         if (FAILED(hr)) {
96             ERR("Could not load msiserver.tlb\n");
97             return hr;
98         }
99     }
100
101     /* Get type information for object */
102     hr = ITypeLib_GetTypeInfoOfGuid(pLib, clsid, &pInfo);
103     ITypeLib_Release(pLib);
104     if (FAILED(hr)) {
105         ERR("Could not load ITypeInfo for %s\n", debugstr_guid(clsid));
106         return hr;
107     }
108     *pptinfo = pInfo;
109     return S_OK;
110 }
111
112 /* Create the automation object, placing the result in the pointer ppObj. The automation object is created
113  * with the appropriate clsid and invocation function. */
114 HRESULT create_automation_object(MSIHANDLE msiHandle, IUnknown *pUnkOuter, LPVOID *ppObj, REFIID clsid,
115             HRESULT (STDMETHODCALLTYPE *funcInvoke)(AutomationObject*,DISPID,REFIID,LCID,WORD,DISPPARAMS*,
116                                                     VARIANT*,EXCEPINFO*,UINT*))
117 {
118     AutomationObject *object;
119     HRESULT hr;
120
121     TRACE("(%ld,%p,%p,%s,%p)\n", (unsigned long)msiHandle, pUnkOuter, ppObj, debugstr_guid(clsid), funcInvoke);
122
123     if( pUnkOuter )
124         return CLASS_E_NOAGGREGATION;
125
126     object = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(AutomationObject));
127
128     /* Set all the VTable references */
129     object->lpVtbl = &AutomationObject_Vtbl;
130     object->lpvtblIProvideMultipleClassInfo = &AutomationObject_IProvideMultipleClassInfo_Vtbl;
131     object->ref = 1;
132
133     /* Store data that was passed */
134     object->msiHandle = msiHandle;
135     object->clsid = (LPCLSID)clsid;
136     object->funcInvoke = funcInvoke;
137
138     /* Load our TypeInfo so we don't have to process GetIDsOfNames */
139     object->iTypeInfo = NULL;
140     hr = load_type_info((IDispatch *)object, &object->iTypeInfo, clsid, 0x0);
141     if (FAILED(hr)) {
142         HeapFree(GetProcessHeap(), 0, object);
143         return hr;
144     }
145
146     *ppObj = object;
147
148     return S_OK;
149 }
150
151 /* Macros to get pointer to AutomationObject from the other VTables. */
152 static inline AutomationObject *obj_from_IProvideMultipleClassInfo( IProvideMultipleClassInfo *iface )
153 {
154     return (AutomationObject *)((char*)iface - FIELD_OFFSET(AutomationObject, lpvtblIProvideMultipleClassInfo));
155 }
156
157 /*
158  * AutomationObject methods
159  */
160
161 /*** IUnknown methods ***/
162 static HRESULT WINAPI AutomationObject_QueryInterface(IDispatch* iface, REFIID riid, void** ppvObject)
163 {
164     AutomationObject *This = (AutomationObject *)iface;
165
166     TRACE("(%p/%p)->(%s,%p)\n", iface, This, debugstr_guid(riid), ppvObject);
167
168     if (ppvObject == NULL)
169       return E_INVALIDARG;
170
171     *ppvObject = 0;
172
173     if (IsEqualGUID(riid, &IID_IUnknown) || IsEqualGUID(riid, &IID_IDispatch) || IsEqualGUID(riid, This->clsid))
174         *ppvObject = This;
175     else if (IsEqualGUID(riid, &IID_IProvideClassInfo) ||
176              IsEqualGUID(riid, &IID_IProvideClassInfo2) ||
177              IsEqualGUID(riid, &IID_IProvideMultipleClassInfo))
178         *ppvObject = &This->lpvtblIProvideMultipleClassInfo;
179     else
180     {
181         TRACE("() : asking for unsupported interface %s\n",debugstr_guid(riid));
182         return E_NOINTERFACE;
183     }
184
185     /*
186      * Query Interface always increases the reference count by one when it is
187      * successful
188      */
189     IClassFactory_AddRef(iface);
190
191     return S_OK;
192 }
193
194 static ULONG WINAPI AutomationObject_AddRef(IDispatch* iface)
195 {
196     AutomationObject *This = (AutomationObject *)iface;
197
198     TRACE("(%p/%p)\n", iface, This);
199
200     return InterlockedIncrement(&This->ref);
201 }
202
203 static ULONG WINAPI AutomationObject_Release(IDispatch* iface)
204 {
205     AutomationObject *This = (AutomationObject *)iface;
206     ULONG ref = InterlockedDecrement(&This->ref);
207
208     TRACE("(%p/%p)\n", iface, This);
209
210     if (!ref)
211     {
212         MsiCloseHandle(This->msiHandle);
213         HeapFree(GetProcessHeap(), 0, This);
214     }
215
216     return ref;
217 }
218
219 /*** IDispatch methods ***/
220 static HRESULT WINAPI AutomationObject_GetTypeInfoCount(
221         IDispatch* iface,
222         UINT* pctinfo)
223 {
224     AutomationObject *This = (AutomationObject *)iface;
225
226     TRACE("(%p/%p)->(%p)\n", iface, This, pctinfo);
227     *pctinfo = 1;
228     return S_OK;
229 }
230
231 static HRESULT WINAPI AutomationObject_GetTypeInfo(
232         IDispatch* iface,
233         UINT iTInfo,
234         LCID lcid,
235         ITypeInfo** ppTInfo)
236 {
237     AutomationObject *This = (AutomationObject *)iface;
238     TRACE("(%p/%p)->(%d,%d,%p)\n", iface, This, iTInfo, lcid, ppTInfo);
239
240     ITypeInfo_AddRef(This->iTypeInfo);
241     *ppTInfo = This->iTypeInfo;
242     return S_OK;
243 }
244
245 static HRESULT WINAPI AutomationObject_GetIDsOfNames(
246         IDispatch* iface,
247         REFIID riid,
248         LPOLESTR* rgszNames,
249         UINT cNames,
250         LCID lcid,
251         DISPID* rgDispId)
252 {
253     AutomationObject *This = (AutomationObject *)iface;
254     TRACE("(%p/%p)->(%p,%p,%d,%d,%p)\n", iface, This, riid, rgszNames, cNames, lcid, rgDispId);
255
256     if (!IsEqualGUID(riid, &IID_NULL)) return E_INVALIDARG;
257     return ITypeInfo_GetIDsOfNames(This->iTypeInfo, rgszNames, cNames, rgDispId);
258 }
259
260 /* Maximum number of allowed function parameters+1 */
261 #define MAX_FUNC_PARAMS 20
262
263 /* Some error checking is done here to simplify individual object function invocation */
264 static HRESULT WINAPI AutomationObject_Invoke(
265         IDispatch* iface,
266         DISPID dispIdMember,
267         REFIID riid,
268         LCID lcid,
269         WORD wFlags,
270         DISPPARAMS* pDispParams,
271         VARIANT* pVarResult,
272         EXCEPINFO* pExcepInfo,
273         UINT* puArgErr)
274 {
275     AutomationObject *This = (AutomationObject *)iface;
276     HRESULT hr;
277     unsigned int uArgErr;
278     VARIANT varResultDummy;
279     BSTR bstrName = NULL;
280
281     TRACE("(%p/%p)->(%d,%p,%d,%d,%p,%p,%p,%p)\n", iface, This, dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
282
283     if (!IsEqualIID(riid, &IID_NULL))
284     {
285         ERR("riid was %s instead of IID_NULL\n", debugstr_guid(riid));
286         return DISP_E_UNKNOWNNAME;
287     }
288
289     if (wFlags & DISPATCH_PROPERTYGET && !pVarResult)
290     {
291         ERR("NULL pVarResult not allowed when DISPATCH_PROPERTYGET specified\n");
292         return DISP_E_PARAMNOTOPTIONAL;
293     }
294
295     /* This simplifies our individual object invocation functions */
296     if (puArgErr == NULL) puArgErr = &uArgErr;
297     if (pVarResult == NULL) pVarResult = &varResultDummy;
298
299     /* Assume return type is void unless determined otherwise */
300     VariantInit(pVarResult);
301
302     /* If we are tracing, we want to see the name of the member we are invoking */
303     if (TRACE_ON(msi))
304     {
305         ITypeInfo_GetDocumentation(This->iTypeInfo, dispIdMember, &bstrName, NULL, NULL, NULL);
306         TRACE("Method %d, %s\n", dispIdMember, debugstr_w(bstrName));
307     }
308
309     hr = This->funcInvoke(This,dispIdMember,riid,lcid,wFlags,pDispParams,pVarResult,pExcepInfo,puArgErr);
310
311     if (hr == DISP_E_MEMBERNOTFOUND) {
312         if (bstrName == NULL) ITypeInfo_GetDocumentation(This->iTypeInfo, dispIdMember, &bstrName, NULL, NULL, NULL);
313         FIXME("Method %d, %s wflags %d not implemented, clsid %s\n", dispIdMember, debugstr_w(bstrName), wFlags, debugstr_guid(This->clsid));
314     }
315     else if (pExcepInfo &&
316              (hr == DISP_E_PARAMNOTFOUND ||
317               hr == DISP_E_EXCEPTION)) {
318         static const WCHAR szComma[] = { ',',0 };
319         static WCHAR szExceptionSource[] = {'M','s','i',' ','A','P','I',' ','E','r','r','o','r',0};
320         WCHAR szExceptionDescription[MAX_PATH];
321         BSTR bstrParamNames[MAX_FUNC_PARAMS];
322         unsigned namesNo, i;
323         BOOL bFirst = TRUE;
324
325         if (FAILED(ITypeInfo_GetNames(This->iTypeInfo, dispIdMember, bstrParamNames,
326                                       MAX_FUNC_PARAMS, &namesNo)))
327         {
328             TRACE("Failed to retrieve names for dispIdMember %d\n", dispIdMember);
329         }
330         else
331         {
332             memset(szExceptionDescription, 0, sizeof(szExceptionDescription));
333             for (i=0; i<namesNo; i++)
334             {
335                 if (bFirst) bFirst = FALSE;
336                 else {
337                     lstrcpyW(&szExceptionDescription[lstrlenW(szExceptionDescription)], szComma);
338                 }
339                 lstrcpyW(&szExceptionDescription[lstrlenW(szExceptionDescription)], bstrParamNames[i]);
340                 SysFreeString(bstrParamNames[i]);
341             }
342
343             memset(pExcepInfo, 0, sizeof(EXCEPINFO));
344             pExcepInfo->wCode = 1000;
345             pExcepInfo->bstrSource = szExceptionSource;
346             pExcepInfo->bstrDescription = szExceptionDescription;
347             hr = DISP_E_EXCEPTION;
348         }
349     }
350
351     /* Make sure we free the return variant if it is our dummy variant */
352     if (pVarResult == &varResultDummy) VariantClear(pVarResult);
353
354     /* Free function name if we retrieved it */
355     if (bstrName) SysFreeString(bstrName);
356
357     TRACE("Returning 0x%08x, %s\n", hr, SUCCEEDED(hr) ? "ok" : "not ok");
358
359     return hr;
360 }
361
362 static const struct IDispatchVtbl AutomationObject_Vtbl =
363 {
364     AutomationObject_QueryInterface,
365     AutomationObject_AddRef,
366     AutomationObject_Release,
367     AutomationObject_GetTypeInfoCount,
368     AutomationObject_GetTypeInfo,
369     AutomationObject_GetIDsOfNames,
370     AutomationObject_Invoke
371 };
372
373 /*
374  * IProvideMultipleClassInfo methods
375  */
376
377 static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_QueryInterface(
378   IProvideMultipleClassInfo* iface,
379   REFIID     riid,
380   VOID**     ppvoid)
381 {
382     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
383     return AutomationObject_QueryInterface((IDispatch *)This, riid, ppvoid);
384 }
385
386 static ULONG WINAPI AutomationObject_IProvideMultipleClassInfo_AddRef(IProvideMultipleClassInfo* iface)
387 {
388     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
389     return AutomationObject_AddRef((IDispatch *)This);
390 }
391
392 static ULONG WINAPI AutomationObject_IProvideMultipleClassInfo_Release(IProvideMultipleClassInfo* iface)
393 {
394     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
395     return AutomationObject_Release((IDispatch *)This);
396 }
397
398 static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_GetClassInfo(IProvideMultipleClassInfo* iface, ITypeInfo** ppTI)
399 {
400     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
401     TRACE("(%p/%p)->(%p)\n", iface, This, ppTI);
402     return load_type_info((IDispatch *)This, ppTI, This->clsid, 0);
403 }
404
405 static HRESULT WINAPI AutomationObject_IProvideMultipleClassInfo_GetGUID(IProvideMultipleClassInfo* iface, DWORD dwGuidKind, GUID* pGUID)
406 {
407     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
408     TRACE("(%p/%p)->(%d,%s)\n", iface, This, dwGuidKind, debugstr_guid(pGUID));
409
410     if (dwGuidKind != GUIDKIND_DEFAULT_SOURCE_DISP_IID)
411         return E_INVALIDARG;
412     else {
413         *pGUID = *This->clsid;
414         return S_OK;
415     }
416 }
417
418 static HRESULT WINAPI AutomationObject_GetMultiTypeInfoCount(IProvideMultipleClassInfo* iface, ULONG* pcti)
419 {
420     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
421
422     TRACE("(%p/%p)->(%p)\n", iface, This, pcti);
423     *pcti = 1;
424     return S_OK;
425 }
426
427 static HRESULT WINAPI AutomationObject_GetInfoOfIndex(IProvideMultipleClassInfo* iface,
428         ULONG iti,
429         DWORD dwFlags,
430         ITypeInfo** pptiCoClass,
431         DWORD* pdwTIFlags,
432         ULONG* pcdispidReserved,
433         IID* piidPrimary,
434         IID* piidSource)
435 {
436     AutomationObject *This = obj_from_IProvideMultipleClassInfo(iface);
437
438     TRACE("(%p/%p)->(%d,%d,%p,%p,%p,%p,%p)\n", iface, This, iti, dwFlags, pptiCoClass, pdwTIFlags, pcdispidReserved, piidPrimary, piidSource);
439
440     if (iti != 0)
441         return E_INVALIDARG;
442
443     if (dwFlags & MULTICLASSINFO_GETTYPEINFO)
444         load_type_info((IDispatch *)This, pptiCoClass, This->clsid, 0);
445
446     if (dwFlags & MULTICLASSINFO_GETNUMRESERVEDDISPIDS)
447     {
448         *pdwTIFlags = 0;
449         *pcdispidReserved = 0;
450     }
451
452     if (dwFlags & MULTICLASSINFO_GETIIDPRIMARY){
453         *piidPrimary = *This->clsid;
454     }
455
456     if (dwFlags & MULTICLASSINFO_GETIIDSOURCE){
457         *piidSource = *This->clsid;
458     }
459
460     return S_OK;
461 }
462
463 static const IProvideMultipleClassInfoVtbl AutomationObject_IProvideMultipleClassInfo_Vtbl =
464 {
465     AutomationObject_IProvideMultipleClassInfo_QueryInterface,
466     AutomationObject_IProvideMultipleClassInfo_AddRef,
467     AutomationObject_IProvideMultipleClassInfo_Release,
468     AutomationObject_IProvideMultipleClassInfo_GetClassInfo,
469     AutomationObject_IProvideMultipleClassInfo_GetGUID,
470     AutomationObject_GetMultiTypeInfoCount,
471     AutomationObject_GetInfoOfIndex
472 };