text: /*
   * Copyright 2005 Jacek Caban
   *
   * This library is free software; you can redistribute it and/or
   * modify it under the terms of the GNU Lesser General Public
   * License as published by the Free Software Foundation; either
   * version 2.1 of the License, or (at your option) any later version.
   *
   * This library is distributed in the hope that it will be useful,
   * but WITHOUT ANY WARRANTY; without even the implied warranty of
   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   * Lesser General Public License for more details.
   *
   * You should have received a copy of the GNU Lesser General Public
   * License along with this library; if not, write to the Free Software
   * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
   */
  
  #include <stdarg.h>
  
  #define COBJMACROS
  #define NONAMELESSUNION
  
  #include "windef.h"
  #include "winbase.h"
  #include "objbase.h"
  #include "oaidl.h"
  #include "oleauto.h"
  #include "variant.h"
  
  #include "wine/debug.h"
  
  WINE_DEFAULT_DEBUG_CHANNEL(ole);
  
  typedef struct {
      enum VARENUM vt;
      VARKIND varkind;
      ULONG offset;
      BSTR name;
  } fieldstr;
  
  typedef struct {
      IRecordInfo IRecordInfo_iface;
      LONG ref;
  
      GUID guid;
      UINT lib_index;
      WORD n_vars;
      ULONG size;
      BSTR name;
      fieldstr *fields;
      ITypeInfo *pTypeInfo;
  } IRecordInfoImpl;
  
  static inline IRecordInfoImpl *impl_from_IRecordInfo(IRecordInfo *iface)
  {
      return CONTAINING_RECORD(iface, IRecordInfoImpl, IRecordInfo_iface);
  }
  
  static HRESULT copy_to_variant(void *src, VARIANT *pvar, enum VARENUM vt)
  {
      TRACE("%p %p %d\n", src, pvar, vt);
  
  #define CASE_COPY(x) \
      case VT_ ## x: \
          memcpy(&V_ ## x(pvar), src, sizeof(V_ ## x(pvar))); \
          break 
  
      switch(vt) {
          CASE_COPY(I2);
          CASE_COPY(I4);
          CASE_COPY(R4);
          CASE_COPY(R8);
          CASE_COPY(CY);
          CASE_COPY(DATE);
          CASE_COPY(BSTR);
          CASE_COPY(ERROR);
          CASE_COPY(BOOL);
          CASE_COPY(DECIMAL);
          CASE_COPY(I1);
          CASE_COPY(UI1);
          CASE_COPY(UI2);
          CASE_COPY(UI4);
          CASE_COPY(I8);
          CASE_COPY(UI8);
          CASE_COPY(INT);
          CASE_COPY(UINT);
          CASE_COPY(INT_PTR);
          CASE_COPY(UINT_PTR);
      default:
          FIXME("Not supported type: %d\n", vt);
          return E_NOTIMPL;
      };
  #undef CASE_COPY
  
      V_VT(pvar) = vt;
      return S_OK;
  }
  
  static HRESULT copy_from_variant(VARIANT *src, void *dest, enum VARENUM vt)
  {
      VARIANT var;
      HRESULT hres;
  
      TRACE("(%p(%d) %p %d)\n", src, V_VT(src), dest, vt);
  
      hres = VariantChangeType(&var, src, 0, vt);
      if(FAILED(hres))
          return hres;
  
  #define CASE_COPY(x) \
      case VT_ ## x: \
          memcpy(dest, &V_ ## x(&var), sizeof(V_ ## x(&var))); \
          break
  
      switch(vt) {
          CASE_COPY(I2);
          CASE_COPY(I4);
          CASE_COPY(R4);
          CASE_COPY(R8);
          CASE_COPY(CY);
          CASE_COPY(DATE);
          CASE_COPY(BSTR);
          CASE_COPY(ERROR);
          CASE_COPY(BOOL);
          CASE_COPY(DECIMAL);
          CASE_COPY(I1);
          CASE_COPY(UI1);
          CASE_COPY(UI2);
          CASE_COPY(UI4);
          CASE_COPY(I8);
          CASE_COPY(UI8);
          CASE_COPY(INT);
          CASE_COPY(UINT);
          CASE_COPY(INT_PTR);
          CASE_COPY(UINT_PTR);
      default:
          FIXME("Not supported type: %d\n", V_VT(&var));
          return E_NOTIMPL;
      };
  #undef CASE_COPY
      return S_OK;
  }
  
  static HRESULT WINAPI IRecordInfoImpl_QueryInterface(IRecordInfo *iface, REFIID riid,
                                                  void **ppvObject)
  {
      TRACE("(%p)->(%s %p)\n", iface, debugstr_guid(riid), ppvObject);
  
      *ppvObject = NULL;
  
      if(IsEqualGUID(&IID_IUnknown, riid) || IsEqualGUID(&IID_IRecordInfo, riid)) {
         *ppvObject = iface;
         IRecordInfo_AddRef(iface);
         return S_OK;
      }
  
      FIXME("Not supported interface: %s\n", debugstr_guid(riid));
      return E_NOINTERFACE;
  }
  
  static ULONG WINAPI IRecordInfoImpl_AddRef(IRecordInfo *iface)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
      ULONG ref = InterlockedIncrement(&This->ref);
      TRACE("(%p) -> %d\n", This, ref);
      return ref;
  }
  
  static ULONG WINAPI IRecordInfoImpl_Release(IRecordInfo *iface)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
      ULONG ref = InterlockedDecrement(&This->ref);
  
      TRACE("(%p) -> %d\n", This, ref);
  
      if(!ref) {
          int i;
          for(i=0; i<This->n_vars; i++)
              SysFreeString(This->fields[i].name);
          SysFreeString(This->name);
          HeapFree(GetProcessHeap(), 0, This->fields);
          ITypeInfo_Release(This->pTypeInfo);
          HeapFree(GetProcessHeap(), 0, This);
      }
      return ref;
  }
  
  static HRESULT WINAPI IRecordInfoImpl_RecordInit(IRecordInfo *iface, PVOID pvNew)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
      TRACE("(%p)->(%p)\n", This, pvNew);
  
      if(!pvNew)
          return E_INVALIDARG;
  
      memset(pvNew, 0, This->size);
      return S_OK;
  }
  
  static HRESULT WINAPI IRecordInfoImpl_RecordClear(IRecordInfo *iface, PVOID pvExisting)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
      int i;
      PVOID var;
  
      TRACE("(%p)->(%p)\n", This, pvExisting);
  
      if(!pvExisting)
          return E_INVALIDARG;
  
      for(i=0; i<This->n_vars; i++) {
          if(This->fields[i].varkind != VAR_PERINSTANCE) {
              ERR("varkind != VAR_PERINSTANCE\n");
              continue;
          }
          var = ((PBYTE)pvExisting)+This->fields[i].offset;
          switch(This->fields[i].vt) {
              case VT_BSTR:
                 SysFreeString(*(BSTR*)var);
                  *(BSTR*)var = NULL;
                  break;
              case VT_I2:
              case VT_I4:
              case VT_R4:
              case VT_R8:
              case VT_CY:
              case VT_DATE:
              case VT_ERROR:
              case VT_BOOL:
              case VT_DECIMAL:
              case VT_I1:
              case VT_UI1:
              case VT_UI2:
              case VT_UI4:
              case VT_I8:
              case VT_UI8:
              case VT_INT:
              case VT_UINT:
              case VT_HRESULT:
                  break;
              case VT_INT_PTR:
              case VT_UINT_PTR:
                  *(void**)var = NULL;
                  break;
              case VT_SAFEARRAY:
                  SafeArrayDestroy(var);
                  break;
              case VT_UNKNOWN:
              case VT_DISPATCH:
              {
                  IUnknown *unk = *(IUnknown**)var;
                  if (unk)
                      IUnknown_Release(unk);
                  *(void**)var = NULL;
                  break;
              }
              default:
                  FIXME("Not supported vt = %d\n", This->fields[i].vt);
                  break;
          }
      }
      
      return S_OK;
  }
  
  static HRESULT WINAPI IRecordInfoImpl_RecordCopy(IRecordInfo *iface, void *src_rec, void *dest_rec)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
      HRESULT hr = S_OK;
      int i;
  
      TRACE("(%p)->(%p %p)\n", This, src_rec, dest_rec);
  
      if(!src_rec || !dest_rec)
          return E_INVALIDARG;
  
      /* release already stored data */
      IRecordInfo_RecordClear(iface, dest_rec);
  
      for (i = 0; i < This->n_vars; i++)
      {
          void *src, *dest;
  
          if (This->fields[i].varkind != VAR_PERINSTANCE) {
              ERR("varkind != VAR_PERINSTANCE\n");
              continue;
          }
  
          src  = ((BYTE*)src_rec) + This->fields[i].offset;
          dest = ((BYTE*)dest_rec) + This->fields[i].offset;
          switch (This->fields[i].vt)
          {
              case VT_BSTR:
              {
                  BSTR src_str = *(BSTR*)src;
  
                  if (src_str)
                  {
                      BSTR str = SysAllocString(*(BSTR*)src);
                      if (!str) hr = E_OUTOFMEMORY;
  
                      *(BSTR*)dest = str;
                  }
                  else
                      *(BSTR*)dest = NULL;
                  break;
              }
              case VT_UNKNOWN:
              case VT_DISPATCH:
              {
                  IUnknown *unk = *(IUnknown**)src;
                  *(IUnknown**)dest = unk;
                  if (unk) IUnknown_AddRef(unk);
                  break;
              }
              case VT_SAFEARRAY:
                  hr = SafeArrayCopy(src, dest);
                  break;
              default:
              {
                  /* copy directly for types that don't need deep copy */
                  int len = get_type_size(NULL, This->fields[i].vt);
                  memcpy(dest, src, len);
                  break;
              }
          }
  
          if (FAILED(hr)) break;
      }
  
      if (FAILED(hr))
          IRecordInfo_RecordClear(iface, dest_rec);
  
      return hr;
  }
  
  static HRESULT WINAPI IRecordInfoImpl_GetGuid(IRecordInfo *iface, GUID *pguid)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
  
      TRACE("(%p)->(%p)\n", This, pguid);
  
      if(!pguid)
          return E_INVALIDARG;
  
      *pguid = This->guid;
      return S_OK;
  }
  
  static HRESULT WINAPI IRecordInfoImpl_GetName(IRecordInfo *iface, BSTR *pbstrName)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
  
      TRACE("(%p)->(%p)\n", This, pbstrName);
  
      if(!pbstrName)
          return E_INVALIDARG;
  
      *pbstrName = SysAllocString(This->name);
      return S_OK;
  }
  
  static HRESULT WINAPI IRecordInfoImpl_GetSize(IRecordInfo *iface, ULONG *pcbSize)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
  
      TRACE("(%p)->(%p)\n", This, pcbSize);
  
      if(!pcbSize)
          return E_INVALIDARG;
      
      *pcbSize = This->size;
      return S_OK;
  }
  
  static HRESULT WINAPI IRecordInfoImpl_GetTypeInfo(IRecordInfo *iface, ITypeInfo **ppTypeInfo)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
  
      TRACE("(%p)->(%p)\n", This, ppTypeInfo);
  
      if(!ppTypeInfo)
          return E_INVALIDARG;
  
      ITypeInfo_AddRef(This->pTypeInfo);
      *ppTypeInfo = This->pTypeInfo;
  
      return S_OK;
  }
  
  static HRESULT WINAPI IRecordInfoImpl_GetField(IRecordInfo *iface, PVOID pvData,
                                                  LPCOLESTR szFieldName, VARIANT *pvarField)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
      int i;
  
      TRACE("(%p)->(%p %s %p)\n", This, pvData, debugstr_w(szFieldName), pvarField);
  
      if(!pvData || !szFieldName || !pvarField)
          return E_INVALIDARG;
  
      for(i=0; i<This->n_vars; i++)
          if(!wcscmp(This->fields[i].name, szFieldName))
              break;
      if(i == This->n_vars)
          return TYPE_E_FIELDNOTFOUND;
      
      VariantClear(pvarField);
      return copy_to_variant(((PBYTE)pvData)+This->fields[i].offset, pvarField,
              This->fields[i].vt);
  }
  
  static HRESULT WINAPI IRecordInfoImpl_GetFieldNoCopy(IRecordInfo *iface, PVOID pvData,
                              LPCOLESTR szFieldName, VARIANT *pvarField, PVOID *ppvDataCArray)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
      int i;
  
      TRACE("(%p)->(%p %s %p %p)\n", This, pvData, debugstr_w(szFieldName), pvarField, ppvDataCArray);
  
      if(!pvData || !szFieldName || !pvarField)
          return E_INVALIDARG;
  
      for(i=0; i<This->n_vars; i++)
          if(!wcscmp(This->fields[i].name, szFieldName))
              break;
      if(i == This->n_vars)
          return TYPE_E_FIELDNOTFOUND;
  
      VariantClear(pvarField);
      V_VT(pvarField) = VT_BYREF|This->fields[i].vt;
      V_BYREF(pvarField) = ((PBYTE)pvData)+This->fields[i].offset;
      *ppvDataCArray = NULL;
      return S_OK;
  }
  
  static HRESULT WINAPI IRecordInfoImpl_PutField(IRecordInfo *iface, ULONG wFlags, PVOID pvData,
                                              LPCOLESTR szFieldName, VARIANT *pvarField)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
      int i;
  
      TRACE("(%p)->(%08x %p %s %p)\n", This, wFlags, pvData, debugstr_w(szFieldName),
                                      pvarField);
  
      if(!pvData || !szFieldName || !pvarField
              || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
          return E_INVALIDARG;
  
      if(wFlags == INVOKE_PROPERTYPUTREF) {
          FIXME("wFlag == INVOKE_PROPERTYPUTREF not supported\n");
          return E_NOTIMPL;
      }
  
      for(i=0; i<This->n_vars; i++)
          if(!wcscmp(This->fields[i].name, szFieldName))
              break;
      if(i == This->n_vars)
          return TYPE_E_FIELDNOTFOUND;
  
      return copy_from_variant(pvarField, ((PBYTE)pvData)+This->fields[i].offset,
              This->fields[i].vt);
  }
  
  static HRESULT WINAPI IRecordInfoImpl_PutFieldNoCopy(IRecordInfo *iface, ULONG wFlags,
                  PVOID pvData, LPCOLESTR szFieldName, VARIANT *pvarField)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
      int i;
  
      FIXME("(%p)->(%08x %p %s %p) stub\n", This, wFlags, pvData, debugstr_w(szFieldName), pvarField);
  
      if(!pvData || !szFieldName || !pvarField
              || (wFlags != INVOKE_PROPERTYPUTREF && wFlags != INVOKE_PROPERTYPUT))
          return E_INVALIDARG;
  
      for(i=0; i<This->n_vars; i++)
          if(!wcscmp(This->fields[i].name, szFieldName))
              break;
      if(i == This->n_vars)
          return TYPE_E_FIELDNOTFOUND;
  
      return E_NOTIMPL;
  }
  
  static HRESULT WINAPI IRecordInfoImpl_GetFieldNames(IRecordInfo *iface, ULONG *pcNames,
                                                  BSTR *rgBstrNames)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
      ULONG n = This->n_vars, i;
  
      TRACE("(%p)->(%p %p)\n", This, pcNames, rgBstrNames);
  
      if(!pcNames)
          return E_INVALIDARG;
  
      if(*pcNames < n)
          n =  *pcNames;
  
      if(rgBstrNames) {
          for(i=0; i<n; i++)
              rgBstrNames[i] = SysAllocString(This->fields[i].name);
      }
      
      *pcNames = n;
      return S_OK;
  }
  
  static BOOL WINAPI IRecordInfoImpl_IsMatchingType(IRecordInfo *iface, IRecordInfo *info2)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
      GUID guid2;
  
      TRACE( "(%p)->(%p)\n", This, info2 );
  
      IRecordInfo_GetGuid( info2, &guid2 );
      return IsEqualGUID( &This->guid, &guid2 );
  }
  
  static PVOID WINAPI IRecordInfoImpl_RecordCreate(IRecordInfo *iface)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
      void *record;
  
      TRACE("(%p)\n", This);
  
      record = HeapAlloc(GetProcessHeap(), 0, This->size);
      IRecordInfo_RecordInit(iface, record);
      TRACE("created record at %p\n", record);
      return record;
  }
  
  static HRESULT WINAPI IRecordInfoImpl_RecordCreateCopy(IRecordInfo *iface, PVOID pvSource,
                                                      PVOID *ppvDest)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
  
      TRACE("(%p)->(%p %p)\n", This, pvSource, ppvDest);
  
      if(!pvSource || !ppvDest)
          return E_INVALIDARG;
      
      *ppvDest = IRecordInfo_RecordCreate(iface);
      return IRecordInfo_RecordCopy(iface, pvSource, *ppvDest);
  }
  
  static HRESULT WINAPI IRecordInfoImpl_RecordDestroy(IRecordInfo *iface, PVOID pvRecord)
  {
      IRecordInfoImpl *This = impl_from_IRecordInfo(iface);
      HRESULT hres;
  
      TRACE("(%p)->(%p)\n", This, pvRecord);
  
      hres = IRecordInfo_RecordClear(iface, pvRecord);
      if(FAILED(hres))
          return hres;
  
      if(!HeapFree(GetProcessHeap(), 0, pvRecord))
          return E_INVALIDARG;
  
      return S_OK;
  }
  
  static const IRecordInfoVtbl IRecordInfoImplVtbl = {
      IRecordInfoImpl_QueryInterface,
      IRecordInfoImpl_AddRef,
      IRecordInfoImpl_Release,
      IRecordInfoImpl_RecordInit,
      IRecordInfoImpl_RecordClear,
      IRecordInfoImpl_RecordCopy,
      IRecordInfoImpl_GetGuid,
      IRecordInfoImpl_GetName,
      IRecordInfoImpl_GetSize,
      IRecordInfoImpl_GetTypeInfo,
      IRecordInfoImpl_GetField,
      IRecordInfoImpl_GetFieldNoCopy,
      IRecordInfoImpl_PutField,
      IRecordInfoImpl_PutFieldNoCopy,
      IRecordInfoImpl_GetFieldNames,
      IRecordInfoImpl_IsMatchingType,
      IRecordInfoImpl_RecordCreate,
      IRecordInfoImpl_RecordCreateCopy,
      IRecordInfoImpl_RecordDestroy
  };
  
  /******************************************************************************
   *      GetRecordInfoFromGuids  [OLEAUT32.322]
   *
   * RETURNS
   *  Success: S_OK
   *  Failure: E_INVALIDARG, if any argument is invalid.
   */
  HRESULT WINAPI GetRecordInfoFromGuids(REFGUID rGuidTypeLib, ULONG uVerMajor,
                          ULONG uVerMinor, LCID lcid, REFGUID rGuidTypeInfo, IRecordInfo** ppRecInfo)
  {
      ITypeInfo *pTypeInfo;
      ITypeLib *pTypeLib;
      HRESULT hres;
      
      TRACE("(%p,%d,%d,%d,%s,%p)\n", rGuidTypeLib, uVerMajor, uVerMinor,
              lcid, debugstr_guid(rGuidTypeInfo), ppRecInfo);
  
      hres = LoadRegTypeLib(rGuidTypeLib, uVerMajor, uVerMinor, lcid, &pTypeLib);
      if(FAILED(hres)) {
          WARN("LoadRegTypeLib failed!\n");
          return hres;
      }
  
      hres = ITypeLib_GetTypeInfoOfGuid(pTypeLib, rGuidTypeInfo, &pTypeInfo);
      ITypeLib_Release(pTypeLib);
      if(FAILED(hres)) {
          WARN("GetTypeInfoOfGuid failed!\n");
          return hres;
      }
  
      hres = GetRecordInfoFromTypeInfo(pTypeInfo, ppRecInfo);
      ITypeInfo_Release(pTypeInfo);
      return hres;
  }
  
  /******************************************************************************
   *      GetRecordInfoFromTypeInfo [OLEAUT32.332]
   */
  HRESULT WINAPI GetRecordInfoFromTypeInfo(ITypeInfo* pTI, IRecordInfo** ppRecInfo) {
      HRESULT hres;
      TYPEATTR *typeattr;
      IRecordInfoImpl *ret;
      ITypeInfo *pTypeInfo;
      int i;
      GUID guid;
  
      TRACE("(%p %p)\n", pTI, ppRecInfo);
  
      if(!pTI || !ppRecInfo)
          return E_INVALIDARG;
      
      hres = ITypeInfo_GetTypeAttr(pTI, &typeattr);
      if(FAILED(hres) || !typeattr) {
          WARN("GetTypeAttr failed: %08x\n", hres);
          return hres;
      }
  
      if(typeattr->typekind == TKIND_ALIAS) {
          hres = ITypeInfo_GetRefTypeInfo(pTI, typeattr->tdescAlias.u.hreftype, &pTypeInfo);
          guid = typeattr->guid;
          ITypeInfo_ReleaseTypeAttr(pTI, typeattr);
          if(FAILED(hres)) {
              WARN("GetRefTypeInfo failed: %08x\n", hres);
              return hres;
          }
          hres = ITypeInfo_GetTypeAttr(pTypeInfo, &typeattr);
          if(FAILED(hres)) {
              ITypeInfo_Release(pTypeInfo);
              WARN("GetTypeAttr failed for referenced type: %08x\n", hres);
              return hres;
          }
      }else  {
          pTypeInfo = pTI;
          ITypeInfo_AddRef(pTypeInfo);
          guid = typeattr->guid;
      }
  
      if(typeattr->typekind != TKIND_RECORD) {
          WARN("typekind != TKIND_RECORD\n");
          ITypeInfo_ReleaseTypeAttr(pTypeInfo, typeattr);
          ITypeInfo_Release(pTypeInfo);
          return E_INVALIDARG;
      }
  
      ret = HeapAlloc(GetProcessHeap(), 0, sizeof(*ret));
      ret->IRecordInfo_iface.lpVtbl = &IRecordInfoImplVtbl;
      ret->ref = 1;
      ret->pTypeInfo = pTypeInfo;
      ret->n_vars = typeattr->cVars;
      ret->size = typeattr->cbSizeInstance;
      ITypeInfo_ReleaseTypeAttr(pTypeInfo, typeattr);
  
      ret->guid = guid;
  
      /* NOTE: Windows implementation calls ITypeInfo::GetCantainingTypeLib and
       *       ITypeLib::GetLibAttr, but we currently don't need this.
       */
  
      hres = ITypeInfo_GetDocumentation(pTypeInfo, MEMBERID_NIL, &ret->name, NULL, NULL, NULL);
      if(FAILED(hres)) {
          WARN("ITypeInfo::GetDocumentation failed\n");
          ret->name = NULL;
      }
  
      ret->fields = HeapAlloc(GetProcessHeap(), 0, ret->n_vars*sizeof(fieldstr));
      for(i = 0; i<ret->n_vars; i++) {
          VARDESC *vardesc;
          hres = ITypeInfo_GetVarDesc(pTypeInfo, i, &vardesc);
          if(FAILED(hres)) {
              WARN("GetVarDesc failed\n");
              continue;
          }
          ret->fields[i].vt = vardesc->elemdescVar.tdesc.vt;
          ret->fields[i].varkind = vardesc->varkind;
          ret->fields[i].offset = vardesc->u.oInst;
          hres = ITypeInfo_GetDocumentation(pTypeInfo, vardesc->memid, &ret->fields[i].name,
                  NULL, NULL, NULL);
          if(FAILED(hres))
              WARN("GetDocumentation failed: %08x\n", hres);
          TRACE("field=%s, offset=%d\n", debugstr_w(ret->fields[i].name), ret->fields[i].offset);
          ITypeInfo_ReleaseVarDesc(pTypeInfo, vardesc);
      }
  
      *ppRecInfo = &ret->IRecordInfo_iface;
  
      return S_OK;
  }
  