xref: /reactos/dll/win32/oleaut32/recinfo.c (revision f986527d)
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 
24 #include "windef.h"
25 #include "winbase.h"
26 #include "objbase.h"
27 #include "oaidl.h"
28 #include "oleauto.h"
29 #include "variant.h"
30 
31 #include "wine/debug.h"
32 
33 WINE_DEFAULT_DEBUG_CHANNEL(ole);
34 
35 typedef struct {
36     enum VARENUM vt;
37     VARKIND varkind;
38     ULONG offset;
39     BSTR name;
40 } fieldstr;
41 
42 typedef struct {
43     IRecordInfo IRecordInfo_iface;
44     LONG ref;
45 
46     GUID guid;
47     UINT lib_index;
48     WORD n_vars;
49     ULONG size;
50     BSTR name;
51     fieldstr *fields;
52     ITypeInfo *pTypeInfo;
53 } IRecordInfoImpl;
54 
55 static inline IRecordInfoImpl *impl_from_IRecordInfo(IRecordInfo *iface)
56 {
57     return CONTAINING_RECORD(iface, IRecordInfoImpl, IRecordInfo_iface);
58 }
59 
60 static HRESULT copy_to_variant(void *src, VARIANT *pvar, enum VARENUM vt)
61 {
62     TRACE("%p %p %d\n", src, pvar, vt);
63 
64 #define CASE_COPY(x) \
65     case VT_ ## x: \
66         memcpy(&V_ ## x(pvar), src, sizeof(V_ ## x(pvar))); \
67         break
68 
69     switch(vt) {
70         CASE_COPY(I2);
71         CASE_COPY(I4);
72         CASE_COPY(R4);
73         CASE_COPY(R8);
74         CASE_COPY(CY);
75         CASE_COPY(DATE);
76         CASE_COPY(BSTR);
77         CASE_COPY(ERROR);
78         CASE_COPY(BOOL);
79         CASE_COPY(DECIMAL);
80         CASE_COPY(I1);
81         CASE_COPY(UI1);
82         CASE_COPY(UI2);
83         CASE_COPY(UI4);
84         CASE_COPY(I8);
85         CASE_COPY(UI8);
86         CASE_COPY(INT);
87         CASE_COPY(UINT);
88         CASE_COPY(INT_PTR);
89         CASE_COPY(UINT_PTR);
90     default:
91         FIXME("Not supported type: %d\n", vt);
92         return E_NOTIMPL;
93     };
94 #undef CASE_COPY
95 
96     V_VT(pvar) = vt;
97     return S_OK;
98 }
99 
100 static HRESULT copy_from_variant(VARIANT *src, void *dest, enum VARENUM vt)
101 {
102     VARIANT var;
103     HRESULT hres;
104 
105     TRACE("(%p(%d) %p %d)\n", src, V_VT(src), dest, vt);
106 
107     hres = VariantChangeType(&var, src, 0, vt);
108     if(FAILED(hres))
109         return hres;
110 
111 #define CASE_COPY(x) \
112     case VT_ ## x: \
113         memcpy(dest, &V_ ## x(&var), sizeof(V_ ## x(&var))); \
114         break
115 
116     switch(vt) {
117         CASE_COPY(I2);
118         CASE_COPY(I4);
119         CASE_COPY(R4);
120         CASE_COPY(R8);
121         CASE_COPY(CY);
122         CASE_COPY(DATE);
123         CASE_COPY(BSTR);
124         CASE_COPY(ERROR);
125         CASE_COPY(BOOL);
126         CASE_COPY(DECIMAL);
127         CASE_COPY(I1);
128         CASE_COPY(UI1);
129         CASE_COPY(UI2);
130         CASE_COPY(UI4);
131         CASE_COPY(I8);
132         CASE_COPY(UI8);
133         CASE_COPY(INT);
134         CASE_COPY(UINT);
135         CASE_COPY(INT_PTR);
136         CASE_COPY(UINT_PTR);
137     default:
138         FIXME("Not supported type: %d\n", V_VT(&var));
139         return E_NOTIMPL;
140     };
141 #undef CASE_COPY
142     return S_OK;
143 }
144 
145 static HRESULT WINAPI IRecordInfoImpl_QueryInterface(IRecordInfo *iface, REFIID riid,
146                                                 void **ppvObject)
147 {
148     TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
149 
150     *ppvObject = NULL;
151 
152     if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IRecordInfo, riid)) {
153        *ppvObject = iface;
154        IRecordInfo_AddRef(iface);
155        return S_OK;
156     }
157 
158     FIXME("Not supported interface: %s\n", debugstr_guid(riid));
159     return E_NOINTERFACE;
160 }
161 
162 static ULONG WINAPI IRecordInfoImpl_AddRef(IRecordInfo *iface)
163 {
164     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
165     ULONG ref = InterlockedIncrement(&This->ref);
166     TRACE("(%p) -> %d\n", This, ref);
167     return ref;
168 }
169 
170 static ULONG WINAPI IRecordInfoImpl_Release(IRecordInfo *iface)
171 {
172     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
173     ULONG ref = InterlockedDecrement(&This->ref);
174 
175     TRACE("(%p) -> %d\n", This, ref);
176 
177     if(!ref) {
178         int i;
179         for(i=0; i<This->n_vars; i++)
180             SysFreeString(This->fields[i].name);
181         SysFreeString(This->name);
182         HeapFree(GetProcessHeap(), 0, This->fields);
183         ITypeInfo_Release(This->pTypeInfo);
184         HeapFree(GetProcessHeap(), 0, This);
185     }
186     return ref;
187 }
188 
189 static HRESULT WINAPI IRecordInfoImpl_RecordInit(IRecordInfo *iface, PVOID pvNew)
190 {
191     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
192     TRACE("(%p)->(%p)\n", This, pvNew);
193 
194     if(!pvNew)
195         return E_INVALIDARG;
196 
197     memset(pvNew, 0, This->size);
198     return S_OK;
199 }
200 
201 static HRESULT WINAPI IRecordInfoImpl_RecordClear(IRecordInfo *iface, PVOID pvExisting)
202 {
203     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
204     int i;
205     PVOID var;
206 
207     TRACE("(%p)->(%p)\n", This, pvExisting);
208 
209     if(!pvExisting)
210         return E_INVALIDARG;
211 
212     for(i=0; i<This->n_vars; i++) {
213         if(This->fields[i].varkind != VAR_PERINSTANCE) {
214             ERR("varkind != VAR_PERINSTANCE\n");
215             continue;
216         }
217         var = ((PBYTE)pvExisting)+This->fields[i].offset;
218         switch(This->fields[i].vt) {
219             case VT_BSTR:
220                SysFreeString(*(BSTR*)var);
221                 *(BSTR*)var = NULL;
222                 break;
223             case VT_I2:
224             case VT_I4:
225             case VT_R4:
226             case VT_R8:
227             case VT_CY:
228             case VT_DATE:
229             case VT_ERROR:
230             case VT_BOOL:
231             case VT_DECIMAL:
232             case VT_I1:
233             case VT_UI1:
234             case VT_UI2:
235             case VT_UI4:
236             case VT_I8:
237             case VT_UI8:
238             case VT_INT:
239             case VT_UINT:
240             case VT_HRESULT:
241                 break;
242             case VT_INT_PTR:
243             case VT_UINT_PTR:
244                 *(void**)var = NULL;
245                 break;
246             case VT_SAFEARRAY:
247                 SafeArrayDestroy(var);
248                 break;
249             case VT_UNKNOWN:
250             case VT_DISPATCH:
251             {
252                 IUnknown *unk = *(IUnknown**)var;
253                 if (unk)
254                     IUnknown_Release(unk);
255                 *(void**)var = NULL;
256                 break;
257             }
258             default:
259                 FIXME("Not supported vt = %d\n", This->fields[i].vt);
260                 break;
261         }
262     }
263 
264     return S_OK;
265 }
266 
267 static HRESULT WINAPI IRecordInfoImpl_RecordCopy(IRecordInfo *iface, void *src_rec, void *dest_rec)
268 {
269     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
270     HRESULT hr = S_OK;
271     int i;
272 
273     TRACE("(%p)->(%p %p)\n", This, src_rec, dest_rec);
274 
275     if(!src_rec || !dest_rec)
276         return E_INVALIDARG;
277 
278     /* release already stored data */
279     IRecordInfo_RecordClear(iface, dest_rec);
280 
281     for (i = 0; i < This->n_vars; i++)
282     {
283         void *src, *dest;
284 
285         if (This->fields[i].varkind != VAR_PERINSTANCE) {
286             ERR("varkind != VAR_PERINSTANCE\n");
287             continue;
288         }
289 
290         src  = ((BYTE*)src_rec) + This->fields[i].offset;
291         dest = ((BYTE*)dest_rec) + This->fields[i].offset;
292         switch (This->fields[i].vt)
293         {
294             case VT_BSTR:
295             {
296                 BSTR src_str = *(BSTR*)src;
297 
298                 if (src_str)
299                 {
300                     BSTR str = SysAllocString(*(BSTR*)src);
301                     if (!str) hr = E_OUTOFMEMORY;
302 
303                     *(BSTR*)dest = str;
304                 }
305                 else
306                     *(BSTR*)dest = NULL;
307                 break;
308             }
309             case VT_UNKNOWN:
310             case VT_DISPATCH:
311             {
312                 IUnknown *unk = *(IUnknown**)src;
313                 *(IUnknown**)dest = unk;
314                 if (unk) IUnknown_AddRef(unk);
315                 break;
316             }
317             case VT_SAFEARRAY:
318                 hr = SafeArrayCopy(src, dest);
319                 break;
320             default:
321             {
322                 /* copy directly for types that don't need deep copy */
323                 int len = get_type_size(NULL, This->fields[i].vt);
324                 memcpy(dest, src, len);
325                 break;
326             }
327         }
328 
329         if (FAILED(hr)) break;
330     }
331 
332     if (FAILED(hr))
333         IRecordInfo_RecordClear(iface, dest_rec);
334 
335     return hr;
336 }
337 
338 static HRESULT WINAPI IRecordInfoImpl_GetGuid(IRecordInfo *iface, GUID *pguid)
339 {
340     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
341 
342     TRACE("(%p)->(%p)\n", This, pguid);
343 
344     if(!pguid)
345         return E_INVALIDARG;
346 
347     *pguid = This->guid;
348     return S_OK;
349 }
350 
351 static HRESULT WINAPI IRecordInfoImpl_GetName(IRecordInfo *iface, BSTR *pbstrName)
352 {
353     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
354 
355     TRACE("(%p)->(%p)\n", This, pbstrName);
356 
357     if(!pbstrName)
358         return E_INVALIDARG;
359 
360     *pbstrName = SysAllocString(This->name);
361     return S_OK;
362 }
363 
364 static HRESULT WINAPI IRecordInfoImpl_GetSize(IRecordInfo *iface, ULONG *pcbSize)
365 {
366     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
367 
368     TRACE("(%p)->(%p)\n", This, pcbSize);
369 
370     if(!pcbSize)
371         return E_INVALIDARG;
372 
373     *pcbSize = This->size;
374     return S_OK;
375 }
376 
377 static HRESULT WINAPI IRecordInfoImpl_GetTypeInfo(IRecordInfo *iface, ITypeInfo **ppTypeInfo)
378 {
379     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
380 
381     TRACE("(%p)->(%p)\n", This, ppTypeInfo);
382 
383     if(!ppTypeInfo)
384         return E_INVALIDARG;
385 
386     ITypeInfo_AddRef(This->pTypeInfo);
387     *ppTypeInfo = This->pTypeInfo;
388 
389     return S_OK;
390 }
391 
392 static HRESULT WINAPI IRecordInfoImpl_GetField(IRecordInfo *iface, PVOID pvData,
393                                                 LPCOLESTR szFieldName, VARIANT *pvarField)
394 {
395     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
396     int i;
397 
398     TRACE("(%p)->(%p %s %p)\n", This, pvData, debugstr_w(szFieldName), pvarField);
399 
400     if(!pvData || !szFieldName || !pvarField)
401         return E_INVALIDARG;
402 
403     for(i=0; i<This->n_vars; i++)
404         if(!wcscmp(This->fields[i].name, szFieldName))
405             break;
406     if(i == This->n_vars)
407         return TYPE_E_FIELDNOTFOUND;
408 
409     VariantClear(pvarField);
410     return copy_to_variant(((PBYTE)pvData)+This->fields[i].offset, pvarField,
411             This->fields[i].vt);
412 }
413 
414 static HRESULT WINAPI IRecordInfoImpl_GetFieldNoCopy(IRecordInfo *iface, PVOID pvData,
415                             LPCOLESTR szFieldName, VARIANT *pvarField, PVOID *ppvDataCArray)
416 {
417     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
418     int i;
419 
420     TRACE("(%p)->(%p %s %p %p)\n", This, pvData, debugstr_w(szFieldName), pvarField, ppvDataCArray);
421 
422     if(!pvData || !szFieldName || !pvarField)
423         return E_INVALIDARG;
424 
425     for(i=0; i<This->n_vars; i++)
426         if(!wcscmp(This->fields[i].name, szFieldName))
427             break;
428     if(i == This->n_vars)
429         return TYPE_E_FIELDNOTFOUND;
430 
431     VariantClear(pvarField);
432     V_VT(pvarField) = VT_BYREF|This->fields[i].vt;
433     V_BYREF(pvarField) = ((PBYTE)pvData)+This->fields[i].offset;
434     *ppvDataCArray = NULL;
435     return S_OK;
436 }
437 
438 static HRESULT WINAPI IRecordInfoImpl_PutField(IRecordInfo *iface, ULONG wFlags, PVOID pvData,
439                                             LPCOLESTR szFieldName, VARIANT *pvarField)
440 {
441     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
442     int i;
443 
444     TRACE("(%p)->(%08x %p %s %p)\n", This, wFlags, pvData, debugstr_w(szFieldName),
445                                     pvarField);
446 
447     if(!pvData || !szFieldName || !pvarField
448             || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
449         return E_INVALIDARG;
450 
451     if(wFlags == INVOKE_PROPERTYPUTREF) {
452         FIXME("wFlag == INVOKE_PROPERTYPUTREF not supported\n");
453         return E_NOTIMPL;
454     }
455 
456     for(i=0; i<This->n_vars; i++)
457         if(!wcscmp(This->fields[i].name, szFieldName))
458             break;
459     if(i == This->n_vars)
460         return TYPE_E_FIELDNOTFOUND;
461 
462     return copy_from_variant(pvarField, ((PBYTE)pvData)+This->fields[i].offset,
463             This->fields[i].vt);
464 }
465 
466 static HRESULT WINAPI IRecordInfoImpl_PutFieldNoCopy(IRecordInfo *iface, ULONG wFlags,
467                 PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField)
468 {
469     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
470     int i;
471 
472     FIXME("(%p)->(%08x %p %s %p) stub\n", This, wFlags, pvData, debugstr_w(szFieldName), pvarField);
473 
474     if(!pvData || !szFieldName || !pvarField
475             || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
476         return E_INVALIDARG;
477 
478     for(i=0; i<This->n_vars; i++)
479         if(!wcscmp(This->fields[i].name, szFieldName))
480             break;
481     if(i == This->n_vars)
482         return TYPE_E_FIELDNOTFOUND;
483 
484     return E_NOTIMPL;
485 }
486 
487 static HRESULT WINAPI IRecordInfoImpl_GetFieldNames(IRecordInfo *iface, ULONG *pcNames,
488                                                 BSTR *rgBstrNames)
489 {
490     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
491     ULONG n = This->n_vars, i;
492 
493     TRACE("(%p)->(%p %p)\n", This, pcNames, rgBstrNames);
494 
495     if(!pcNames)
496         return E_INVALIDARG;
497 
498     if(*pcNames < n)
499         n =  *pcNames;
500 
501     if(rgBstrNames) {
502         for(i=0; i<n; i++)
503             rgBstrNames[i] = SysAllocString(This->fields[i].name);
504     }
505 
506     *pcNames = n;
507     return S_OK;
508 }
509 
510 static BOOL WINAPI IRecordInfoImpl_IsMatchingType(IRecordInfo *iface, IRecordInfo *info2)
511 {
512     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
513     GUID guid2;
514 
515     TRACE( "(%p)->(%p)\n", This, info2 );
516 
517     IRecordInfo_GetGuid( info2, &guid2 );
518     return IsEqualGUID( &This->guid, &guid2 );
519 }
520 
521 static PVOID WINAPI IRecordInfoImpl_RecordCreate(IRecordInfo *iface)
522 {
523     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
524     void *record;
525 
526     TRACE("(%p)\n", This);
527 
528     record = HeapAlloc(GetProcessHeap(), 0, This->size);
529     IRecordInfo_RecordInit(iface, record);
530     TRACE("created record at %p\n", record);
531     return record;
532 }
533 
534 static HRESULT WINAPI IRecordInfoImpl_RecordCreateCopy(IRecordInfo *iface, PVOID pvSource,
535                                                     PVOID *ppvDest)
536 {
537     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
538 
539     TRACE("(%p)->(%p %p)\n", This, pvSource, ppvDest);
540 
541     if(!pvSource || !ppvDest)
542         return E_INVALIDARG;
543 
544     *ppvDest = IRecordInfo_RecordCreate(iface);
545     return IRecordInfo_RecordCopy(iface, pvSource, *ppvDest);
546 }
547 
548 static HRESULT WINAPI IRecordInfoImpl_RecordDestroy(IRecordInfo *iface, PVOID pvRecord)
549 {
550     IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
551     HRESULT hres;
552 
553     TRACE("(%p)->(%p)\n", This, pvRecord);
554 
555     hres = IRecordInfo_RecordClear(iface, pvRecord);
556     if(FAILED(hres))
557         return hres;
558 
559     if(!HeapFree(GetProcessHeap(), 0, pvRecord))
560         return E_INVALIDARG;
561 
562     return S_OK;
563 }
564 
565 static const IRecordInfoVtbl IRecordInfoImplVtbl = {
566     IRecordInfoImpl_QueryInterface,
567     IRecordInfoImpl_AddRef,
568     IRecordInfoImpl_Release,
569     IRecordInfoImpl_RecordInit,
570     IRecordInfoImpl_RecordClear,
571     IRecordInfoImpl_RecordCopy,
572     IRecordInfoImpl_GetGuid,
573     IRecordInfoImpl_GetName,
574     IRecordInfoImpl_GetSize,
575     IRecordInfoImpl_GetTypeInfo,
576     IRecordInfoImpl_GetField,
577     IRecordInfoImpl_GetFieldNoCopy,
578     IRecordInfoImpl_PutField,
579     IRecordInfoImpl_PutFieldNoCopy,
580     IRecordInfoImpl_GetFieldNames,
581     IRecordInfoImpl_IsMatchingType,
582     IRecordInfoImpl_RecordCreate,
583     IRecordInfoImpl_RecordCreateCopy,
584     IRecordInfoImpl_RecordDestroy
585 };
586 
587 /******************************************************************************
588  *      GetRecordInfoFromGuids  [OLEAUT32.322]
589  *
590  * RETURNS
591  *  Success: S_OK
592  *  Failure: E_INVALIDARG, if any argument is invalid.
593  */
594 HRESULT WINAPI GetRecordInfoFromGuids(REFGUID rGuidTypeLib, ULONG uVerMajor,
595                         ULONG uVerMinor, LCID lcid, REFGUID rGuidTypeInfo, IRecordInfo** ppRecInfo)
596 {
597     ITypeInfo *pTypeInfo;
598     ITypeLib *pTypeLib;
599     HRESULT hres;
600 
601     TRACE("(%p,%d,%d,%d,%s,%p)\n", rGuidTypeLib, uVerMajor, uVerMinor,
602             lcid, debugstr_guid(rGuidTypeInfo), ppRecInfo);
603 
604     hres = LoadRegTypeLib(rGuidTypeLib, uVerMajor, uVerMinor, lcid, &pTypeLib);
605     if(FAILED(hres)) {
606         WARN("LoadRegTypeLib failed!\n");
607         return hres;
608     }
609 
610     hres = ITypeLib_GetTypeInfoOfGuid(pTypeLib, rGuidTypeInfo, &pTypeInfo);
611     ITypeLib_Release(pTypeLib);
612     if(FAILED(hres)) {
613         WARN("GetTypeInfoOfGuid failed!\n");
614         return hres;
615     }
616 
617     hres = GetRecordInfoFromTypeInfo(pTypeInfo, ppRecInfo);
618     ITypeInfo_Release(pTypeInfo);
619     return hres;
620 }
621 
622 /******************************************************************************
623  *      GetRecordInfoFromTypeInfo [OLEAUT32.332]
624  */
625 HRESULT WINAPI GetRecordInfoFromTypeInfo(ITypeInfo* pTI, IRecordInfo** ppRecInfo) {
626     HRESULT hres;
627     TYPEATTR *typeattr;
628     IRecordInfoImpl *ret;
629     ITypeInfo *pTypeInfo;
630     int i;
631     GUID guid;
632 
633     TRACE("(%p %p)\n", pTI, ppRecInfo);
634 
635     if(!pTI || !ppRecInfo)
636         return E_INVALIDARG;
637 
638     hres = ITypeInfo_GetTypeAttr(pTI, &typeattr);
639     if(FAILED(hres) || !typeattr) {
640         WARN("GetTypeAttr failed: %08x\n", hres);
641         return hres;
642     }
643 
644     if(typeattr->typekind == TKIND_ALIAS) {
645         hres = ITypeInfo_GetRefTypeInfo(pTI, typeattr->tdescAlias.u.hreftype, &pTypeInfo);
646         guid = typeattr->guid;
647         ITypeInfo_ReleaseTypeAttr(pTI, typeattr);
648         if(FAILED(hres)) {
649             WARN("GetRefTypeInfo failed: %08x\n", hres);
650             return hres;
651         }
652         hres = ITypeInfo_GetTypeAttr(pTypeInfo, &typeattr);
653         if(FAILED(hres)) {
654             ITypeInfo_Release(pTypeInfo);
655             WARN("GetTypeAttr failed for referenced type: %08x\n", hres);
656             return hres;
657         }
658     }else  {
659         pTypeInfo = pTI;
660         ITypeInfo_AddRef(pTypeInfo);
661         guid = typeattr->guid;
662     }
663 
664     if(typeattr->typekind != TKIND_RECORD) {
665         WARN("typekind != TKIND_RECORD\n");
666         ITypeInfo_ReleaseTypeAttr(pTypeInfo, typeattr);
667         ITypeInfo_Release(pTypeInfo);
668         return E_INVALIDARG;
669     }
670 
671     ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret));
672     ret->IRecordInfo_iface.lpVtbl = &IRecordInfoImplVtbl;
673     ret->ref = 1;
674     ret->pTypeInfo = pTypeInfo;
675     ret->n_vars = typeattr->cVars;
676     ret->size = typeattr->cbSizeInstance;
677     ITypeInfo_ReleaseTypeAttr(pTypeInfo, typeattr);
678 
679     ret->guid = guid;
680 
681     /* NOTE: Windows implementation calls ITypeInfo::GetCantainingTypeLib and
682      *       ITypeLib::GetLibAttr, but we currently don't need this.
683      */
684 
685     hres = ITypeInfo_GetDocumentation(pTypeInfo, MEMBERID_NIL, &ret->name, NULL, NULL, NULL);
686     if(FAILED(hres)) {
687         WARN("ITypeInfo::GetDocumentation failed\n");
688         ret->name = NULL;
689     }
690 
691     ret->fields = HeapAlloc(GetProcessHeap(), 0, ret->n_vars*sizeof(fieldstr));
692     for(i = 0; i<ret->n_vars; i++) {
693         VARDESC *vardesc;
694         hres = ITypeInfo_GetVarDesc(pTypeInfo, i, &vardesc);
695         if(FAILED(hres)) {
696             WARN("GetVarDesc failed\n");
697             continue;
698         }
699         ret->fields[i].vt = vardesc->elemdescVar.tdesc.vt;
700         ret->fields[i].varkind = vardesc->varkind;
701         ret->fields[i].offset = vardesc->u.oInst;
702         hres = ITypeInfo_GetDocumentation(pTypeInfo, vardesc->memid, &ret->fields[i].name,
703                 NULL, NULL, NULL);
704         if(FAILED(hres))
705             WARN("GetDocumentation failed: %08x\n", hres);
706         TRACE("field=%s, offset=%d\n", debugstr_w(ret->fields[i].name), ret->fields[i].offset);
707         ITypeInfo_ReleaseVarDesc(pTypeInfo, vardesc);
708     }
709 
710     *ppRecInfo = &ret->IRecordInfo_iface;
711 
712     return S_OK;
713 }
714