winspool/tests: Add tests for GetFormA().
[wine.git] / dlls / oleaut32 / recinfo.c
blob44d46595e85a50ccb44e0339db3b91e75d922522
1 /*
2 * Copyright 2005 Jacek Caban
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.
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.
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
19 #include <stdarg.h>
21 #define COBJMACROS
22 #define NONAMELESSUNION
24 #include "windef.h"
25 #include "winbase.h"
26 #include "objbase.h"
27 #include "oaidl.h"
28 #include "oleauto.h"
29 #include "variant.h"
31 #include "wine/debug.h"
33 WINE_DEFAULT_DEBUG_CHANNEL(ole);
35 typedef struct {
36 enum VARENUM vt;
37 VARKIND varkind;
38 ULONG offset;
39 BSTR name;
40 } fieldstr;
42 typedef struct {
43 IRecordInfo IRecordInfo_iface;
44 LONG ref;
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;
55 static inline IRecordInfoImpl *impl_from_IRecordInfo(IRecordInfo *iface)
57 return CONTAINING_RECORD(iface, IRecordInfoImpl, IRecordInfo_iface);
60 static HRESULT copy_to_variant(void *src, VARIANT *pvar, enum VARENUM vt)
62 TRACE("%p %p %d\n", src, pvar, vt);
64 #define CASE_COPY(x) \
65 case VT_ ## x: \
66 memcpy(&V_ ## x(pvar), src, sizeof(V_ ## x(pvar))); \
67 break
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;
94 #undef CASE_COPY
96 V_VT(pvar) = vt;
97 return S_OK;
100 static HRESULT copy_from_variant(VARIANT *src, void *dest, enum VARENUM vt)
102 VARIANT var;
103 HRESULT hres;
105 TRACE("(%p(%d) %p %d)\n", src, V_VT(src), dest, vt);
107 hres = VariantChangeType(&var, src, 0, vt);
108 if(FAILED(hres))
109 return hres;
111 #define CASE_COPY(x) \
112 case VT_ ## x: \
113 memcpy(dest, &V_ ## x(&var), sizeof(V_ ## x(&var))); \
114 break
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;
141 #undef CASE_COPY
142 return S_OK;
145 static HRESULT WINAPI IRecordInfoImpl_QueryInterface(IRecordInfo *iface, REFIID riid,
146 void **ppvObject)
148 TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
150 *ppvObject = NULL;
152 if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IRecordInfo, riid)) {
153 *ppvObject = iface;
154 IRecordInfo_AddRef(iface);
155 return S_OK;
158 FIXME("Not supported interface: %s\n", debugstr_guid(riid));
159 return E_NOINTERFACE;
162 static ULONG WINAPI IRecordInfoImpl_AddRef(IRecordInfo *iface)
164 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
165 ULONG ref = InterlockedIncrement(&This->ref);
166 TRACE("(%p) -> %d\n", This, ref);
167 return ref;
170 static ULONG WINAPI IRecordInfoImpl_Release(IRecordInfo *iface)
172 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
173 ULONG ref = InterlockedDecrement(&This->ref);
175 TRACE("(%p) -> %d\n", This, ref);
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);
186 return ref;
189 static HRESULT WINAPI IRecordInfoImpl_RecordInit(IRecordInfo *iface, PVOID pvNew)
191 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
192 TRACE("(%p)->(%p)\n", This, pvNew);
194 if(!pvNew)
195 return E_INVALIDARG;
197 memset(pvNew, 0, This->size);
198 return S_OK;
201 static HRESULT WINAPI IRecordInfoImpl_RecordClear(IRecordInfo *iface, PVOID pvExisting)
203 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
204 int i;
205 PVOID var;
207 TRACE("(%p)->(%p)\n", This, pvExisting);
209 if(!pvExisting)
210 return E_INVALIDARG;
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;
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:
252 IUnknown *unk = *(IUnknown**)var;
253 if (unk)
254 IUnknown_Release(unk);
255 *(void**)var = NULL;
256 break;
258 default:
259 FIXME("Not supported vt = %d\n", This->fields[i].vt);
260 break;
264 return S_OK;
267 static HRESULT WINAPI IRecordInfoImpl_RecordCopy(IRecordInfo *iface, void *src_rec, void *dest_rec)
269 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
270 HRESULT hr = S_OK;
271 int i;
273 TRACE("(%p)->(%p %p)\n", This, src_rec, dest_rec);
275 if(!src_rec || !dest_rec)
276 return E_INVALIDARG;
278 /* release already stored data */
279 IRecordInfo_RecordClear(iface, dest_rec);
281 for (i = 0; i < This->n_vars; i++)
283 void *src, *dest;
285 if (This->fields[i].varkind != VAR_PERINSTANCE) {
286 ERR("varkind != VAR_PERINSTANCE\n");
287 continue;
290 src = ((BYTE*)src_rec) + This->fields[i].offset;
291 dest = ((BYTE*)dest_rec) + This->fields[i].offset;
292 switch (This->fields[i].vt)
294 case VT_BSTR:
296 BSTR src_str = *(BSTR*)src;
298 if (src_str)
300 BSTR str = SysAllocString(*(BSTR*)src);
301 if (!str) hr = E_OUTOFMEMORY;
303 *(BSTR*)dest = str;
305 else
306 *(BSTR*)dest = NULL;
307 break;
309 case VT_UNKNOWN:
310 case VT_DISPATCH:
312 IUnknown *unk = *(IUnknown**)src;
313 *(IUnknown**)dest = unk;
314 if (unk) IUnknown_AddRef(unk);
315 break;
317 case VT_SAFEARRAY:
318 hr = SafeArrayCopy(src, dest);
319 break;
320 default:
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;
329 if (FAILED(hr)) break;
332 if (FAILED(hr))
333 IRecordInfo_RecordClear(iface, dest_rec);
335 return hr;
338 static HRESULT WINAPI IRecordInfoImpl_GetGuid(IRecordInfo *iface, GUID *pguid)
340 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
342 TRACE("(%p)->(%p)\n", This, pguid);
344 if(!pguid)
345 return E_INVALIDARG;
347 *pguid = This->guid;
348 return S_OK;
351 static HRESULT WINAPI IRecordInfoImpl_GetName(IRecordInfo *iface, BSTR *pbstrName)
353 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
355 TRACE("(%p)->(%p)\n", This, pbstrName);
357 if(!pbstrName)
358 return E_INVALIDARG;
360 *pbstrName = SysAllocString(This->name);
361 return S_OK;
364 static HRESULT WINAPI IRecordInfoImpl_GetSize(IRecordInfo *iface, ULONG *pcbSize)
366 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
368 TRACE("(%p)->(%p)\n", This, pcbSize);
370 if(!pcbSize)
371 return E_INVALIDARG;
373 *pcbSize = This->size;
374 return S_OK;
377 static HRESULT WINAPI IRecordInfoImpl_GetTypeInfo(IRecordInfo *iface, ITypeInfo **ppTypeInfo)
379 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
381 TRACE("(%p)->(%p)\n", This, ppTypeInfo);
383 if(!ppTypeInfo)
384 return E_INVALIDARG;
386 ITypeInfo_AddRef(This->pTypeInfo);
387 *ppTypeInfo = This->pTypeInfo;
389 return S_OK;
392 static HRESULT WINAPI IRecordInfoImpl_GetField(IRecordInfo *iface, PVOID pvData,
393 LPCOLESTR szFieldName, VARIANT *pvarField)
395 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
396 int i;
398 TRACE("(%p)->(%p %s %p)\n", This, pvData, debugstr_w(szFieldName), pvarField);
400 if(!pvData || !szFieldName || !pvarField)
401 return E_INVALIDARG;
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;
409 VariantClear(pvarField);
410 return copy_to_variant(((PBYTE)pvData)+This->fields[i].offset, pvarField,
411 This->fields[i].vt);
414 static HRESULT WINAPI IRecordInfoImpl_GetFieldNoCopy(IRecordInfo *iface, PVOID pvData,
415 LPCOLESTR szFieldName, VARIANT *pvarField, PVOID *ppvDataCArray)
417 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
418 int i;
420 TRACE("(%p)->(%p %s %p %p)\n", This, pvData, debugstr_w(szFieldName), pvarField, ppvDataCArray);
422 if(!pvData || !szFieldName || !pvarField)
423 return E_INVALIDARG;
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;
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;
438 static HRESULT WINAPI IRecordInfoImpl_PutField(IRecordInfo *iface, ULONG wFlags, PVOID pvData,
439 LPCOLESTR szFieldName, VARIANT *pvarField)
441 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
442 int i;
444 TRACE("(%p)->(%08x %p %s %p)\n", This, wFlags, pvData, debugstr_w(szFieldName),
445 pvarField);
447 if(!pvData || !szFieldName || !pvarField
448 || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
449 return E_INVALIDARG;
451 if(wFlags == INVOKE_PROPERTYPUTREF) {
452 FIXME("wFlag == INVOKE_PROPERTYPUTREF not supported\n");
453 return E_NOTIMPL;
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;
462 return copy_from_variant(pvarField, ((PBYTE)pvData)+This->fields[i].offset,
463 This->fields[i].vt);
466 static HRESULT WINAPI IRecordInfoImpl_PutFieldNoCopy(IRecordInfo *iface, ULONG wFlags,
467 PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField)
469 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
470 int i;
472 FIXME("(%p)->(%08x %p %s %p) stub\n", This, wFlags, pvData, debugstr_w(szFieldName), pvarField);
474 if(!pvData || !szFieldName || !pvarField
475 || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
476 return E_INVALIDARG;
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;
484 return E_NOTIMPL;
487 static HRESULT WINAPI IRecordInfoImpl_GetFieldNames(IRecordInfo *iface, ULONG *pcNames,
488 BSTR *rgBstrNames)
490 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
491 ULONG n = This->n_vars, i;
493 TRACE("(%p)->(%p %p)\n", This, pcNames, rgBstrNames);
495 if(!pcNames)
496 return E_INVALIDARG;
498 if(*pcNames < n)
499 n = *pcNames;
501 if(rgBstrNames) {
502 for(i=0; i<n; i++)
503 rgBstrNames[i] = SysAllocString(This->fields[i].name);
506 *pcNames = n;
507 return S_OK;
510 static BOOL WINAPI IRecordInfoImpl_IsMatchingType(IRecordInfo *iface, IRecordInfo *info2)
512 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
513 GUID guid2;
515 TRACE( "(%p)->(%p)\n", This, info2 );
517 IRecordInfo_GetGuid( info2, &guid2 );
518 return IsEqualGUID( &This->guid, &guid2 );
521 static PVOID WINAPI IRecordInfoImpl_RecordCreate(IRecordInfo *iface)
523 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
524 void *record;
526 TRACE("(%p)\n", This);
528 record = HeapAlloc(GetProcessHeap(), 0, This->size);
529 IRecordInfo_RecordInit(iface, record);
530 TRACE("created record at %p\n", record);
531 return record;
534 static HRESULT WINAPI IRecordInfoImpl_RecordCreateCopy(IRecordInfo *iface, PVOID pvSource,
535 PVOID *ppvDest)
537 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
539 TRACE("(%p)->(%p %p)\n", This, pvSource, ppvDest);
541 if(!pvSource || !ppvDest)
542 return E_INVALIDARG;
544 *ppvDest = IRecordInfo_RecordCreate(iface);
545 return IRecordInfo_RecordCopy(iface, pvSource, *ppvDest);
548 static HRESULT WINAPI IRecordInfoImpl_RecordDestroy(IRecordInfo *iface, PVOID pvRecord)
550 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
551 HRESULT hres;
553 TRACE("(%p)->(%p)\n", This, pvRecord);
555 hres = IRecordInfo_RecordClear(iface, pvRecord);
556 if(FAILED(hres))
557 return hres;
559 if(!HeapFree(GetProcessHeap(), 0, pvRecord))
560 return E_INVALIDARG;
562 return S_OK;
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
587 /******************************************************************************
588 * GetRecordInfoFromGuids [OLEAUT32.322]
590 * RETURNS
591 * Success: S_OK
592 * Failure: E_INVALIDARG, if any argument is invalid.
594 HRESULT WINAPI GetRecordInfoFromGuids(REFGUID rGuidTypeLib, ULONG uVerMajor,
595 ULONG uVerMinor, LCID lcid, REFGUID rGuidTypeInfo, IRecordInfo** ppRecInfo)
597 ITypeInfo *pTypeInfo;
598 ITypeLib *pTypeLib;
599 HRESULT hres;
601 TRACE("(%p,%d,%d,%d,%s,%p)\n", rGuidTypeLib, uVerMajor, uVerMinor,
602 lcid, debugstr_guid(rGuidTypeInfo), ppRecInfo);
604 hres = LoadRegTypeLib(rGuidTypeLib, uVerMajor, uVerMinor, lcid, &pTypeLib);
605 if(FAILED(hres)) {
606 WARN("LoadRegTypeLib failed!\n");
607 return hres;
610 hres = ITypeLib_GetTypeInfoOfGuid(pTypeLib, rGuidTypeInfo, &pTypeInfo);
611 ITypeLib_Release(pTypeLib);
612 if(FAILED(hres)) {
613 WARN("GetTypeInfoOfGuid failed!\n");
614 return hres;
617 hres = GetRecordInfoFromTypeInfo(pTypeInfo, ppRecInfo);
618 ITypeInfo_Release(pTypeInfo);
619 return hres;
622 /******************************************************************************
623 * GetRecordInfoFromTypeInfo [OLEAUT32.332]
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;
633 TRACE("(%p %p)\n", pTI, ppRecInfo);
635 if(!pTI || !ppRecInfo)
636 return E_INVALIDARG;
638 hres = ITypeInfo_GetTypeAttr(pTI, &typeattr);
639 if(FAILED(hres) || !typeattr) {
640 WARN("GetTypeAttr failed: %08x\n", hres);
641 return hres;
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;
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;
658 }else {
659 pTypeInfo = pTI;
660 ITypeInfo_AddRef(pTypeInfo);
661 guid = typeattr->guid;
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;
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);
679 ret->guid = guid;
681 /* NOTE: Windows implementation calls ITypeInfo::GetCantainingTypeLib and
682 * ITypeLib::GetLibAttr, but we currently don't need this.
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;
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;
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);
710 *ppRecInfo = &ret->IRecordInfo_iface;
712 return S_OK;