|  | /* | 
|  | * 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA | 
|  | */ | 
|  |  | 
|  | #include <stdarg.h> | 
|  |  | 
|  | #define COBJMACROS | 
|  | #define NONAMELESSUNION | 
|  | #define NONAMELESSSTRUCT | 
|  |  | 
|  | #include "windef.h" | 
|  | #include "winbase.h" | 
|  | #include "objbase.h" | 
|  | #include "oaidl.h" | 
|  | #include "oleauto.h" | 
|  |  | 
|  | #include "wine/unicode.h" | 
|  | #include "wine/debug.h" | 
|  |  | 
|  | WINE_DEFAULT_DEBUG_CHANNEL(ole); | 
|  |  | 
|  | typedef struct { | 
|  | enum VARENUM vt; | 
|  | VARKIND varkind; | 
|  | ULONG offset; | 
|  | BSTR name; | 
|  | } fieldstr; | 
|  |  | 
|  | typedef struct { | 
|  | const IRecordInfoVtbl *lpVtbl; | 
|  | LONG ref; | 
|  |  | 
|  | GUID guid; | 
|  | UINT lib_index; | 
|  | WORD n_vars; | 
|  | ULONG size; | 
|  | BSTR name; | 
|  | fieldstr *fields; | 
|  | ITypeInfo *pTypeInfo; | 
|  | } IRecordInfoImpl; | 
|  |  | 
|  | 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: \ | 
|  | V_ ## x(pvar) = *(typeof(V_ ## x(pvar))*)src; \ | 
|  | 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: \ | 
|  | *(typeof(V_ ## x(&var))*)dest = 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); | 
|  |  | 
|  | 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 = (IRecordInfoImpl*)iface; | 
|  | ULONG ref = InterlockedIncrement(&This->ref); | 
|  | TRACE("(%p) -> %ld\n", This, ref); | 
|  | return ref; | 
|  | } | 
|  |  | 
|  | static ULONG WINAPI IRecordInfoImpl_Release(IRecordInfo *iface) | 
|  | { | 
|  | IRecordInfoImpl *This = (IRecordInfoImpl*)iface; | 
|  | ULONG ref = InterlockedDecrement(&This->ref); | 
|  |  | 
|  | TRACE("(%p) -> %ld\n", This, ref); | 
|  |  | 
|  | if(!ref) { | 
|  | int i; | 
|  | for(i=0; i<This->n_vars; i++) | 
|  | SysFreeString(This->fields[i].name); | 
|  | HeapFree(GetProcessHeap(), 0, 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 = (IRecordInfoImpl*)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 = (IRecordInfoImpl*)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: | 
|  | /* NOTE: Windows implementatino reads DWORD (len) before string, | 
|  | *       but it seems to do nothing with this */ | 
|  | *(BSTR*)var = NULL; | 
|  | break; | 
|  | case VT_I2: | 
|  | case VT_I4: | 
|  | 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: | 
|  | break; | 
|  | case VT_INT_PTR: | 
|  | case VT_UINT_PTR: | 
|  | *(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, PVOID pvExisting, | 
|  | PVOID pvNew) | 
|  | { | 
|  | IRecordInfoImpl *This = (IRecordInfoImpl*)iface; | 
|  |  | 
|  | TRACE("(%p)->(%p %p)\n", This, pvExisting, pvNew); | 
|  |  | 
|  | if(!pvExisting || !pvNew) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | memcpy(pvExisting, pvNew, This->size); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IRecordInfoImpl_GetGuid(IRecordInfo *iface, GUID *pguid) | 
|  | { | 
|  | IRecordInfoImpl *This = (IRecordInfoImpl*)iface; | 
|  |  | 
|  | TRACE("(%p)->(%p)\n", This, pguid); | 
|  |  | 
|  | if(!pguid) | 
|  | return E_INVALIDARG; | 
|  |  | 
|  | memcpy(pguid, &This->guid, sizeof(GUID)); | 
|  | return S_OK; | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IRecordInfoImpl_GetName(IRecordInfo *iface, BSTR *pbstrName) | 
|  | { | 
|  | IRecordInfoImpl *This = (IRecordInfoImpl*)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 = (IRecordInfoImpl*)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 = (IRecordInfoImpl*)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 = (IRecordInfoImpl*)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(!strcmpW(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 = (IRecordInfoImpl*)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(!strcmpW(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 = (IRecordInfoImpl*)iface; | 
|  | int i; | 
|  |  | 
|  | TRACE("(%p)->(%08lx %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(!strcmpW(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 = (IRecordInfoImpl*)iface; | 
|  | int i; | 
|  |  | 
|  | FIXME("(%p)->(%08lx %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(!strcmpW(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 = (IRecordInfoImpl*)iface; | 
|  | ULONG n = This->n_vars; | 
|  | int 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 *pRecordInfo) | 
|  | { | 
|  | IRecordInfoImpl *This = (IRecordInfoImpl*)iface; | 
|  |  | 
|  | FIXME("(%p)->(%p) stub\n", This, pRecordInfo); | 
|  |  | 
|  | return FALSE; | 
|  | } | 
|  |  | 
|  | static PVOID WINAPI IRecordInfoImpl_RecordCreate(IRecordInfo *iface) | 
|  | { | 
|  | IRecordInfoImpl *This = (IRecordInfoImpl*)iface; | 
|  |  | 
|  | TRACE("(%p)\n", This); | 
|  |  | 
|  | return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->size); | 
|  | } | 
|  |  | 
|  | static HRESULT WINAPI IRecordInfoImpl_RecordCreateCopy(IRecordInfo *iface, PVOID pvSource, | 
|  | PVOID *ppvDest) | 
|  | { | 
|  | IRecordInfoImpl *This = (IRecordInfoImpl*)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 = (IRecordInfoImpl*)iface; | 
|  |  | 
|  | TRACE("(%p)->(%p)\n", This, pvRecord); | 
|  |  | 
|  | 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,%ld,%ld,%ld,%p,%p)\n", rGuidTypeLib, uVerMajor, uVerMinor, | 
|  | lcid, 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: %08lx\n", hres); | 
|  | return hres; | 
|  | } | 
|  |  | 
|  | if(typeattr->typekind == TKIND_ALIAS) { | 
|  | hres = ITypeInfo_GetRefTypeInfo(pTI, typeattr->tdescAlias.u.hreftype, &pTypeInfo); | 
|  | memcpy(&guid, &typeattr->guid, sizeof(GUID)); | 
|  | ITypeInfo_ReleaseTypeAttr(pTI, typeattr); | 
|  | if(FAILED(hres)) { | 
|  | WARN("GetRefTypeInfo failed: %08lx\n", hres); | 
|  | return hres; | 
|  | } | 
|  | ITypeInfo_GetTypeAttr(pTypeInfo, &typeattr); | 
|  | }else  { | 
|  | pTypeInfo = pTI; | 
|  | ITypeInfo_AddRef(pTypeInfo); | 
|  | memcpy(&guid, &typeattr->guid, sizeof(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->lpVtbl = &IRecordInfoImplVtbl; | 
|  | ret->ref = 1; | 
|  | ret->pTypeInfo = pTypeInfo; | 
|  | ret->n_vars = typeattr->cVars; | 
|  | ret->size = typeattr->cbSizeInstance; | 
|  | ITypeInfo_ReleaseTypeAttr(pTypeInfo, typeattr); | 
|  |  | 
|  | memcpy(&ret->guid, &guid, sizeof(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(VARDESC)); | 
|  | 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: %08lx\n", hres); | 
|  | ITypeInfo_ReleaseVarDesc(pTypeInfo, vardesc); | 
|  | } | 
|  |  | 
|  | *ppRecInfo = (IRecordInfo*)ret; | 
|  |  | 
|  | return S_OK; | 
|  | } |