| /* 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/list.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(oledb); | |
| struct error_record | |
| { | |
| IErrorLookup *lookup_service; | |
| ERRORINFO info; | |
| DISPPARAMS dispparams; | |
| IUnknown *custom_error; | |
| DWORD lookup_id; | |
| DWORD dynamic_id; | |
| }; | |
| typedef struct errorrecords | |
| { | |
| IErrorInfo IErrorInfo_iface; | |
| IErrorRecords IErrorRecords_iface; | |
| LONG ref; | |
| IErrorInfo *error_info; | |
| struct error_record *records; | |
| unsigned int allocated; | |
| unsigned int count; | |
| } errorrecords; | |
| enum error_info_flags | |
| { | |
| ERROR_INFO_HAS_DESCRIPTION = 0x1, | |
| ERROR_INFO_HAS_HELPINFO = 0x2, | |
| ERROR_INFO_NO_PARENT_REF = 0x4, | |
| }; | |
| struct error_info | |
| { | |
| IErrorInfo IErrorInfo_iface; | |
| LONG refcount; | |
| LCID lcid; | |
| unsigned int index; | |
| struct errorrecords *records; | |
| unsigned int flags; | |
| BSTR source; | |
| BSTR description; | |
| BSTR helpfile; | |
| DWORD helpcontext; | |
| }; | |
| static inline errorrecords *impl_records_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 inline struct error_info *impl_from_IErrorInfo( IErrorInfo *iface ) | |
| { | |
| return CONTAINING_RECORD(iface, struct error_info, IErrorInfo_iface); | |
| } | |
| static HRESULT create_error_info(errorrecords *records, unsigned int index, LCID lcid, | |
| unsigned int flags, IErrorInfo **out); | |
| static HRESULT return_bstr(BSTR src, BSTR *dst) | |
| { | |
| *dst = SysAllocStringLen(src, SysStringLen(src)); | |
| return *dst ? S_OK : E_OUTOFMEMORY; | |
| } | |
| static HRESULT errorrecords_get_record(errorrecords *records, unsigned int index, | |
| struct error_record **record) | |
| { | |
| *record = NULL; | |
| if (index >= records->count) | |
| return DB_E_BADRECORDNUM; | |
| *record = &records->records[records->count - index - 1]; | |
| return S_OK; | |
| } | |
| static HRESULT error_record_get_lookup_service(struct error_record *record, IErrorLookup **lookup_service) | |
| { | |
| WCHAR name[64], clsidW[39]; | |
| CLSID clsid; | |
| LSTATUS ret; | |
| DWORD size; | |
| HRESULT hr; | |
| HKEY hkey; | |
| if (!record->lookup_service) | |
| { | |
| StringFromGUID2(&record->info.clsid, clsidW, ARRAY_SIZE(clsidW)); | |
| wcscpy(name, L"CLSID\\"); | |
| wcscat(name, clsidW); | |
| wcscat(name, L"\\ExtendedErrors"); | |
| if (RegOpenKeyExW(HKEY_CLASSES_ROOT, name, 0, KEY_READ, &hkey)) | |
| return E_FAIL; | |
| size = ARRAY_SIZE(clsidW); | |
| ret = RegEnumKeyExW(hkey, 0, clsidW, &size, NULL, NULL, NULL, NULL); | |
| RegCloseKey(hkey); | |
| if (ret) | |
| return E_FAIL; | |
| if (FAILED(CLSIDFromString(clsidW, &clsid))) | |
| return E_FAIL; | |
| if (FAILED(hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER, &IID_IErrorLookup, | |
| (void **)&record->lookup_service))) | |
| { | |
| return hr; | |
| } | |
| } | |
| *lookup_service = record->lookup_service; | |
| IErrorLookup_AddRef(*lookup_service); | |
| return S_OK; | |
| } | |
| static HRESULT error_info_fetch_description(struct error_info *info) | |
| { | |
| IErrorLookup *lookup_service; | |
| struct error_record *record; | |
| BSTR source, description; | |
| HRESULT hr; | |
| if (FAILED(hr = errorrecords_get_record(info->records, info->index, &record))) | |
| return hr; | |
| if (FAILED(hr = error_record_get_lookup_service(record, &lookup_service))) | |
| return hr; | |
| hr = IErrorLookup_GetErrorDescription(lookup_service, record->info.hrError, record->lookup_id, | |
| &record->dispparams, info->lcid, &source, &description); | |
| IErrorLookup_Release(lookup_service); | |
| if (FAILED(hr)) | |
| return hr; | |
| info->source = source; | |
| info->description = description; | |
| info->flags |= ERROR_INFO_HAS_DESCRIPTION; | |
| return S_OK; | |
| } | |
| static HRESULT error_info_fetch_helpinfo(struct error_info *info) | |
| { | |
| IErrorLookup *lookup_service; | |
| struct error_record *record; | |
| DWORD helpcontext; | |
| BSTR helpfile; | |
| HRESULT hr; | |
| if (FAILED(hr = errorrecords_get_record(info->records, info->index, &record))) | |
| return hr; | |
| if (FAILED(hr = error_record_get_lookup_service(record, &lookup_service))) | |
| return hr; | |
| hr = IErrorLookup_GetHelpInfo(lookup_service, record->info.hrError, record->lookup_id, | |
| info->lcid, &helpfile, &helpcontext); | |
| IErrorLookup_Release(lookup_service); | |
| if (FAILED(hr)) | |
| return hr; | |
| info->helpfile = helpfile; | |
| info->helpcontext = helpcontext; | |
| info->flags |= ERROR_INFO_HAS_HELPINFO; | |
| return S_OK; | |
| } | |
| static HRESULT error_info_get_source(struct error_info *info, BSTR *source) | |
| { | |
| struct error_record *record; | |
| HRESULT hr; | |
| if (!source) | |
| return E_INVALIDARG; | |
| *source = NULL; | |
| if (FAILED(hr = errorrecords_get_record(info->records, info->index, &record))) | |
| return hr; | |
| if (record->lookup_id == IDENTIFIER_SDK_ERROR) | |
| return E_FAIL; | |
| if (info->flags & ERROR_INFO_HAS_DESCRIPTION) | |
| return return_bstr(info->source, source); | |
| if (FAILED(hr = error_info_fetch_description(info))) | |
| return hr; | |
| return return_bstr(info->source, source); | |
| } | |
| static HRESULT error_info_get_description(struct error_info *info, BSTR *description) | |
| { | |
| struct error_record *record; | |
| WCHAR str[32]; | |
| HRESULT hr; | |
| if (!description) | |
| return E_INVALIDARG; | |
| *description = NULL; | |
| if (FAILED(hr = errorrecords_get_record(info->records, info->index, &record))) | |
| return hr; | |
| if (record->lookup_id == IDENTIFIER_SDK_ERROR) | |
| { | |
| /* TODO: description should come from resources */ | |
| swprintf(str, ARRAY_SIZE(str), L"Return code %#lx.", record->info.hrError); | |
| *description = SysAllocString(str); | |
| return S_OK; | |
| } | |
| if (info->flags & ERROR_INFO_HAS_DESCRIPTION) | |
| return return_bstr(info->description, description); | |
| if (FAILED(hr = error_info_fetch_description(info))) | |
| return hr; | |
| return return_bstr(info->description, description); | |
| } | |
| static HRESULT error_info_get_helpfile(struct error_info *info, BSTR *helpfile) | |
| { | |
| struct error_record *record; | |
| HRESULT hr = S_OK; | |
| if (!helpfile) | |
| return E_INVALIDARG; | |
| *helpfile = NULL; | |
| if (FAILED(hr = errorrecords_get_record(info->records, info->index, &record))) | |
| return hr; | |
| if (record->lookup_id == IDENTIFIER_SDK_ERROR) | |
| return E_FAIL; | |
| if (!(info->flags & ERROR_INFO_HAS_HELPINFO)) | |
| hr = error_info_fetch_helpinfo(info); | |
| return SUCCEEDED(hr) ? return_bstr(info->helpfile, helpfile) : hr; | |
| } | |
| static HRESULT error_info_get_helpcontext(struct error_info *info, DWORD *helpcontext) | |
| { | |
| struct error_record *record; | |
| HRESULT hr; | |
| if (!helpcontext) | |
| return E_INVALIDARG; | |
| *helpcontext = 0; | |
| if (FAILED(hr = errorrecords_get_record(info->records, info->index, &record))) | |
| return hr; | |
| if (record->lookup_id == IDENTIFIER_SDK_ERROR) | |
| return E_FAIL; | |
| if (!(info->flags & ERROR_INFO_HAS_HELPINFO)) | |
| hr = error_info_fetch_helpinfo(info); | |
| if (SUCCEEDED(hr)) | |
| *helpcontext = info->helpcontext; | |
| return hr; | |
| } | |
| static HRESULT WINAPI errorrecords_QueryInterface(IErrorInfo* iface, REFIID riid, void **ppvoid) | |
| { | |
| errorrecords *records = impl_records_from_IErrorInfo(iface); | |
| TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), ppvoid); | |
| *ppvoid = NULL; | |
| if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IErrorInfo)) | |
| { | |
| *ppvoid = &records->IErrorInfo_iface; | |
| } | |
| else if (IsEqualIID(riid, &IID_IErrorRecords)) | |
| { | |
| *ppvoid = &records->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 *records = impl_records_from_IErrorInfo(iface); | |
| ULONG ref = InterlockedIncrement(&records->ref); | |
| TRACE("%p refcount %lu.\n", iface, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI errorrecords_Release(IErrorInfo* iface) | |
| { | |
| errorrecords *records = impl_records_from_IErrorInfo(iface); | |
| ULONG ref = InterlockedDecrement(&records->ref); | |
| TRACE("%p refcount %lu.\n", iface, ref); | |
| if (!ref) | |
| { | |
| unsigned int i; | |
| for (i = 0; i < records->count; i++) | |
| { | |
| struct error_record *record = &records->records[i]; | |
| unsigned int j; | |
| if (record->custom_error) | |
| IUnknown_Release(record->custom_error); | |
| for (j = 0; j < record->dispparams.cArgs && record->dispparams.rgvarg; j++) | |
| VariantClear(&record->dispparams.rgvarg[j]); | |
| CoTaskMemFree(record->dispparams.rgvarg); | |
| CoTaskMemFree(record->dispparams.rgdispidNamedArgs); | |
| if (record->lookup_service) | |
| { | |
| if (record->dynamic_id) | |
| IErrorLookup_ReleaseErrors(record->lookup_service, record->dynamic_id); | |
| IErrorLookup_Release(record->lookup_service); | |
| } | |
| } | |
| free(records->records); | |
| free(records); | |
| } | |
| return ref; | |
| } | |
| static HRESULT errorrecords_get_error_info(errorrecords *records, IErrorInfo **info) | |
| { | |
| HRESULT hr; | |
| *info = NULL; | |
| if (!records->count) | |
| return E_FAIL; | |
| if (!records->error_info) | |
| { | |
| if (FAILED(hr = create_error_info(records, 0, GetUserDefaultLCID(), ERROR_INFO_NO_PARENT_REF, &records->error_info))) | |
| return hr; | |
| } | |
| if (records->error_info) | |
| { | |
| *info = records->error_info; | |
| IErrorInfo_AddRef(*info); | |
| } | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI errorrecords_GetGUID(IErrorInfo* iface, GUID *guid) | |
| { | |
| errorrecords *records = impl_records_from_IErrorInfo(iface); | |
| IErrorInfo *error_info; | |
| HRESULT hr; | |
| TRACE("%p, %p.\n", iface, guid); | |
| if (!guid) | |
| return E_INVALIDARG; | |
| if (SUCCEEDED(errorrecords_get_error_info(records, &error_info))) | |
| { | |
| hr = IErrorInfo_GetGUID(error_info, guid); | |
| IErrorInfo_Release(error_info); | |
| return hr; | |
| } | |
| memset(guid, 0, sizeof(*guid)); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI errorrecords_GetSource(IErrorInfo* iface, BSTR *source) | |
| { | |
| errorrecords *records = impl_records_from_IErrorInfo(iface); | |
| IErrorInfo *error_info; | |
| HRESULT hr; | |
| TRACE("%p, %p.\n", iface, source); | |
| if (!source) | |
| return E_INVALIDARG; | |
| *source = NULL; | |
| if (SUCCEEDED(errorrecords_get_error_info(records, &error_info))) | |
| { | |
| hr = IErrorInfo_GetSource(error_info, source); | |
| IErrorInfo_Release(error_info); | |
| return hr; | |
| } | |
| return E_FAIL; | |
| } | |
| static HRESULT WINAPI errorrecords_GetDescription(IErrorInfo* iface, BSTR *description) | |
| { | |
| errorrecords *records = impl_records_from_IErrorInfo(iface); | |
| IErrorInfo *error_info; | |
| HRESULT hr; | |
| TRACE("%p, %p.\n", iface, description); | |
| if (!description) | |
| return E_INVALIDARG; | |
| *description = NULL; | |
| if (SUCCEEDED(errorrecords_get_error_info(records, &error_info))) | |
| { | |
| hr = IErrorInfo_GetDescription(error_info, description); | |
| IErrorInfo_Release(error_info); | |
| return hr; | |
| } | |
| return E_FAIL; | |
| } | |
| static HRESULT WINAPI errorrecords_GetHelpFile(IErrorInfo* iface, BSTR *helpfile) | |
| { | |
| errorrecords *records = impl_records_from_IErrorInfo(iface); | |
| IErrorInfo *error_info; | |
| HRESULT hr; | |
| TRACE("%p, %p.\n", iface, helpfile); | |
| if (!helpfile) | |
| return E_INVALIDARG; | |
| *helpfile = NULL; | |
| if (SUCCEEDED(errorrecords_get_error_info(records, &error_info))) | |
| { | |
| hr = IErrorInfo_GetHelpFile(error_info, helpfile); | |
| IErrorInfo_Release(error_info); | |
| return hr; | |
| } | |
| return E_FAIL; | |
| } | |
| static HRESULT WINAPI errorrecords_GetHelpContext(IErrorInfo* iface, DWORD *context) | |
| { | |
| errorrecords *records = impl_records_from_IErrorInfo(iface); | |
| IErrorInfo *error_info; | |
| HRESULT hr; | |
| TRACE("%p, %p.\n", iface, context); | |
| if (!context) | |
| return E_INVALIDARG; | |
| *context = 0; | |
| if (SUCCEEDED(errorrecords_get_error_info(records, &error_info))) | |
| { | |
| hr = IErrorInfo_GetHelpContext(error_info, context); | |
| IErrorInfo_Release(error_info); | |
| return hr; | |
| } | |
| 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 error_record *entry, *new_ptr; | |
| HRESULT hr; | |
| TRACE("(%p)->(%p %ld %p %p %ld)\n", This, pErrorInfo, dwLookupID, pdispparams, punkCustomError, dwDynamicErrorID); | |
| if(!pErrorInfo) | |
| return E_INVALIDARG; | |
| if (!This->records) | |
| { | |
| const unsigned int initial_size = 16; | |
| if (!(This->records = malloc(initial_size * sizeof(*This->records)))) | |
| return E_OUTOFMEMORY; | |
| This->allocated = initial_size; | |
| } | |
| else if (This->count == This->allocated) | |
| { | |
| new_ptr = 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->lookup_id = dwLookupID; | |
| entry->dynamic_id = dwDynamicErrorID; | |
| entry->lookup_service = NULL; | |
| This->count++; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI errorrec_GetBasicErrorInfo(IErrorRecords *iface, ULONG index, ERRORINFO *info) | |
| { | |
| errorrecords *records = impl_from_IErrorRecords(iface); | |
| struct error_record *record; | |
| HRESULT hr; | |
| TRACE("%p, %lu, %p.\n", iface, index, info); | |
| if (!info) | |
| return E_INVALIDARG; | |
| if (SUCCEEDED(hr = errorrecords_get_record(records, index, &record))) | |
| *info = record->info; | |
| return hr; | |
| } | |
| static HRESULT WINAPI errorrec_GetCustomErrorObject(IErrorRecords *iface, ULONG index, | |
| REFIID riid, IUnknown **object) | |
| { | |
| errorrecords *records = impl_from_IErrorRecords(iface); | |
| struct error_record *record; | |
| HRESULT hr; | |
| TRACE("%p, %lu, %s, %p.\n", iface, index, debugstr_guid(riid), object); | |
| if (!object) | |
| return E_INVALIDARG; | |
| *object = NULL; | |
| if (SUCCEEDED(hr = errorrecords_get_record(records, index, &record)) && record->custom_error) | |
| return IUnknown_QueryInterface(record->custom_error, riid, (void **)object); | |
| return hr; | |
| } | |
| static HRESULT WINAPI errorrec_GetErrorInfo(IErrorRecords *iface, ULONG index, | |
| LCID lcid, IErrorInfo **ppErrorInfo) | |
| { | |
| errorrecords *records = impl_from_IErrorRecords(iface); | |
| TRACE("%p, %lu, %ld, %p.\n", iface, index, lcid, ppErrorInfo); | |
| if (!ppErrorInfo) | |
| return E_INVALIDARG; | |
| if (index >= records->count) | |
| return DB_E_BADRECORDNUM; | |
| if (!index) | |
| return IErrorInfo_QueryInterface(&records->IErrorInfo_iface, &IID_IErrorInfo, (void **)ppErrorInfo); | |
| return create_error_info(records, index, lcid, 0, ppErrorInfo); | |
| } | |
| static HRESULT WINAPI errorrec_GetErrorParameters(IErrorRecords *iface, ULONG index, DISPPARAMS *pdispparams) | |
| { | |
| errorrecords *records = impl_from_IErrorRecords(iface); | |
| struct error_record *record; | |
| HRESULT hr; | |
| TRACE("%p, %lu, %p.\n", iface, index, pdispparams); | |
| if (!pdispparams) | |
| return E_INVALIDARG; | |
| if (SUCCEEDED(hr = errorrecords_get_record(records, index, &record))) | |
| return dup_dispparams(&record->dispparams, pdispparams); | |
| return hr; | |
| } | |
| 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("<--(%lu)\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_object(IUnknown *outer, void **obj) | |
| { | |
| errorrecords *This; | |
| TRACE("(%p, %p)\n", outer, obj); | |
| *obj = NULL; | |
| if(outer) return CLASS_E_NOAGGREGATION; | |
| This = calloc(1, sizeof(*This)); | |
| if(!This) return E_OUTOFMEMORY; | |
| This->IErrorInfo_iface.lpVtbl = &ErrorInfoVtbl; | |
| This->IErrorRecords_iface.lpVtbl = &ErrorRecordsVtbl; | |
| This->ref = 1; | |
| *obj = &This->IErrorInfo_iface; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI error_info_QueryInterface(IErrorInfo *iface, REFIID riid, void **obj) | |
| { | |
| TRACE("%p, %s, %p.\n", iface, debugstr_guid(riid), obj); | |
| if (IsEqualIID(riid, &IID_IErrorInfo) || IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| *obj = iface; | |
| IErrorInfo_AddRef(iface); | |
| return S_OK; | |
| } | |
| *obj = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI error_info_AddRef(IErrorInfo *iface) | |
| { | |
| struct error_info *error_info = impl_from_IErrorInfo(iface); | |
| LONG refcount = InterlockedIncrement(&error_info->refcount); | |
| TRACE("%p, %lu.\n", iface, refcount); | |
| return refcount; | |
| } | |
| static ULONG WINAPI error_info_Release(IErrorInfo *iface) | |
| { | |
| struct error_info *error_info = impl_from_IErrorInfo(iface); | |
| LONG refcount = InterlockedDecrement(&error_info->refcount); | |
| TRACE("%p, %lu.\n", iface, refcount); | |
| if (!refcount) | |
| { | |
| if (!(error_info->flags & ERROR_INFO_NO_PARENT_REF)) | |
| IErrorRecords_Release(&error_info->records->IErrorRecords_iface); | |
| SysFreeString(error_info->source); | |
| SysFreeString(error_info->description); | |
| SysFreeString(error_info->helpfile); | |
| free(error_info); | |
| } | |
| return refcount; | |
| } | |
| static HRESULT WINAPI error_info_GetGUID(IErrorInfo *iface, GUID *guid) | |
| { | |
| struct error_info *error_info = impl_from_IErrorInfo(iface); | |
| struct error_record *record; | |
| HRESULT hr; | |
| TRACE("%p, %p.\n", iface, guid); | |
| if (!guid) | |
| return E_INVALIDARG; | |
| if (FAILED(hr = errorrecords_get_record(error_info->records, error_info->index, &record))) | |
| return hr; | |
| *guid = record->info.iid; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI error_info_GetSource(IErrorInfo *iface, BSTR *source) | |
| { | |
| struct error_info *error_info = impl_from_IErrorInfo(iface); | |
| TRACE("%p, %p.\n", iface, source); | |
| return error_info_get_source(error_info, source); | |
| } | |
| static HRESULT WINAPI error_info_GetDescription(IErrorInfo *iface, BSTR *description) | |
| { | |
| struct error_info *error_info = impl_from_IErrorInfo(iface); | |
| TRACE("%p, %p.\n", iface, description); | |
| return error_info_get_description(error_info, description); | |
| } | |
| static HRESULT WINAPI error_info_GetHelpFile(IErrorInfo *iface, BSTR *helpfile) | |
| { | |
| struct error_info *error_info = impl_from_IErrorInfo(iface); | |
| TRACE("%p, %p.\n", iface, helpfile); | |
| return error_info_get_helpfile(error_info, helpfile); | |
| } | |
| static HRESULT WINAPI error_info_GetHelpContext(IErrorInfo *iface, DWORD *context) | |
| { | |
| struct error_info *error_info = impl_from_IErrorInfo(iface); | |
| TRACE("%p, %p.\n", iface, context); | |
| return error_info_get_helpcontext(error_info, context); | |
| } | |
| static const IErrorInfoVtbl error_info_vtbl = | |
| { | |
| error_info_QueryInterface, | |
| error_info_AddRef, | |
| error_info_Release, | |
| error_info_GetGUID, | |
| error_info_GetSource, | |
| error_info_GetDescription, | |
| error_info_GetHelpFile, | |
| error_info_GetHelpContext, | |
| }; | |
| static HRESULT create_error_info(errorrecords *records, unsigned int index, LCID lcid, | |
| unsigned int flags, IErrorInfo **out) | |
| { | |
| struct error_info *object; | |
| if (!(object = calloc(1, sizeof(*object)))) | |
| return E_OUTOFMEMORY; | |
| object->IErrorInfo_iface.lpVtbl = &error_info_vtbl; | |
| object->refcount = 1; | |
| object->lcid = lcid; | |
| object->records = records; | |
| if (!(flags & ERROR_INFO_NO_PARENT_REF)) | |
| IErrorRecords_AddRef(&records->IErrorRecords_iface); | |
| object->index = index; | |
| object->flags = flags; | |
| *out = &object->IErrorInfo_iface; | |
| return S_OK; | |
| } |