oleaut32: Added SAFEARRAY support for RecordInfo::RecordClear.
[wine] / dlls / oleaut32 / recinfo.c
1 /*
2  * Copyright 2005 Jacek Caban
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2.1 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  */
18
19 #include <stdarg.h>
20
21 #define COBJMACROS
22 #define NONAMELESSUNION
23 #define NONAMELESSSTRUCT
24
25 #include "windef.h"
26 #include "winbase.h"
27 #include "objbase.h"
28 #include "oaidl.h"
29 #include "oleauto.h"
30
31 #include "wine/unicode.h"
32 #include "wine/debug.h"
33
34 WINE_DEFAULT_DEBUG_CHANNEL(ole);
35
36 typedef struct {
37     enum VARENUM vt;
38     VARKIND varkind;
39     ULONG offset;
40     BSTR name;
41 } fieldstr;
42
43 typedef struct {
44     const IRecordInfoVtbl *lpVtbl;
45     LONG ref;
46
47     GUID guid;
48     UINT lib_index;
49     WORD n_vars;
50     ULONG size;
51     BSTR name;
52     fieldstr *fields;
53     ITypeInfo *pTypeInfo;
54 } IRecordInfoImpl;
55
56 static HRESULT copy_to_variant(void *src, VARIANT *pvar, enum VARENUM vt)
57 {
58     TRACE("%p %p %d\n", src, pvar, vt);
59
60 #define CASE_COPY(x) \
61     case VT_ ## x: \
62         memcpy(&V_ ## x(pvar), src, sizeof(V_ ## x(pvar))); \
63         break 
64
65     switch(vt) {
66         CASE_COPY(I2);
67         CASE_COPY(I4);
68         CASE_COPY(R4);
69         CASE_COPY(R8);
70         CASE_COPY(CY);
71         CASE_COPY(DATE);
72         CASE_COPY(BSTR);
73         CASE_COPY(ERROR);
74         CASE_COPY(BOOL);
75         CASE_COPY(DECIMAL);
76         CASE_COPY(I1);
77         CASE_COPY(UI1);
78         CASE_COPY(UI2);
79         CASE_COPY(UI4);
80         CASE_COPY(I8);
81         CASE_COPY(UI8);
82         CASE_COPY(INT);
83         CASE_COPY(UINT);
84         CASE_COPY(INT_PTR);
85         CASE_COPY(UINT_PTR);
86     default:
87         FIXME("Not supported type: %d\n", vt);
88         return E_NOTIMPL;
89     };
90 #undef CASE_COPY
91
92     V_VT(pvar) = vt;
93     return S_OK;
94 }
95
96 static HRESULT copy_from_variant(VARIANT *src, void *dest, enum VARENUM vt)
97 {
98     VARIANT var;
99     HRESULT hres;
100
101     TRACE("(%p(%d) %p %d)\n", src, V_VT(src), dest, vt);
102
103     hres = VariantChangeType(&var, src, 0, vt);
104     if(FAILED(hres))
105         return hres;
106
107 #define CASE_COPY(x) \
108     case VT_ ## x: \
109         memcpy(dest, &V_ ## x(&var), sizeof(V_ ## x(&var))); \
110         break
111
112     switch(vt) {
113         CASE_COPY(I2);
114         CASE_COPY(I4);
115         CASE_COPY(R4);
116         CASE_COPY(R8);
117         CASE_COPY(CY);
118         CASE_COPY(DATE);
119         CASE_COPY(BSTR);
120         CASE_COPY(ERROR);
121         CASE_COPY(BOOL);
122         CASE_COPY(DECIMAL);
123         CASE_COPY(I1);
124         CASE_COPY(UI1);
125         CASE_COPY(UI2);
126         CASE_COPY(UI4);
127         CASE_COPY(I8);
128         CASE_COPY(UI8);
129         CASE_COPY(INT);
130         CASE_COPY(UINT);
131         CASE_COPY(INT_PTR);
132         CASE_COPY(UINT_PTR);
133     default:
134         FIXME("Not supported type: %d\n", V_VT(&var));
135         return E_NOTIMPL;
136     };
137 #undef CASE_COPY
138     return S_OK;
139 }
140
141 static HRESULT WINAPI IRecordInfoImpl_QueryInterface(IRecordInfo *iface, REFIID riid,
142                                                 void **ppvObject)
143 {
144     TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
145
146     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IRecordInfo, riid)) {
147        *ppvObject = iface;
148        IRecordInfo_AddRef(iface);
149        return S_OK;
150     }
151
152     FIXME("Not supported interface: %s\n", debugstr_guid(riid));
153     return E_NOINTERFACE;
154 }
155
156 static ULONG WINAPI IRecordInfoImpl_AddRef(IRecordInfo *iface)
157 {
158     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
159     ULONG ref = InterlockedIncrement(&This->ref);
160     TRACE("(%p) -> %d\n", This, ref);
161     return ref;
162 }
163
164 static ULONG WINAPI IRecordInfoImpl_Release(IRecordInfo *iface)
165 {
166     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
167     ULONG ref = InterlockedDecrement(&This->ref);
168
169     TRACE("(%p) -> %d\n", This, ref);
170
171     if(!ref) {
172         int i;
173         for(i=0; i<This->n_vars; i++)
174             SysFreeString(This->fields[i].name);
175         HeapFree(GetProcessHeap(), 0, This->name);
176         HeapFree(GetProcessHeap(), 0, This->fields);
177         ITypeInfo_Release(This->pTypeInfo);
178         HeapFree(GetProcessHeap(), 0, This);
179     }
180     return ref;
181 }
182
183 static HRESULT WINAPI IRecordInfoImpl_RecordInit(IRecordInfo *iface, PVOID pvNew)
184 {
185     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
186     TRACE("(%p)->(%p)\n", This, pvNew);
187
188     if(!pvNew)
189         return E_INVALIDARG;
190
191     memset(pvNew, 0, This->size);
192     return S_OK;
193 }
194
195 static HRESULT WINAPI IRecordInfoImpl_RecordClear(IRecordInfo *iface, PVOID pvExisting)
196 {
197     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
198     int i;
199     PVOID var;
200
201     TRACE("(%p)->(%p)\n", This, pvExisting);
202
203     if(!pvExisting)
204         return E_INVALIDARG;
205
206     for(i=0; i<This->n_vars; i++) {
207         if(This->fields[i].varkind != VAR_PERINSTANCE) {
208             ERR("varkind != VAR_PERINSTANCE\n");
209             continue;
210         }
211         var = ((PBYTE)pvExisting)+This->fields[i].offset;
212         switch(This->fields[i].vt) {
213             case VT_BSTR:
214                SysFreeString(*(BSTR*)var);
215                 *(BSTR*)var = NULL;
216                 break;
217             case VT_I2:
218             case VT_I4:
219             case VT_R4:
220             case VT_R8:
221             case VT_CY:
222             case VT_DATE:
223             case VT_ERROR:
224             case VT_BOOL:
225             case VT_DECIMAL:
226             case VT_I1:
227             case VT_UI1:
228             case VT_UI2:
229             case VT_UI4:
230             case VT_I8:
231             case VT_UI8:
232             case VT_INT:
233             case VT_UINT:
234                 break;
235             case VT_INT_PTR:
236             case VT_UINT_PTR:
237                 *(void**)var = NULL;
238                 break;
239             case VT_SAFEARRAY:
240                 SafeArrayDestroy((SAFEARRAY*)var);
241                 break;
242             default:
243                 FIXME("Not supported vt = %d\n", This->fields[i].vt);
244                 break;
245         }
246     }
247     
248     return S_OK;
249 }
250
251 static HRESULT WINAPI IRecordInfoImpl_RecordCopy(IRecordInfo *iface, PVOID pvExisting,
252                                                 PVOID pvNew)
253 {
254     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
255
256     TRACE("(%p)->(%p %p)\n", This, pvExisting, pvNew);
257     
258     if(!pvExisting || !pvNew)
259         return E_INVALIDARG;
260
261     memcpy(pvExisting, pvNew, This->size);
262     return S_OK;
263 }
264
265 static HRESULT WINAPI IRecordInfoImpl_GetGuid(IRecordInfo *iface, GUID *pguid)
266 {
267     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
268
269     TRACE("(%p)->(%p)\n", This, pguid);
270
271     if(!pguid)
272         return E_INVALIDARG;
273
274     *pguid = This->guid;
275     return S_OK;
276 }
277
278 static HRESULT WINAPI IRecordInfoImpl_GetName(IRecordInfo *iface, BSTR *pbstrName)
279 {
280     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
281
282     TRACE("(%p)->(%p)\n", This, pbstrName);
283
284     if(!pbstrName)
285         return E_INVALIDARG;
286
287     *pbstrName = SysAllocString(This->name);
288     return S_OK;
289 }
290
291 static HRESULT WINAPI IRecordInfoImpl_GetSize(IRecordInfo *iface, ULONG *pcbSize)
292 {
293     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
294     
295     TRACE("(%p)->(%p)\n", This, pcbSize);
296
297     if(!pcbSize)
298         return E_INVALIDARG;
299     
300     *pcbSize = This->size;
301     return S_OK;
302 }
303
304 static HRESULT WINAPI IRecordInfoImpl_GetTypeInfo(IRecordInfo *iface, ITypeInfo **ppTypeInfo)
305 {
306     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
307
308     TRACE("(%p)->(%p)\n", This, ppTypeInfo);
309
310     if(!ppTypeInfo)
311         return E_INVALIDARG;
312
313     ITypeInfo_AddRef(This->pTypeInfo);
314     *ppTypeInfo = This->pTypeInfo;
315
316     return S_OK;
317 }
318
319 static HRESULT WINAPI IRecordInfoImpl_GetField(IRecordInfo *iface, PVOID pvData,
320                                                 LPCOLESTR szFieldName, VARIANT *pvarField)
321 {
322     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
323     int i;
324
325     TRACE("(%p)->(%p %s %p)\n", This, pvData, debugstr_w(szFieldName), pvarField);
326
327     if(!pvData || !szFieldName || !pvarField)
328         return E_INVALIDARG;
329
330     for(i=0; i<This->n_vars; i++)
331         if(!strcmpW(This->fields[i].name, szFieldName))
332             break;
333     if(i == This->n_vars)
334         return TYPE_E_FIELDNOTFOUND;
335     
336     VariantClear(pvarField);
337     return copy_to_variant(((PBYTE)pvData)+This->fields[i].offset, pvarField,
338             This->fields[i].vt);
339 }
340
341 static HRESULT WINAPI IRecordInfoImpl_GetFieldNoCopy(IRecordInfo *iface, PVOID pvData,
342                             LPCOLESTR szFieldName, VARIANT *pvarField, PVOID *ppvDataCArray)
343 {
344     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
345     int i;
346
347     TRACE("(%p)->(%p %s %p %p)\n", This, pvData, debugstr_w(szFieldName), pvarField, ppvDataCArray);
348
349     if(!pvData || !szFieldName || !pvarField)
350         return E_INVALIDARG;
351
352     for(i=0; i<This->n_vars; i++)
353         if(!strcmpW(This->fields[i].name, szFieldName))
354             break;
355     if(i == This->n_vars)
356         return TYPE_E_FIELDNOTFOUND;
357
358     VariantClear(pvarField);
359     V_VT(pvarField) = VT_BYREF|This->fields[i].vt;
360     V_BYREF(pvarField) = ((PBYTE)pvData)+This->fields[i].offset;
361     *ppvDataCArray = NULL;
362     return S_OK;
363 }
364
365 static HRESULT WINAPI IRecordInfoImpl_PutField(IRecordInfo *iface, ULONG wFlags, PVOID pvData,
366                                             LPCOLESTR szFieldName, VARIANT *pvarField)
367 {
368     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
369     int i;
370
371     TRACE("(%p)->(%08x %p %s %p)\n", This, wFlags, pvData, debugstr_w(szFieldName),
372                                     pvarField);
373
374     if(!pvData || !szFieldName || !pvarField
375             || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
376         return E_INVALIDARG;
377
378     if(wFlags == INVOKE_PROPERTYPUTREF) {
379         FIXME("wFlag == INVOKE_PROPERTYPUTREF not supported\n");
380         return E_NOTIMPL;
381     }
382
383     for(i=0; i<This->n_vars; i++)
384         if(!strcmpW(This->fields[i].name, szFieldName))
385             break;
386     if(i == This->n_vars)
387         return TYPE_E_FIELDNOTFOUND;
388
389     return copy_from_variant(pvarField, ((PBYTE)pvData)+This->fields[i].offset,
390             This->fields[i].vt);
391 }
392
393 static HRESULT WINAPI IRecordInfoImpl_PutFieldNoCopy(IRecordInfo *iface, ULONG wFlags,
394                 PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField)
395 {
396     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
397     int i;
398
399     FIXME("(%p)->(%08x %p %s %p) stub\n", This, wFlags, pvData, debugstr_w(szFieldName), pvarField);
400
401     if(!pvData || !szFieldName || !pvarField
402             || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
403         return E_INVALIDARG;
404
405     for(i=0; i<This->n_vars; i++)
406         if(!strcmpW(This->fields[i].name, szFieldName))
407             break;
408     if(i == This->n_vars)
409         return TYPE_E_FIELDNOTFOUND;
410
411     return E_NOTIMPL;
412 }
413
414 static HRESULT WINAPI IRecordInfoImpl_GetFieldNames(IRecordInfo *iface, ULONG *pcNames,
415                                                 BSTR *rgBstrNames)
416 {
417     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
418     ULONG n = This->n_vars, i;
419
420     TRACE("(%p)->(%p %p)\n", This, pcNames, rgBstrNames);
421
422     if(!pcNames)
423         return E_INVALIDARG;
424
425     if(*pcNames < n)
426         n =  *pcNames;
427
428     if(rgBstrNames) {
429         for(i=0; i<n; i++)
430             rgBstrNames[i] = SysAllocString(This->fields[i].name);
431     }
432     
433     *pcNames = n;
434     return S_OK;
435 }
436
437 static BOOL WINAPI IRecordInfoImpl_IsMatchingType(IRecordInfo *iface, IRecordInfo *pRecordInfo)
438 {
439     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
440
441     FIXME("(%p)->(%p) stub\n", This, pRecordInfo);
442
443     return FALSE;
444 }
445
446 static PVOID WINAPI IRecordInfoImpl_RecordCreate(IRecordInfo *iface)
447 {
448     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
449
450     TRACE("(%p)\n", This);
451
452     return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->size);
453 }
454
455 static HRESULT WINAPI IRecordInfoImpl_RecordCreateCopy(IRecordInfo *iface, PVOID pvSource,
456                                                     PVOID *ppvDest)
457 {
458     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
459
460     TRACE("(%p)->(%p %p)\n", This, pvSource, ppvDest);
461
462     if(!pvSource || !ppvDest)
463         return E_INVALIDARG;
464     
465     *ppvDest = IRecordInfo_RecordCreate(iface);
466     return IRecordInfo_RecordCopy(iface, pvSource, *ppvDest);
467 }
468
469 static HRESULT WINAPI IRecordInfoImpl_RecordDestroy(IRecordInfo *iface, PVOID pvRecord)
470 {
471     IRecordInfoImpl *This = (IRecordInfoImpl*)iface;
472     HRESULT hres;
473
474     TRACE("(%p)->(%p)\n", This, pvRecord);
475
476     hres = IRecordInfo_RecordClear(iface, pvRecord);
477     if(FAILED(hres))
478         return hres;
479
480     if(!HeapFree(GetProcessHeap(), 0, pvRecord))
481         return E_INVALIDARG;
482
483     return S_OK;
484 }
485
486 static const IRecordInfoVtbl IRecordInfoImplVtbl = {
487     IRecordInfoImpl_QueryInterface,
488     IRecordInfoImpl_AddRef,
489     IRecordInfoImpl_Release,
490     IRecordInfoImpl_RecordInit,
491     IRecordInfoImpl_RecordClear,
492     IRecordInfoImpl_RecordCopy,
493     IRecordInfoImpl_GetGuid,
494     IRecordInfoImpl_GetName,
495     IRecordInfoImpl_GetSize,
496     IRecordInfoImpl_GetTypeInfo,
497     IRecordInfoImpl_GetField,
498     IRecordInfoImpl_GetFieldNoCopy,
499     IRecordInfoImpl_PutField,
500     IRecordInfoImpl_PutFieldNoCopy,
501     IRecordInfoImpl_GetFieldNames,
502     IRecordInfoImpl_IsMatchingType,
503     IRecordInfoImpl_RecordCreate,
504     IRecordInfoImpl_RecordCreateCopy,
505     IRecordInfoImpl_RecordDestroy
506 };
507
508 /******************************************************************************
509  *      GetRecordInfoFromGuids  [OLEAUT32.322]
510  *
511  * RETURNS
512  *  Success: S_OK
513  *  Failure: E_INVALIDARG, if any argument is invalid.
514  */
515 HRESULT WINAPI GetRecordInfoFromGuids(REFGUID rGuidTypeLib, ULONG uVerMajor,
516                         ULONG uVerMinor, LCID lcid, REFGUID rGuidTypeInfo, IRecordInfo** ppRecInfo)
517 {
518     ITypeInfo *pTypeInfo;
519     ITypeLib *pTypeLib;
520     HRESULT hres;
521     
522     TRACE("(%p,%d,%d,%d,%p,%p)\n", rGuidTypeLib, uVerMajor, uVerMinor,
523             lcid, rGuidTypeInfo, ppRecInfo);
524
525     hres = LoadRegTypeLib(rGuidTypeLib, uVerMajor, uVerMinor, lcid, &pTypeLib);
526     if(FAILED(hres)) {
527         WARN("LoadRegTypeLib failed!\n");
528         return hres;
529     }
530
531     hres = ITypeLib_GetTypeInfoOfGuid(pTypeLib, rGuidTypeInfo, &pTypeInfo);
532     ITypeLib_Release(pTypeLib);
533     if(FAILED(hres)) {
534         WARN("GetTypeInfoOfGuid failed!\n");
535         return hres;
536     }
537
538     hres = GetRecordInfoFromTypeInfo(pTypeInfo, ppRecInfo);
539     ITypeInfo_Release(pTypeInfo);
540     return hres;
541 }
542
543 /******************************************************************************
544  *      GetRecordInfoFromTypeInfo [OLEAUT32.332]
545  */
546 HRESULT WINAPI GetRecordInfoFromTypeInfo(ITypeInfo* pTI, IRecordInfo** ppRecInfo) {
547     HRESULT hres;
548     TYPEATTR *typeattr;
549     IRecordInfoImpl *ret;
550     ITypeInfo *pTypeInfo;
551     int i;
552     GUID guid;
553
554     TRACE("(%p %p)\n", pTI, ppRecInfo);
555
556     if(!pTI || !ppRecInfo)
557         return E_INVALIDARG;
558     
559     hres = ITypeInfo_GetTypeAttr(pTI, &typeattr);
560     if(FAILED(hres) || !typeattr) {
561         WARN("GetTypeAttr failed: %08x\n", hres);
562         return hres;
563     }
564
565     if(typeattr->typekind == TKIND_ALIAS) {
566         hres = ITypeInfo_GetRefTypeInfo(pTI, typeattr->tdescAlias.u.hreftype, &pTypeInfo);
567         guid = typeattr->guid;
568         ITypeInfo_ReleaseTypeAttr(pTI, typeattr);
569         if(FAILED(hres)) {
570             WARN("GetRefTypeInfo failed: %08x\n", hres);
571             return hres;
572         }
573         ITypeInfo_GetTypeAttr(pTypeInfo, &typeattr);
574     }else  {
575         pTypeInfo = pTI;
576         ITypeInfo_AddRef(pTypeInfo);
577         guid = typeattr->guid;
578     }
579
580     if(typeattr->typekind != TKIND_RECORD) {
581         WARN("typekind != TKIND_RECORD\n");
582         ITypeInfo_ReleaseTypeAttr(pTypeInfo, typeattr);
583         ITypeInfo_Release(pTypeInfo);
584         return E_INVALIDARG;
585     }
586
587     ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret));
588     ret->lpVtbl = &IRecordInfoImplVtbl;
589     ret->ref = 1;
590     ret->pTypeInfo = pTypeInfo;
591     ret->n_vars = typeattr->cVars;
592     ret->size = typeattr->cbSizeInstance;
593     ITypeInfo_ReleaseTypeAttr(pTypeInfo, typeattr);
594
595     ret->guid = guid;
596
597     /* NOTE: Windows implementation calls ITypeInfo::GetCantainingTypeLib and
598      *       ITypeLib::GetLibAttr, but we currently don't need this.
599      */
600
601     hres = ITypeInfo_GetDocumentation(pTypeInfo, MEMBERID_NIL, &ret->name, NULL, NULL, NULL);
602     if(FAILED(hres)) {
603         WARN("ITypeInfo::GetDocumentation failed\n");
604         ret->name = NULL;
605     }
606
607     ret->fields = HeapAlloc(GetProcessHeap(), 0, ret->n_vars*sizeof(VARDESC));
608     for(i = 0; i<ret->n_vars; i++) {
609         VARDESC *vardesc;
610         hres = ITypeInfo_GetVarDesc(pTypeInfo, i, &vardesc);
611         if(FAILED(hres)) {
612             WARN("GetVarDesc failed\n");
613             continue;
614         }
615         ret->fields[i].vt = vardesc->elemdescVar.tdesc.vt;
616         ret->fields[i].varkind = vardesc->varkind;
617         ret->fields[i].offset = vardesc->u.oInst;
618         hres = ITypeInfo_GetDocumentation(pTypeInfo, vardesc->memid, &ret->fields[i].name,
619                 NULL, NULL, NULL);
620         if(FAILED(hres))
621             WARN("GetDocumentation failed: %08x\n", hres);
622         ITypeInfo_ReleaseVarDesc(pTypeInfo, vardesc);
623     }
624         
625     *ppRecInfo = (IRecordInfo*)ret;
626
627     return S_OK;
628 }