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