| /* 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 <stdarg.h> | |
| #include <string.h> | |
| #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/unicode.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 WINAPI errorrec_AddRef(IErrorRecords *iface) | |
| { | |
| errorrecords *This = impl_from_IErrorRecords(iface); | |
| return IErrorInfo_AddRef(&This->IErrorInfo_iface); | |
| } | |
| static ULONG WINAPI 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; | |
| } |