/* * 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 #define COBJMACROS #define NONAMELESSUNION #define NONAMELESSSTRUCT #include "windef.h" #include "winbase.h" #include "objbase.h" #include "oaidl.h" #include "oleauto.h" #include "variant.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 { 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; in_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 = 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; in_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; in_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 = 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; in_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 = 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; in_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 = 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; in_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 = 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; ifields[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 ); if (IsEqualGUID( &This->guid, &guid2 )) return TRUE; FIXME( "records have different guids (%s %s) but could still match\n", debugstr_guid( &This->guid ), debugstr_guid( &guid2 ) ); return FALSE; } 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; } ITypeInfo_GetTypeAttr(pTypeInfo, &typeattr); }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; in_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; }