gdi32: Fix leak in GdiDeleteSpoolFileHandle.
[wine.git] / dlls / oleaut32 / recinfo.c
blobdfe7625a98688ab06ceda5ee688756a1417d2a41
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 #include "windef.h"
23 #include "winbase.h"
24 #include "objbase.h"
25 #include "oaidl.h"
26 #include "oleauto.h"
27 #include "variant.h"
29 #include "wine/debug.h"
31 WINE_DEFAULT_DEBUG_CHANNEL(ole);
33 typedef struct {
34 enum VARENUM vt;
35 VARKIND varkind;
36 ULONG offset;
37 BSTR name;
38 } fieldstr;
40 typedef struct {
41 IRecordInfo IRecordInfo_iface;
42 LONG ref;
44 GUID guid;
45 UINT lib_index;
46 WORD n_vars;
47 ULONG size;
48 BSTR name;
49 fieldstr *fields;
50 ITypeInfo *pTypeInfo;
51 } IRecordInfoImpl;
53 static inline IRecordInfoImpl *impl_from_IRecordInfo(IRecordInfo *iface)
55 return CONTAINING_RECORD(iface, IRecordInfoImpl, IRecordInfo_iface);
58 static HRESULT copy_to_variant(void *src, VARIANT *pvar, enum VARENUM vt)
60 TRACE("%p %p %d\n", src, pvar, vt);
62 #define CASE_COPY(x) \
63 case VT_ ## x: \
64 memcpy(&V_ ## x(pvar), src, sizeof(V_ ## x(pvar))); \
65 break
67 switch(vt) {
68 CASE_COPY(I2);
69 CASE_COPY(I4);
70 CASE_COPY(R4);
71 CASE_COPY(R8);
72 CASE_COPY(CY);
73 CASE_COPY(DATE);
74 CASE_COPY(BSTR);
75 CASE_COPY(ERROR);
76 CASE_COPY(BOOL);
77 CASE_COPY(DECIMAL);
78 CASE_COPY(I1);
79 CASE_COPY(UI1);
80 CASE_COPY(UI2);
81 CASE_COPY(UI4);
82 CASE_COPY(I8);
83 CASE_COPY(UI8);
84 CASE_COPY(INT);
85 CASE_COPY(UINT);
86 CASE_COPY(INT_PTR);
87 CASE_COPY(UINT_PTR);
88 default:
89 FIXME("Not supported type: %d\n", vt);
90 return E_NOTIMPL;
92 #undef CASE_COPY
94 V_VT(pvar) = vt;
95 return S_OK;
98 static HRESULT copy_from_variant(VARIANT *src, void *dest, enum VARENUM vt)
100 VARIANT var;
101 HRESULT hres;
103 TRACE("(%p(%d) %p %d)\n", src, V_VT(src), dest, vt);
105 hres = VariantChangeType(&var, src, 0, vt);
106 if(FAILED(hres))
107 return hres;
109 #define CASE_COPY(x) \
110 case VT_ ## x: \
111 memcpy(dest, &V_ ## x(&var), sizeof(V_ ## x(&var))); \
112 break
114 switch(vt) {
115 CASE_COPY(I2);
116 CASE_COPY(I4);
117 CASE_COPY(R4);
118 CASE_COPY(R8);
119 CASE_COPY(CY);
120 CASE_COPY(DATE);
121 CASE_COPY(BSTR);
122 CASE_COPY(ERROR);
123 CASE_COPY(BOOL);
124 CASE_COPY(DECIMAL);
125 CASE_COPY(I1);
126 CASE_COPY(UI1);
127 CASE_COPY(UI2);
128 CASE_COPY(UI4);
129 CASE_COPY(I8);
130 CASE_COPY(UI8);
131 CASE_COPY(INT);
132 CASE_COPY(UINT);
133 CASE_COPY(INT_PTR);
134 CASE_COPY(UINT_PTR);
135 default:
136 FIXME("Not supported type: %d\n", V_VT(&var));
137 return E_NOTIMPL;
139 #undef CASE_COPY
140 return S_OK;
143 static HRESULT WINAPI IRecordInfoImpl_QueryInterface(IRecordInfo *iface, REFIID riid,
144 void **ppvObject)
146 TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
148 *ppvObject = NULL;
150 if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IRecordInfo, riid)) {
151 *ppvObject = iface;
152 IRecordInfo_AddRef(iface);
153 return S_OK;
156 FIXME("Not supported interface: %s\n", debugstr_guid(riid));
157 return E_NOINTERFACE;
160 static ULONG WINAPI IRecordInfoImpl_AddRef(IRecordInfo *iface)
162 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
163 ULONG ref = InterlockedIncrement(&This->ref);
164 TRACE("%p, refcount %lu.\n", iface, ref);
165 return ref;
168 static ULONG WINAPI IRecordInfoImpl_Release(IRecordInfo *iface)
170 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
171 ULONG ref = InterlockedDecrement(&This->ref);
173 TRACE("%p, refcount %lu.\n", iface, ref);
175 if(!ref) {
176 int i;
177 for(i=0; i<This->n_vars; i++)
178 SysFreeString(This->fields[i].name);
179 SysFreeString(This->name);
180 free(This->fields);
181 ITypeInfo_Release(This->pTypeInfo);
182 free(This);
184 return ref;
187 static HRESULT WINAPI IRecordInfoImpl_RecordInit(IRecordInfo *iface, PVOID pvNew)
189 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
190 TRACE("(%p)->(%p)\n", This, pvNew);
192 if(!pvNew)
193 return E_INVALIDARG;
195 memset(pvNew, 0, This->size);
196 return S_OK;
199 static HRESULT WINAPI IRecordInfoImpl_RecordClear(IRecordInfo *iface, PVOID pvExisting)
201 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
202 int i;
203 PVOID var;
205 TRACE("(%p)->(%p)\n", This, pvExisting);
207 if(!pvExisting)
208 return E_INVALIDARG;
210 for(i=0; i<This->n_vars; i++) {
211 if(This->fields[i].varkind != VAR_PERINSTANCE) {
212 ERR("varkind != VAR_PERINSTANCE\n");
213 continue;
215 var = ((PBYTE)pvExisting)+This->fields[i].offset;
216 switch(This->fields[i].vt) {
217 case VT_BSTR:
218 SysFreeString(*(BSTR*)var);
219 *(BSTR*)var = NULL;
220 break;
221 case VT_I2:
222 case VT_I4:
223 case VT_R4:
224 case VT_R8:
225 case VT_CY:
226 case VT_DATE:
227 case VT_ERROR:
228 case VT_BOOL:
229 case VT_DECIMAL:
230 case VT_I1:
231 case VT_UI1:
232 case VT_UI2:
233 case VT_UI4:
234 case VT_I8:
235 case VT_UI8:
236 case VT_INT:
237 case VT_UINT:
238 case VT_HRESULT:
239 break;
240 case VT_INT_PTR:
241 case VT_UINT_PTR:
242 *(void**)var = NULL;
243 break;
244 case VT_SAFEARRAY:
245 SafeArrayDestroy(var);
246 break;
247 case VT_UNKNOWN:
248 case VT_DISPATCH:
250 IUnknown *unk = *(IUnknown**)var;
251 if (unk)
252 IUnknown_Release(unk);
253 *(void**)var = NULL;
254 break;
256 default:
257 FIXME("Not supported vt = %d\n", This->fields[i].vt);
258 break;
262 return S_OK;
265 static HRESULT WINAPI IRecordInfoImpl_RecordCopy(IRecordInfo *iface, void *src_rec, void *dest_rec)
267 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
268 HRESULT hr = S_OK;
269 int i;
271 TRACE("(%p)->(%p %p)\n", This, src_rec, dest_rec);
273 if(!src_rec || !dest_rec)
274 return E_INVALIDARG;
276 /* release already stored data */
277 IRecordInfo_RecordClear(iface, dest_rec);
279 for (i = 0; i < This->n_vars; i++)
281 void *src, *dest;
283 if (This->fields[i].varkind != VAR_PERINSTANCE) {
284 ERR("varkind != VAR_PERINSTANCE\n");
285 continue;
288 src = ((BYTE*)src_rec) + This->fields[i].offset;
289 dest = ((BYTE*)dest_rec) + This->fields[i].offset;
290 switch (This->fields[i].vt)
292 case VT_BSTR:
294 BSTR src_str = *(BSTR*)src;
296 if (src_str)
298 BSTR str = SysAllocString(*(BSTR*)src);
299 if (!str) hr = E_OUTOFMEMORY;
301 *(BSTR*)dest = str;
303 else
304 *(BSTR*)dest = NULL;
305 break;
307 case VT_UNKNOWN:
308 case VT_DISPATCH:
310 IUnknown *unk = *(IUnknown**)src;
311 *(IUnknown**)dest = unk;
312 if (unk) IUnknown_AddRef(unk);
313 break;
315 case VT_SAFEARRAY:
316 hr = SafeArrayCopy(src, dest);
317 break;
318 default:
320 /* copy directly for types that don't need deep copy */
321 int len = get_type_size(NULL, This->fields[i].vt);
322 memcpy(dest, src, len);
323 break;
327 if (FAILED(hr)) break;
330 if (FAILED(hr))
331 IRecordInfo_RecordClear(iface, dest_rec);
333 return hr;
336 static HRESULT WINAPI IRecordInfoImpl_GetGuid(IRecordInfo *iface, GUID *pguid)
338 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
340 TRACE("(%p)->(%p)\n", This, pguid);
342 if(!pguid)
343 return E_INVALIDARG;
345 *pguid = This->guid;
346 return S_OK;
349 static HRESULT WINAPI IRecordInfoImpl_GetName(IRecordInfo *iface, BSTR *pbstrName)
351 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
353 TRACE("(%p)->(%p)\n", This, pbstrName);
355 if(!pbstrName)
356 return E_INVALIDARG;
358 *pbstrName = SysAllocString(This->name);
359 return S_OK;
362 static HRESULT WINAPI IRecordInfoImpl_GetSize(IRecordInfo *iface, ULONG *pcbSize)
364 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
366 TRACE("(%p)->(%p)\n", This, pcbSize);
368 if(!pcbSize)
369 return E_INVALIDARG;
371 *pcbSize = This->size;
372 return S_OK;
375 static HRESULT WINAPI IRecordInfoImpl_GetTypeInfo(IRecordInfo *iface, ITypeInfo **ppTypeInfo)
377 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
379 TRACE("(%p)->(%p)\n", This, ppTypeInfo);
381 if(!ppTypeInfo)
382 return E_INVALIDARG;
384 ITypeInfo_AddRef(This->pTypeInfo);
385 *ppTypeInfo = This->pTypeInfo;
387 return S_OK;
390 static HRESULT WINAPI IRecordInfoImpl_GetField(IRecordInfo *iface, PVOID pvData,
391 LPCOLESTR szFieldName, VARIANT *pvarField)
393 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
394 int i;
396 TRACE("(%p)->(%p %s %p)\n", This, pvData, debugstr_w(szFieldName), pvarField);
398 if(!pvData || !szFieldName || !pvarField)
399 return E_INVALIDARG;
401 for(i=0; i<This->n_vars; i++)
402 if(!wcscmp(This->fields[i].name, szFieldName))
403 break;
404 if(i == This->n_vars)
405 return TYPE_E_FIELDNOTFOUND;
407 VariantClear(pvarField);
408 return copy_to_variant(((PBYTE)pvData)+This->fields[i].offset, pvarField,
409 This->fields[i].vt);
412 static HRESULT WINAPI IRecordInfoImpl_GetFieldNoCopy(IRecordInfo *iface, PVOID pvData,
413 LPCOLESTR szFieldName, VARIANT *pvarField, PVOID *ppvDataCArray)
415 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
416 int i;
418 TRACE("(%p)->(%p %s %p %p)\n", This, pvData, debugstr_w(szFieldName), pvarField, ppvDataCArray);
420 if(!pvData || !szFieldName || !pvarField)
421 return E_INVALIDARG;
423 for(i=0; i<This->n_vars; i++)
424 if(!wcscmp(This->fields[i].name, szFieldName))
425 break;
426 if(i == This->n_vars)
427 return TYPE_E_FIELDNOTFOUND;
429 VariantClear(pvarField);
430 V_VT(pvarField) = VT_BYREF|This->fields[i].vt;
431 V_BYREF(pvarField) = ((PBYTE)pvData)+This->fields[i].offset;
432 *ppvDataCArray = NULL;
433 return S_OK;
436 static HRESULT WINAPI IRecordInfoImpl_PutField(IRecordInfo *iface, ULONG wFlags, PVOID pvData,
437 LPCOLESTR szFieldName, VARIANT *pvarField)
439 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
440 int i;
442 TRACE("%p, %#lx, %p, %s, %p.\n", iface, wFlags, pvData, debugstr_w(szFieldName), pvarField);
444 if(!pvData || !szFieldName || !pvarField
445 || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
446 return E_INVALIDARG;
448 if(wFlags == INVOKE_PROPERTYPUTREF) {
449 FIXME("wFlag == INVOKE_PROPERTYPUTREF not supported\n");
450 return E_NOTIMPL;
453 for(i=0; i<This->n_vars; i++)
454 if(!wcscmp(This->fields[i].name, szFieldName))
455 break;
456 if(i == This->n_vars)
457 return TYPE_E_FIELDNOTFOUND;
459 return copy_from_variant(pvarField, ((PBYTE)pvData)+This->fields[i].offset,
460 This->fields[i].vt);
463 static HRESULT WINAPI IRecordInfoImpl_PutFieldNoCopy(IRecordInfo *iface, ULONG wFlags,
464 PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField)
466 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
467 int i;
469 FIXME("%p, %#lx, %p, %s, %p stub\n", iface, wFlags, pvData, debugstr_w(szFieldName), pvarField);
471 if(!pvData || !szFieldName || !pvarField
472 || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
473 return E_INVALIDARG;
475 for(i=0; i<This->n_vars; i++)
476 if(!wcscmp(This->fields[i].name, szFieldName))
477 break;
478 if(i == This->n_vars)
479 return TYPE_E_FIELDNOTFOUND;
481 return E_NOTIMPL;
484 static HRESULT WINAPI IRecordInfoImpl_GetFieldNames(IRecordInfo *iface, ULONG *pcNames,
485 BSTR *rgBstrNames)
487 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
488 ULONG n = This->n_vars, i;
490 TRACE("(%p)->(%p %p)\n", This, pcNames, rgBstrNames);
492 if(!pcNames)
493 return E_INVALIDARG;
495 if(*pcNames < n)
496 n = *pcNames;
498 if(rgBstrNames) {
499 for(i=0; i<n; i++)
500 rgBstrNames[i] = SysAllocString(This->fields[i].name);
503 *pcNames = n;
504 return S_OK;
507 static BOOL WINAPI IRecordInfoImpl_IsMatchingType(IRecordInfo *iface, IRecordInfo *info2)
509 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
510 GUID guid2;
512 TRACE( "(%p)->(%p)\n", This, info2 );
514 IRecordInfo_GetGuid( info2, &guid2 );
515 return IsEqualGUID( &This->guid, &guid2 );
518 static PVOID WINAPI IRecordInfoImpl_RecordCreate(IRecordInfo *iface)
520 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
521 void *record;
523 TRACE("(%p)\n", This);
525 record = CoTaskMemAlloc(This->size);
526 IRecordInfo_RecordInit(iface, record);
527 TRACE("created record at %p\n", record);
528 return record;
531 static HRESULT WINAPI IRecordInfoImpl_RecordCreateCopy(IRecordInfo *iface, PVOID pvSource,
532 PVOID *ppvDest)
534 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
536 TRACE("(%p)->(%p %p)\n", This, pvSource, ppvDest);
538 if(!pvSource || !ppvDest)
539 return E_INVALIDARG;
541 *ppvDest = IRecordInfo_RecordCreate(iface);
542 return IRecordInfo_RecordCopy(iface, pvSource, *ppvDest);
545 static HRESULT WINAPI IRecordInfoImpl_RecordDestroy(IRecordInfo *iface, PVOID pvRecord)
547 IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
548 HRESULT hres;
550 TRACE("(%p)->(%p)\n", This, pvRecord);
552 hres = IRecordInfo_RecordClear(iface, pvRecord);
553 if(FAILED(hres))
554 return hres;
556 CoTaskMemFree(pvRecord);
557 return S_OK;
560 static const IRecordInfoVtbl IRecordInfoImplVtbl = {
561 IRecordInfoImpl_QueryInterface,
562 IRecordInfoImpl_AddRef,
563 IRecordInfoImpl_Release,
564 IRecordInfoImpl_RecordInit,
565 IRecordInfoImpl_RecordClear,
566 IRecordInfoImpl_RecordCopy,
567 IRecordInfoImpl_GetGuid,
568 IRecordInfoImpl_GetName,
569 IRecordInfoImpl_GetSize,
570 IRecordInfoImpl_GetTypeInfo,
571 IRecordInfoImpl_GetField,
572 IRecordInfoImpl_GetFieldNoCopy,
573 IRecordInfoImpl_PutField,
574 IRecordInfoImpl_PutFieldNoCopy,
575 IRecordInfoImpl_GetFieldNames,
576 IRecordInfoImpl_IsMatchingType,
577 IRecordInfoImpl_RecordCreate,
578 IRecordInfoImpl_RecordCreateCopy,
579 IRecordInfoImpl_RecordDestroy
582 /******************************************************************************
583 * GetRecordInfoFromGuids [OLEAUT32.322]
585 * RETURNS
586 * Success: S_OK
587 * Failure: E_INVALIDARG, if any argument is invalid.
589 HRESULT WINAPI GetRecordInfoFromGuids(REFGUID rGuidTypeLib, ULONG uVerMajor,
590 ULONG uVerMinor, LCID lcid, REFGUID rGuidTypeInfo, IRecordInfo** ppRecInfo)
592 ITypeInfo *pTypeInfo;
593 ITypeLib *pTypeLib;
594 HRESULT hres;
596 TRACE("%s, %lu, %lu, %#lx, %s, %p.\n", debugstr_guid(rGuidTypeLib), uVerMajor, uVerMinor,
597 lcid, debugstr_guid(rGuidTypeInfo), ppRecInfo);
599 hres = LoadRegTypeLib(rGuidTypeLib, uVerMajor, uVerMinor, lcid, &pTypeLib);
600 if(FAILED(hres)) {
601 WARN("LoadRegTypeLib failed!\n");
602 return hres;
605 hres = ITypeLib_GetTypeInfoOfGuid(pTypeLib, rGuidTypeInfo, &pTypeInfo);
606 ITypeLib_Release(pTypeLib);
607 if(FAILED(hres)) {
608 WARN("GetTypeInfoOfGuid failed!\n");
609 return hres;
612 hres = GetRecordInfoFromTypeInfo(pTypeInfo, ppRecInfo);
613 ITypeInfo_Release(pTypeInfo);
614 return hres;
617 /******************************************************************************
618 * GetRecordInfoFromTypeInfo [OLEAUT32.332]
620 HRESULT WINAPI GetRecordInfoFromTypeInfo(ITypeInfo* pTI, IRecordInfo** ppRecInfo) {
621 HRESULT hres;
622 TYPEATTR *typeattr;
623 IRecordInfoImpl *ret;
624 ITypeInfo *pTypeInfo;
625 int i;
626 GUID guid;
628 TRACE("(%p %p)\n", pTI, ppRecInfo);
630 if(!pTI || !ppRecInfo)
631 return E_INVALIDARG;
633 hres = ITypeInfo_GetTypeAttr(pTI, &typeattr);
634 if(FAILED(hres) || !typeattr) {
635 WARN("GetTypeAttr failed: %#lx.\n", hres);
636 return hres;
639 if(typeattr->typekind == TKIND_ALIAS) {
640 hres = ITypeInfo_GetRefTypeInfo(pTI, typeattr->tdescAlias.hreftype, &pTypeInfo);
641 guid = typeattr->guid;
642 ITypeInfo_ReleaseTypeAttr(pTI, typeattr);
643 if(FAILED(hres)) {
644 WARN("GetRefTypeInfo failed: %#lx.\n", hres);
645 return hres;
647 hres = ITypeInfo_GetTypeAttr(pTypeInfo, &typeattr);
648 if(FAILED(hres)) {
649 ITypeInfo_Release(pTypeInfo);
650 WARN("GetTypeAttr failed for referenced type: %#lx.\n", hres);
651 return hres;
653 }else {
654 pTypeInfo = pTI;
655 ITypeInfo_AddRef(pTypeInfo);
656 guid = typeattr->guid;
659 if(typeattr->typekind != TKIND_RECORD) {
660 WARN("typekind != TKIND_RECORD\n");
661 ITypeInfo_ReleaseTypeAttr(pTypeInfo, typeattr);
662 ITypeInfo_Release(pTypeInfo);
663 return E_INVALIDARG;
666 ret = calloc(1, sizeof(*ret));
667 ret->IRecordInfo_iface.lpVtbl = &IRecordInfoImplVtbl;
668 ret->ref = 1;
669 ret->pTypeInfo = pTypeInfo;
670 ret->n_vars = typeattr->cVars;
671 ret->size = typeattr->cbSizeInstance;
672 ITypeInfo_ReleaseTypeAttr(pTypeInfo, typeattr);
674 ret->guid = guid;
676 /* NOTE: Windows implementation calls ITypeInfo::GetCantainingTypeLib and
677 * ITypeLib::GetLibAttr, but we currently don't need this.
680 hres = ITypeInfo_GetDocumentation(pTypeInfo, MEMBERID_NIL, &ret->name, NULL, NULL, NULL);
681 if(FAILED(hres)) {
682 WARN("ITypeInfo::GetDocumentation failed\n");
683 ret->name = NULL;
686 ret->fields = calloc(ret->n_vars, sizeof(fieldstr));
687 for(i = 0; i<ret->n_vars; i++) {
688 VARDESC *vardesc;
689 hres = ITypeInfo_GetVarDesc(pTypeInfo, i, &vardesc);
690 if(FAILED(hres)) {
691 WARN("GetVarDesc failed\n");
692 continue;
694 ret->fields[i].vt = vardesc->elemdescVar.tdesc.vt;
695 ret->fields[i].varkind = vardesc->varkind;
696 ret->fields[i].offset = vardesc->oInst;
697 hres = ITypeInfo_GetDocumentation(pTypeInfo, vardesc->memid, &ret->fields[i].name,
698 NULL, NULL, NULL);
699 if(FAILED(hres))
700 WARN("GetDocumentation failed: %#lx.\n", hres);
701 TRACE("field=%s, offset=%ld\n", debugstr_w(ret->fields[i].name), ret->fields[i].offset);
702 ITypeInfo_ReleaseVarDesc(pTypeInfo, vardesc);
705 *ppRecInfo = &ret->IRecordInfo_iface;
707 return S_OK;