/* OLE DB ErrorInfo * * Copyright 2013 Alistair Leslie-Hughes * * 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 #include #define COBJMACROS #include "windef.h" #include "winbase.h" #include "objbase.h" #include "oleauto.h" #include "winerror.h" #include "oledb.h" #include "oledberr.h" #include "oledb_private.h" #include "wine/heap.h" #include "wine/list.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(oledb); struct ErrorEntry { ERRORINFO info; DISPPARAMS dispparams; IUnknown *custom_error; DWORD lookupID; }; typedef struct errorrecords { IErrorInfo IErrorInfo_iface; IErrorRecords IErrorRecords_iface; LONG ref; struct ErrorEntry *records; unsigned int allocated; unsigned int count; } errorrecords; static inline errorrecords *impl_from_IErrorInfo( IErrorInfo *iface ) { return CONTAINING_RECORD(iface, errorrecords, IErrorInfo_iface); } static inline errorrecords *impl_from_IErrorRecords( IErrorRecords *iface ) { return CONTAINING_RECORD(iface, errorrecords, IErrorRecords_iface); } static HRESULT WINAPI errorrecords_QueryInterface(IErrorInfo* iface, REFIID riid, void **ppvoid) { errorrecords *This = impl_from_IErrorInfo(iface); TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid),ppvoid); *ppvoid = NULL; if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IErrorInfo)) { *ppvoid = &This->IErrorInfo_iface; } else if (IsEqualIID(riid, &IID_IErrorRecords)) { *ppvoid = &This->IErrorRecords_iface; } if(*ppvoid) { IUnknown_AddRef( (IUnknown*)*ppvoid ); return S_OK; } FIXME("interface %s not implemented\n", debugstr_guid(riid)); return E_NOINTERFACE; } static ULONG WINAPI errorrecords_AddRef(IErrorInfo* iface) { errorrecords *This = impl_from_IErrorInfo(iface); TRACE("(%p)->%u\n",This,This->ref); return InterlockedIncrement(&This->ref); } static ULONG WINAPI errorrecords_Release(IErrorInfo* iface) { errorrecords *This = impl_from_IErrorInfo(iface); ULONG ref = InterlockedDecrement(&This->ref); TRACE("(%p)->%u\n",This,ref+1); if (!ref) { unsigned int i; for (i = 0; i < This->count; i++) { DISPPARAMS *dispparams = &This->records[i].dispparams; unsigned int j; if (This->records[i].custom_error) IUnknown_Release(This->records[i].custom_error); for (j = 0; j < dispparams->cArgs && dispparams->rgvarg; j++) VariantClear(&dispparams->rgvarg[i]); CoTaskMemFree(dispparams->rgvarg); CoTaskMemFree(dispparams->rgdispidNamedArgs); } heap_free(This->records); heap_free(This); } return ref; } static HRESULT WINAPI errorrecords_GetGUID(IErrorInfo* iface, GUID *guid) { errorrecords *This = impl_from_IErrorInfo(iface); TRACE("(%p)->(%p)\n", This, guid); if (!guid) return E_INVALIDARG; *guid = GUID_NULL; return S_OK; } static HRESULT WINAPI errorrecords_GetSource(IErrorInfo* iface, BSTR *source) { errorrecords *This = impl_from_IErrorInfo(iface); TRACE("(%p)->(%p)\n", This, source); if (!source) return E_INVALIDARG; *source = NULL; return E_FAIL; } static HRESULT WINAPI errorrecords_GetDescription(IErrorInfo* iface, BSTR *description) { errorrecords *This = impl_from_IErrorInfo(iface); TRACE("(%p)->(%p)\n", This, description); if (!description) return E_INVALIDARG; *description = NULL; return E_FAIL; } static HRESULT WINAPI errorrecords_GetHelpFile(IErrorInfo* iface, BSTR *helpfile) { errorrecords *This = impl_from_IErrorInfo(iface); TRACE("(%p)->(%p)\n", This, helpfile); if (!helpfile) return E_INVALIDARG; *helpfile = NULL; return E_FAIL; } static HRESULT WINAPI errorrecords_GetHelpContext(IErrorInfo* iface, DWORD *context) { errorrecords *This = impl_from_IErrorInfo(iface); TRACE("(%p)->(%p)\n", This, context); if (!context) return E_INVALIDARG; *context = 0; return E_FAIL; } static const IErrorInfoVtbl ErrorInfoVtbl = { errorrecords_QueryInterface, errorrecords_AddRef, errorrecords_Release, errorrecords_GetGUID, errorrecords_GetSource, errorrecords_GetDescription, errorrecords_GetHelpFile, errorrecords_GetHelpContext }; static HRESULT WINAPI errorrec_QueryInterface(IErrorRecords *iface, REFIID riid, void **ppvObject) { errorrecords *This = impl_from_IErrorRecords(iface); return IErrorInfo_QueryInterface(&This->IErrorInfo_iface, riid, ppvObject); } static ULONG WINAPI errorrec_AddRef(IErrorRecords *iface) { errorrecords *This = impl_from_IErrorRecords(iface); return IErrorInfo_AddRef(&This->IErrorInfo_iface); } static ULONG WINAPI errorrec_Release(IErrorRecords *iface) { errorrecords *This = impl_from_IErrorRecords(iface); return IErrorInfo_Release(&This->IErrorInfo_iface); } static HRESULT dup_dispparams(DISPPARAMS *src, DISPPARAMS *dest) { unsigned int i; if (!src) { memset(dest, 0, sizeof(*dest)); return S_OK; } *dest = *src; if (src->cArgs) { dest->rgvarg = CoTaskMemAlloc(dest->cArgs * sizeof(*dest->rgvarg)); for (i = 0; i < src->cArgs; i++) { VariantInit(&dest->rgvarg[i]); VariantCopy(&dest->rgvarg[i], &src->rgvarg[i]); } } if (src->cNamedArgs) { dest->rgdispidNamedArgs = CoTaskMemAlloc(dest->cNamedArgs * sizeof(*dest->rgdispidNamedArgs)); memcpy(dest->rgdispidNamedArgs, src->rgdispidNamedArgs, dest->cNamedArgs * sizeof(*dest->rgdispidNamedArgs)); } return S_OK; } static HRESULT WINAPI errorrec_AddErrorRecord(IErrorRecords *iface, ERRORINFO *pErrorInfo, DWORD dwLookupID, DISPPARAMS *pdispparams, IUnknown *punkCustomError, DWORD dwDynamicErrorID) { errorrecords *This = impl_from_IErrorRecords(iface); struct ErrorEntry *entry; HRESULT hr; TRACE("(%p)->(%p %d %p %p %d)\n", This, pErrorInfo, dwLookupID, pdispparams, punkCustomError, dwDynamicErrorID); if(!pErrorInfo) return E_INVALIDARG; if (!This->records) { const unsigned int initial_size = 16; if (!(This->records = heap_alloc(initial_size * sizeof(*This->records)))) return E_OUTOFMEMORY; This->allocated = initial_size; } else if (This->count == This->allocated) { struct ErrorEntry *new_ptr; new_ptr = heap_realloc(This->records, 2 * This->allocated * sizeof(*This->records)); if (!new_ptr) return E_OUTOFMEMORY; This->records = new_ptr; This->allocated *= 2; } entry = This->records + This->count; entry->info = *pErrorInfo; if (FAILED(hr = dup_dispparams(pdispparams, &entry->dispparams))) return hr; entry->custom_error = punkCustomError; if (entry->custom_error) IUnknown_AddRef(entry->custom_error); entry->lookupID = dwDynamicErrorID; This->count++; return S_OK; } static HRESULT WINAPI errorrec_GetBasicErrorInfo(IErrorRecords *iface, ULONG index, ERRORINFO *info) { errorrecords *This = impl_from_IErrorRecords(iface); TRACE("(%p)->(%u %p)\n", This, index, info); if (!info) return E_INVALIDARG; if (index >= This->count) return DB_E_BADRECORDNUM; index = This->count - index - 1; *info = This->records[index].info; return S_OK; } static HRESULT WINAPI errorrec_GetCustomErrorObject(IErrorRecords *iface, ULONG index, REFIID riid, IUnknown **object) { errorrecords *This = impl_from_IErrorRecords(iface); TRACE("(%p)->(%u %s %p)\n", This, index, debugstr_guid(riid), object); if (!object) return E_INVALIDARG; *object = NULL; if (index >= This->count) return DB_E_BADRECORDNUM; index = This->count - index - 1; if (This->records[index].custom_error) return IUnknown_QueryInterface(This->records[index].custom_error, riid, (void **)object); else return S_OK; } static HRESULT WINAPI errorrec_GetErrorInfo(IErrorRecords *iface, ULONG index, LCID lcid, IErrorInfo **ppErrorInfo) { errorrecords *This = impl_from_IErrorRecords(iface); FIXME("(%p)->(%u %d, %p)\n", This, index, lcid, ppErrorInfo); if (!ppErrorInfo) return E_INVALIDARG; if (index >= This->count) return DB_E_BADRECORDNUM; return E_NOTIMPL; } static HRESULT WINAPI errorrec_GetErrorParameters(IErrorRecords *iface, ULONG index, DISPPARAMS *pdispparams) { errorrecords *This = impl_from_IErrorRecords(iface); TRACE("(%p)->(%u %p)\n", This, index, pdispparams); if (!pdispparams) return E_INVALIDARG; if (index >= This->count) return DB_E_BADRECORDNUM; index = This->count - index - 1; return dup_dispparams(&This->records[index].dispparams, pdispparams); } static HRESULT WINAPI errorrec_GetRecordCount(IErrorRecords *iface, ULONG *count) { errorrecords *This = impl_from_IErrorRecords(iface); TRACE("(%p)->(%p)\n", This, count); if(!count) return E_INVALIDARG; *count = This->count; TRACE("<--(%u)\n", *count); return S_OK; } static const IErrorRecordsVtbl ErrorRecordsVtbl = { errorrec_QueryInterface, errorrec_AddRef, errorrec_Release, errorrec_AddErrorRecord, errorrec_GetBasicErrorInfo, errorrec_GetCustomErrorObject, errorrec_GetErrorInfo, errorrec_GetErrorParameters, errorrec_GetRecordCount }; HRESULT create_error_info(IUnknown *outer, void **obj) { errorrecords *This; TRACE("(%p, %p)\n", outer, obj); *obj = NULL; if(outer) return CLASS_E_NOAGGREGATION; This = heap_alloc(sizeof(*This)); if(!This) return E_OUTOFMEMORY; This->IErrorInfo_iface.lpVtbl = &ErrorInfoVtbl; This->IErrorRecords_iface.lpVtbl = &ErrorRecordsVtbl; This->ref = 1; This->records = NULL; This->allocated = 0; This->count = 0; *obj = &This->IErrorInfo_iface; return S_OK; }