d3d10core/tests: Remove dead code.
[wine.git] / dlls / oleaut32 / recinfo.c
blobec65a523e386c7f086648b52417c98088ecfbd43
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/unicode.h"
32 #include "wine/debug.h"
34 WINE_DEFAULT_DEBUG_CHANNEL(ole);
36 typedef struct {
37 enum VARENUM vt;
38 VARKIND varkind;
39 ULONG offset;
40 BSTR name;
41 } fieldstr;
43 typedef struct {
44 IRecordInfo IRecordInfo_iface;
45 LONG ref;
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;
56 static inline IRecordInfoImpl *impl_from_IRecordInfo(IRecordInfo *iface)
58 return CONTAINING_RECORD(iface, IRecordInfoImpl, IRecordInfo_iface);
61 static HRESULT copy_to_variant(void *src, VARIANT *pvar, enum VARENUM vt)
63 TRACE("%p %p %d\n", src, pvar, vt);
65 #define CASE_COPY(x) \
66 case VT_ ## x: \
67 memcpy(&V_ ## x(pvar), src, sizeof(V_ ## x(pvar))); \
68 break
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;
95 #undef CASE_COPY
97 V_VT(pvar) = vt;
98 return S_OK;
101 static HRESULT copy_from_variant(VARIANT *src, void *dest, enum VARENUM vt)
103 VARIANT var;
104 HRESULT hres;
106 TRACE("(%p(%d) %p %d)\n", src, V_VT(src), dest, vt);
108 hres = VariantChangeType(&var, src, 0, vt);
109 if(FAILED(hres))
110 return hres;
112 #define CASE_COPY(x) \
113 case VT_ ## x: \
114 memcpy(dest, &V_ ## x(&var), sizeof(V_ ## x(&var))); \
115 break
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;
142 #undef CASE_COPY
143 return S_OK;
146 static HRESULT WINAPI IRecordInfoImpl_QueryInterface(IRecordInfo *iface, REFIID riid,
147 void **ppvObject)
149 TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
151 *ppvObject = NULL;
153 if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IRecordInfo, riid)) {
154 *ppvObject = iface;
155 IRecordInfo_AddRef(iface);
156 return S_OK;
159 FIXME("Not supported interface: %s\n", debugstr_guid(riid));
160 return E_NOINTERFACE;
163 static ULONG WINAPI IRecordInfoImpl_AddRef(IRecordInfo *iface)
165 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
166 ULONG ref = InterlockedIncrement(&This->ref);
167 TRACE("(%p) -> %d\n", This, ref);
168 return ref;
171 static ULONG WINAPI IRecordInfoImpl_Release(IRecordInfo *iface)
173 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
174 ULONG ref = InterlockedDecrement(&This->ref);
176 TRACE("(%p) -> %d\n", This, ref);
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);
187 return ref;
190 static HRESULT WINAPI IRecordInfoImpl_RecordInit(IRecordInfo *iface, PVOID pvNew)
192 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
193 TRACE("(%p)->(%p)\n", This, pvNew);
195 if(!pvNew)
196 return E_INVALIDARG;
198 memset(pvNew, 0, This->size);
199 return S_OK;
202 static HRESULT WINAPI IRecordInfoImpl_RecordClear(IRecordInfo *iface, PVOID pvExisting)
204 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
205 int i;
206 PVOID var;
208 TRACE("(%p)->(%p)\n", This, pvExisting);
210 if(!pvExisting)
211 return E_INVALIDARG;
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;
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:
253 IUnknown *unk = *(IUnknown**)var;
254 if (unk)
255 IUnknown_Release(unk);
256 *(void**)var = NULL;
257 break;
259 default:
260 FIXME("Not supported vt = %d\n", This->fields[i].vt);
261 break;
265 return S_OK;
268 static HRESULT WINAPI IRecordInfoImpl_RecordCopy(IRecordInfo *iface, void *src_rec, void *dest_rec)
270 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
271 HRESULT hr = S_OK;
272 int i;
274 TRACE("(%p)->(%p %p)\n", This, src_rec, dest_rec);
276 if(!src_rec || !dest_rec)
277 return E_INVALIDARG;
279 /* release already stored data */
280 IRecordInfo_RecordClear(iface, dest_rec);
282 for (i = 0; i < This->n_vars; i++)
284 void *src, *dest;
286 if (This->fields[i].varkind != VAR_PERINSTANCE) {
287 ERR("varkind != VAR_PERINSTANCE\n");
288 continue;
291 src = ((BYTE*)src_rec) + This->fields[i].offset;
292 dest = ((BYTE*)dest_rec) + This->fields[i].offset;
293 switch (This->fields[i].vt)
295 case VT_BSTR:
297 BSTR src_str = *(BSTR*)src;
299 if (src_str)
301 BSTR str = SysAllocString(*(BSTR*)src);
302 if (!str) hr = E_OUTOFMEMORY;
304 *(BSTR*)dest = str;
306 else
307 *(BSTR*)dest = NULL;
308 break;
310 case VT_UNKNOWN:
311 case VT_DISPATCH:
313 IUnknown *unk = *(IUnknown**)src;
314 *(IUnknown**)dest = unk;
315 if (unk) IUnknown_AddRef(unk);
316 break;
318 case VT_SAFEARRAY:
319 hr = SafeArrayCopy(src, dest);
320 break;
321 default:
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;
330 if (FAILED(hr)) break;
333 if (FAILED(hr))
334 IRecordInfo_RecordClear(iface, dest_rec);
336 return hr;
339 static HRESULT WINAPI IRecordInfoImpl_GetGuid(IRecordInfo *iface, GUID *pguid)
341 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
343 TRACE("(%p)->(%p)\n", This, pguid);
345 if(!pguid)
346 return E_INVALIDARG;
348 *pguid = This->guid;
349 return S_OK;
352 static HRESULT WINAPI IRecordInfoImpl_GetName(IRecordInfo *iface, BSTR *pbstrName)
354 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
356 TRACE("(%p)->(%p)\n", This, pbstrName);
358 if(!pbstrName)
359 return E_INVALIDARG;
361 *pbstrName = SysAllocString(This->name);
362 return S_OK;
365 static HRESULT WINAPI IRecordInfoImpl_GetSize(IRecordInfo *iface, ULONG *pcbSize)
367 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
369 TRACE("(%p)->(%p)\n", This, pcbSize);
371 if(!pcbSize)
372 return E_INVALIDARG;
374 *pcbSize = This->size;
375 return S_OK;
378 static HRESULT WINAPI IRecordInfoImpl_GetTypeInfo(IRecordInfo *iface, ITypeInfo **ppTypeInfo)
380 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
382 TRACE("(%p)->(%p)\n", This, ppTypeInfo);
384 if(!ppTypeInfo)
385 return E_INVALIDARG;
387 ITypeInfo_AddRef(This->pTypeInfo);
388 *ppTypeInfo = This->pTypeInfo;
390 return S_OK;
393 static HRESULT WINAPI IRecordInfoImpl_GetField(IRecordInfo *iface, PVOID pvData,
394 LPCOLESTR szFieldName, VARIANT *pvarField)
396 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
397 int i;
399 TRACE("(%p)->(%p %s %p)\n", This, pvData, debugstr_w(szFieldName), pvarField);
401 if(!pvData || !szFieldName || !pvarField)
402 return E_INVALIDARG;
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;
410 VariantClear(pvarField);
411 return copy_to_variant(((PBYTE)pvData)+This->fields[i].offset, pvarField,
412 This->fields[i].vt);
415 static HRESULT WINAPI IRecordInfoImpl_GetFieldNoCopy(IRecordInfo *iface, PVOID pvData,
416 LPCOLESTR szFieldName, VARIANT *pvarField, PVOID *ppvDataCArray)
418 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
419 int i;
421 TRACE("(%p)->(%p %s %p %p)\n", This, pvData, debugstr_w(szFieldName), pvarField, ppvDataCArray);
423 if(!pvData || !szFieldName || !pvarField)
424 return E_INVALIDARG;
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;
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;
439 static HRESULT WINAPI IRecordInfoImpl_PutField(IRecordInfo *iface, ULONG wFlags, PVOID pvData,
440 LPCOLESTR szFieldName, VARIANT *pvarField)
442 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
443 int i;
445 TRACE("(%p)->(%08x %p %s %p)\n", This, wFlags, pvData, debugstr_w(szFieldName),
446 pvarField);
448 if(!pvData || !szFieldName || !pvarField
449 || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
450 return E_INVALIDARG;
452 if(wFlags == INVOKE_PROPERTYPUTREF) {
453 FIXME("wFlag == INVOKE_PROPERTYPUTREF not supported\n");
454 return E_NOTIMPL;
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;
463 return copy_from_variant(pvarField, ((PBYTE)pvData)+This->fields[i].offset,
464 This->fields[i].vt);
467 static HRESULT WINAPI IRecordInfoImpl_PutFieldNoCopy(IRecordInfo *iface, ULONG wFlags,
468 PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField)
470 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
471 int i;
473 FIXME("(%p)->(%08x %p %s %p) stub\n", This, wFlags, pvData, debugstr_w(szFieldName), pvarField);
475 if(!pvData || !szFieldName || !pvarField
476 || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
477 return E_INVALIDARG;
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;
485 return E_NOTIMPL;
488 static HRESULT WINAPI IRecordInfoImpl_GetFieldNames(IRecordInfo *iface, ULONG *pcNames,
489 BSTR *rgBstrNames)
491 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
492 ULONG n = This->n_vars, i;
494 TRACE("(%p)->(%p %p)\n", This, pcNames, rgBstrNames);
496 if(!pcNames)
497 return E_INVALIDARG;
499 if(*pcNames < n)
500 n = *pcNames;
502 if(rgBstrNames) {
503 for(i=0; i<n; i++)
504 rgBstrNames[i] = SysAllocString(This->fields[i].name);
507 *pcNames = n;
508 return S_OK;
511 static BOOL WINAPI IRecordInfoImpl_IsMatchingType(IRecordInfo *iface, IRecordInfo *info2)
513 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
514 GUID guid2;
516 TRACE( "(%p)->(%p)\n", This, info2 );
518 IRecordInfo_GetGuid( info2, &guid2 );
519 return IsEqualGUID( &This->guid, &guid2 );
522 static PVOID WINAPI IRecordInfoImpl_RecordCreate(IRecordInfo *iface)
524 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
525 void *record;
527 TRACE("(%p)\n", This);
529 record = HeapAlloc(GetProcessHeap(), 0, This->size);
530 IRecordInfo_RecordInit(iface, record);
531 TRACE("created record at %p\n", record);
532 return record;
535 static HRESULT WINAPI IRecordInfoImpl_RecordCreateCopy(IRecordInfo *iface, PVOID pvSource,
536 PVOID *ppvDest)
538 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
540 TRACE("(%p)->(%p %p)\n", This, pvSource, ppvDest);
542 if(!pvSource || !ppvDest)
543 return E_INVALIDARG;
545 *ppvDest = IRecordInfo_RecordCreate(iface);
546 return IRecordInfo_RecordCopy(iface, pvSource, *ppvDest);
549 static HRESULT WINAPI IRecordInfoImpl_RecordDestroy(IRecordInfo *iface, PVOID pvRecord)
551 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
552 HRESULT hres;
554 TRACE("(%p)->(%p)\n", This, pvRecord);
556 hres = IRecordInfo_RecordClear(iface, pvRecord);
557 if(FAILED(hres))
558 return hres;
560 if(!HeapFree(GetProcessHeap(), 0, pvRecord))
561 return E_INVALIDARG;
563 return S_OK;
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
588 /******************************************************************************
589 * GetRecordInfoFromGuids [OLEAUT32.322]
591 * RETURNS
592 * Success: S_OK
593 * Failure: E_INVALIDARG, if any argument is invalid.
595 HRESULT WINAPI GetRecordInfoFromGuids(REFGUID rGuidTypeLib, ULONG uVerMajor,
596 ULONG uVerMinor, LCID lcid, REFGUID rGuidTypeInfo, IRecordInfo** ppRecInfo)
598 ITypeInfo *pTypeInfo;
599 ITypeLib *pTypeLib;
600 HRESULT hres;
602 TRACE("(%p,%d,%d,%d,%s,%p)\n", rGuidTypeLib, uVerMajor, uVerMinor,
603 lcid, debugstr_guid(rGuidTypeInfo), ppRecInfo);
605 hres = LoadRegTypeLib(rGuidTypeLib, uVerMajor, uVerMinor, lcid, &pTypeLib);
606 if(FAILED(hres)) {
607 WARN("LoadRegTypeLib failed!\n");
608 return hres;
611 hres = ITypeLib_GetTypeInfoOfGuid(pTypeLib, rGuidTypeInfo, &pTypeInfo);
612 ITypeLib_Release(pTypeLib);
613 if(FAILED(hres)) {
614 WARN("GetTypeInfoOfGuid failed!\n");
615 return hres;
618 hres = GetRecordInfoFromTypeInfo(pTypeInfo, ppRecInfo);
619 ITypeInfo_Release(pTypeInfo);
620 return hres;
623 /******************************************************************************
624 * GetRecordInfoFromTypeInfo [OLEAUT32.332]
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;
634 TRACE("(%p %p)\n", pTI, ppRecInfo);
636 if(!pTI || !ppRecInfo)
637 return E_INVALIDARG;
639 hres = ITypeInfo_GetTypeAttr(pTI, &typeattr);
640 if(FAILED(hres) || !typeattr) {
641 WARN("GetTypeAttr failed: %08x\n", hres);
642 return hres;
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;
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;
659 }else {
660 pTypeInfo = pTI;
661 ITypeInfo_AddRef(pTypeInfo);
662 guid = typeattr->guid;
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;
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);
680 ret->guid = guid;
682 /* NOTE: Windows implementation calls ITypeInfo::GetCantainingTypeLib and
683 * ITypeLib::GetLibAttr, but we currently don't need this.
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;
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;
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);
711 *ppRecInfo = &ret->IRecordInfo_iface;
713 return S_OK;