| /* OLE DB Row Position library | |
| * | |
| * Copyright 2013 Nikolay Sivov | |
| * | |
| * 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 | |
| */ | |
| #define COBJMACROS | |
| #include "windef.h" | |
| #include "ole2.h" | |
| #include "olectl.h" | |
| #include "oledb.h" | |
| #include "oledberr.h" | |
| #include "oledb_private.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(oledb); | |
| typedef struct rowpos rowpos; | |
| typedef struct | |
| { | |
| IConnectionPoint IConnectionPoint_iface; | |
| rowpos *container; | |
| IRowPositionChange **sinks; | |
| DWORD sinks_size; | |
| } rowpos_cp; | |
| struct rowpos | |
| { | |
| IRowPosition IRowPosition_iface; | |
| IConnectionPointContainer IConnectionPointContainer_iface; | |
| LONG ref; | |
| IRowset *rowset; | |
| IChapteredRowset *chrst; | |
| HROW row; | |
| HCHAPTER chapter; | |
| DBPOSITIONFLAGS flags; | |
| BOOL cleared; | |
| rowpos_cp cp; | |
| }; | |
| static void rowposchange_cp_destroy(rowpos_cp*); | |
| static inline rowpos *impl_from_IRowPosition(IRowPosition *iface) | |
| { | |
| return CONTAINING_RECORD(iface, rowpos, IRowPosition_iface); | |
| } | |
| static inline rowpos *impl_from_IConnectionPointContainer(IConnectionPointContainer *iface) | |
| { | |
| return CONTAINING_RECORD(iface, rowpos, IConnectionPointContainer_iface); | |
| } | |
| static inline rowpos_cp *impl_from_IConnectionPoint(IConnectionPoint *iface) | |
| { | |
| return CONTAINING_RECORD(iface, rowpos_cp, IConnectionPoint_iface); | |
| } | |
| static HRESULT rowpos_fireevent(rowpos *rp, DBREASON reason, DBEVENTPHASE phase) | |
| { | |
| BOOL cant_deny = phase == DBEVENTPHASE_FAILEDTODO || phase == DBEVENTPHASE_SYNCHAFTER; | |
| HRESULT hr = S_OK; | |
| DWORD i; | |
| for (i = 0; i < rp->cp.sinks_size; i++) | |
| if (rp->cp.sinks[i]) | |
| { | |
| hr = IRowPositionChange_OnRowPositionChange(rp->cp.sinks[i], reason, phase, cant_deny); | |
| if (phase == DBEVENTPHASE_FAILEDTODO) return DB_E_CANCELED; | |
| if (hr != S_OK) return hr; | |
| } | |
| return hr; | |
| } | |
| static void rowpos_clearposition(rowpos *rp) | |
| { | |
| if (!rp->cleared) | |
| { | |
| if (rp->rowset) | |
| IRowset_ReleaseRows(rp->rowset, 1, &rp->row, NULL, NULL, NULL); | |
| if (rp->chrst) | |
| IChapteredRowset_ReleaseChapter(rp->chrst, rp->chapter, NULL); | |
| } | |
| rp->row = DB_NULL_HROW; | |
| rp->chapter = DB_NULL_HCHAPTER; | |
| rp->flags = DBPOSITION_NOROW; | |
| } | |
| static HRESULT WINAPI rowpos_QueryInterface(IRowPosition* iface, REFIID riid, void **obj) | |
| { | |
| rowpos *This = impl_from_IRowPosition(iface); | |
| TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), obj); | |
| *obj = NULL; | |
| if (IsEqualIID(riid, &IID_IUnknown) || | |
| IsEqualIID(riid, &IID_IRowPosition)) | |
| { | |
| *obj = iface; | |
| } | |
| else if (IsEqualIID(riid, &IID_IConnectionPointContainer)) | |
| { | |
| *obj = &This->IConnectionPointContainer_iface; | |
| } | |
| else | |
| { | |
| FIXME("interface %s not implemented\n", debugstr_guid(riid)); | |
| return E_NOINTERFACE; | |
| } | |
| IRowPosition_AddRef(iface); | |
| return S_OK; | |
| } | |
| static ULONG WINAPI rowpos_AddRef(IRowPosition* iface) | |
| { | |
| rowpos *This = impl_from_IRowPosition(iface); | |
| ULONG ref = InterlockedIncrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| return ref; | |
| } | |
| static ULONG WINAPI rowpos_Release(IRowPosition* iface) | |
| { | |
| rowpos *This = impl_from_IRowPosition(iface); | |
| LONG ref = InterlockedDecrement(&This->ref); | |
| TRACE("(%p)->(%d)\n", This, ref); | |
| if (ref == 0) | |
| { | |
| if (This->rowset) IRowset_Release(This->rowset); | |
| if (This->chrst) IChapteredRowset_Release(This->chrst); | |
| rowposchange_cp_destroy(&This->cp); | |
| heap_free(This); | |
| } | |
| return ref; | |
| } | |
| static HRESULT WINAPI rowpos_ClearRowPosition(IRowPosition* iface) | |
| { | |
| rowpos *This = impl_from_IRowPosition(iface); | |
| HRESULT hr; | |
| TRACE("(%p)\n", This); | |
| if (!This->rowset) return E_UNEXPECTED; | |
| hr = rowpos_fireevent(This, DBREASON_ROWPOSITION_CLEARED, DBEVENTPHASE_OKTODO); | |
| if (hr != S_OK) | |
| return rowpos_fireevent(This, DBREASON_ROWPOSITION_CLEARED, DBEVENTPHASE_FAILEDTODO); | |
| hr = rowpos_fireevent(This, DBREASON_ROWPOSITION_CLEARED, DBEVENTPHASE_ABOUTTODO); | |
| if (hr != S_OK) | |
| return rowpos_fireevent(This, DBREASON_ROWPOSITION_CLEARED, DBEVENTPHASE_FAILEDTODO); | |
| rowpos_clearposition(This); | |
| This->cleared = TRUE; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI rowpos_GetRowPosition(IRowPosition *iface, HCHAPTER *chapter, | |
| HROW *row, DBPOSITIONFLAGS *flags) | |
| { | |
| rowpos *This = impl_from_IRowPosition(iface); | |
| TRACE("(%p)->(%p %p %p)\n", This, chapter, row, flags); | |
| *chapter = This->chapter; | |
| *row = This->row; | |
| *flags = This->flags; | |
| if (!This->rowset) return E_UNEXPECTED; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI rowpos_GetRowset(IRowPosition *iface, REFIID riid, IUnknown **rowset) | |
| { | |
| rowpos *This = impl_from_IRowPosition(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), rowset); | |
| if (!This->rowset) return E_UNEXPECTED; | |
| return IRowset_QueryInterface(This->rowset, riid, (void**)rowset); | |
| } | |
| static HRESULT WINAPI rowpos_Initialize(IRowPosition *iface, IUnknown *rowset) | |
| { | |
| rowpos *This = impl_from_IRowPosition(iface); | |
| HRESULT hr; | |
| TRACE("(%p)->(%p)\n", This, rowset); | |
| if (This->rowset) return DB_E_ALREADYINITIALIZED; | |
| hr = IUnknown_QueryInterface(rowset, &IID_IRowset, (void**)&This->rowset); | |
| if (FAILED(hr)) return hr; | |
| /* this one is optional */ | |
| IUnknown_QueryInterface(rowset, &IID_IChapteredRowset, (void**)&This->chrst); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI rowpos_SetRowPosition(IRowPosition *iface, HCHAPTER chapter, | |
| HROW row, DBPOSITIONFLAGS flags) | |
| { | |
| rowpos *This = impl_from_IRowPosition(iface); | |
| DBREASON reason; | |
| HRESULT hr; | |
| TRACE("(%p)->(%lx %lx %d)\n", This, chapter, row, flags); | |
| if (!This->cleared) return E_UNEXPECTED; | |
| hr = IRowset_AddRefRows(This->rowset, 1, &row, NULL, NULL); | |
| if (FAILED(hr)) return hr; | |
| if (This->chrst) | |
| { | |
| hr = IChapteredRowset_AddRefChapter(This->chrst, chapter, NULL); | |
| if (FAILED(hr)) | |
| { | |
| IRowset_ReleaseRows(This->rowset, 1, &row, NULL, NULL, NULL); | |
| return hr; | |
| } | |
| } | |
| reason = This->chrst ? DBREASON_ROWPOSITION_CHAPTERCHANGED : DBREASON_ROWPOSITION_CHANGED; | |
| hr = rowpos_fireevent(This, reason, DBEVENTPHASE_SYNCHAFTER); | |
| if (hr != S_OK) | |
| { | |
| IRowset_ReleaseRows(This->rowset, 1, &row, NULL, NULL, NULL); | |
| if (This->chrst) | |
| IChapteredRowset_ReleaseChapter(This->chrst, chapter, NULL); | |
| return rowpos_fireevent(This, reason, DBEVENTPHASE_FAILEDTODO); | |
| } | |
| else | |
| rowpos_fireevent(This, reason, DBEVENTPHASE_DIDEVENT); | |
| /* previously set chapter and row are released with ClearRowPosition() */ | |
| This->chapter = chapter; | |
| This->row = row; | |
| This->flags = flags; | |
| This->cleared = FALSE; | |
| return S_OK; | |
| } | |
| static const struct IRowPositionVtbl rowpos_vtbl = | |
| { | |
| rowpos_QueryInterface, | |
| rowpos_AddRef, | |
| rowpos_Release, | |
| rowpos_ClearRowPosition, | |
| rowpos_GetRowPosition, | |
| rowpos_GetRowset, | |
| rowpos_Initialize, | |
| rowpos_SetRowPosition | |
| }; | |
| static HRESULT WINAPI cpc_QueryInterface(IConnectionPointContainer *iface, REFIID riid, void **obj) | |
| { | |
| rowpos *This = impl_from_IConnectionPointContainer(iface); | |
| return IRowPosition_QueryInterface(&This->IRowPosition_iface, riid, obj); | |
| } | |
| static ULONG WINAPI cpc_AddRef(IConnectionPointContainer *iface) | |
| { | |
| rowpos *This = impl_from_IConnectionPointContainer(iface); | |
| return IRowPosition_AddRef(&This->IRowPosition_iface); | |
| } | |
| static ULONG WINAPI cpc_Release(IConnectionPointContainer *iface) | |
| { | |
| rowpos *This = impl_from_IConnectionPointContainer(iface); | |
| return IRowPosition_Release(&This->IRowPosition_iface); | |
| } | |
| static HRESULT WINAPI cpc_EnumConnectionPoints(IConnectionPointContainer *iface, IEnumConnectionPoints **enum_points) | |
| { | |
| rowpos *This = impl_from_IConnectionPointContainer(iface); | |
| FIXME("(%p)->(%p): stub\n", This, enum_points); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI cpc_FindConnectionPoint(IConnectionPointContainer *iface, REFIID riid, IConnectionPoint **point) | |
| { | |
| rowpos *This = impl_from_IConnectionPointContainer(iface); | |
| TRACE("(%p)->(%s %p)\n", This, debugstr_guid(riid), point); | |
| if (IsEqualIID(riid, &IID_IRowPositionChange)) | |
| { | |
| *point = &This->cp.IConnectionPoint_iface; | |
| IConnectionPoint_AddRef(*point); | |
| return S_OK; | |
| } | |
| else | |
| { | |
| FIXME("unsupported riid %s\n", debugstr_guid(riid)); | |
| return CONNECT_E_NOCONNECTION; | |
| } | |
| } | |
| static const struct IConnectionPointContainerVtbl rowpos_cpc_vtbl = | |
| { | |
| cpc_QueryInterface, | |
| cpc_AddRef, | |
| cpc_Release, | |
| cpc_EnumConnectionPoints, | |
| cpc_FindConnectionPoint | |
| }; | |
| static HRESULT WINAPI rowpos_cp_QueryInterface(IConnectionPoint *iface, REFIID riid, void **obj) | |
| { | |
| rowpos_cp *This = impl_from_IConnectionPoint(iface); | |
| return IConnectionPointContainer_QueryInterface(&This->container->IConnectionPointContainer_iface, riid, obj); | |
| } | |
| static ULONG WINAPI rowpos_cp_AddRef(IConnectionPoint *iface) | |
| { | |
| rowpos_cp *This = impl_from_IConnectionPoint(iface); | |
| return IConnectionPointContainer_AddRef(&This->container->IConnectionPointContainer_iface); | |
| } | |
| static ULONG WINAPI rowpos_cp_Release(IConnectionPoint *iface) | |
| { | |
| rowpos_cp *This = impl_from_IConnectionPoint(iface); | |
| return IConnectionPointContainer_Release(&This->container->IConnectionPointContainer_iface); | |
| } | |
| static HRESULT WINAPI rowpos_cp_GetConnectionInterface(IConnectionPoint *iface, IID *iid) | |
| { | |
| rowpos_cp *This = impl_from_IConnectionPoint(iface); | |
| TRACE("(%p)->(%p)\n", This, iid); | |
| if (!iid) return E_POINTER; | |
| *iid = IID_IRowPositionChange; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI rowpos_cp_GetConnectionPointContainer(IConnectionPoint *iface, IConnectionPointContainer **container) | |
| { | |
| rowpos_cp *This = impl_from_IConnectionPoint(iface); | |
| TRACE("(%p)->(%p)\n", This, container); | |
| if (!container) return E_POINTER; | |
| *container = &This->container->IConnectionPointContainer_iface; | |
| IConnectionPointContainer_AddRef(*container); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI rowpos_cp_Advise(IConnectionPoint *iface, IUnknown *unksink, DWORD *cookie) | |
| { | |
| rowpos_cp *This = impl_from_IConnectionPoint(iface); | |
| IRowPositionChange *sink; | |
| HRESULT hr; | |
| DWORD i; | |
| TRACE("(%p)->(%p %p)\n", This, unksink, cookie); | |
| if (!cookie) return E_POINTER; | |
| hr = IUnknown_QueryInterface(unksink, &IID_IRowPositionChange, (void**)&sink); | |
| if (FAILED(hr)) | |
| { | |
| FIXME("sink doesn't support IRowPositionChange\n"); | |
| return CONNECT_E_CANNOTCONNECT; | |
| } | |
| if (This->sinks) | |
| { | |
| for (i = 0; i < This->sinks_size; i++) | |
| { | |
| if (!This->sinks[i]) | |
| break; | |
| } | |
| if (i == This->sinks_size) | |
| { | |
| This->sinks_size *= 2; | |
| This->sinks = heap_realloc_zero(This->sinks, This->sinks_size*sizeof(*This->sinks)); | |
| } | |
| } | |
| else | |
| { | |
| This->sinks_size = 10; | |
| This->sinks = heap_alloc_zero(This->sinks_size*sizeof(*This->sinks)); | |
| i = 0; | |
| } | |
| This->sinks[i] = sink; | |
| if (cookie) *cookie = i + 1; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI rowpos_cp_Unadvise(IConnectionPoint *iface, DWORD cookie) | |
| { | |
| rowpos_cp *This = impl_from_IConnectionPoint(iface); | |
| TRACE("(%p)->(%d)\n", This, cookie); | |
| if (!cookie || cookie > This->sinks_size || !This->sinks[cookie-1]) | |
| return CONNECT_E_NOCONNECTION; | |
| IRowPositionChange_Release(This->sinks[cookie-1]); | |
| This->sinks[cookie-1] = NULL; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI rowpos_cp_EnumConnections(IConnectionPoint *iface, IEnumConnections **enum_c) | |
| { | |
| rowpos_cp *This = impl_from_IConnectionPoint(iface); | |
| FIXME("(%p)->(%p): stub\n", This, enum_c); | |
| return E_NOTIMPL; | |
| } | |
| static const struct IConnectionPointVtbl rowpos_cp_vtbl = | |
| { | |
| rowpos_cp_QueryInterface, | |
| rowpos_cp_AddRef, | |
| rowpos_cp_Release, | |
| rowpos_cp_GetConnectionInterface, | |
| rowpos_cp_GetConnectionPointContainer, | |
| rowpos_cp_Advise, | |
| rowpos_cp_Unadvise, | |
| rowpos_cp_EnumConnections | |
| }; | |
| static void rowposchange_cp_init(rowpos_cp *cp, rowpos *container) | |
| { | |
| cp->IConnectionPoint_iface.lpVtbl = &rowpos_cp_vtbl; | |
| cp->container = container; | |
| cp->sinks = NULL; | |
| cp->sinks_size = 0; | |
| } | |
| void rowposchange_cp_destroy(rowpos_cp *cp) | |
| { | |
| DWORD i; | |
| for (i = 0; i < cp->sinks_size; i++) | |
| { | |
| if (cp->sinks[i]) | |
| IRowPositionChange_Release(cp->sinks[i]); | |
| } | |
| heap_free(cp->sinks); | |
| } | |
| HRESULT create_oledb_rowpos(IUnknown *outer, void **obj) | |
| { | |
| rowpos *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->IRowPosition_iface.lpVtbl = &rowpos_vtbl; | |
| This->IConnectionPointContainer_iface.lpVtbl = &rowpos_cpc_vtbl; | |
| This->ref = 1; | |
| This->rowset = NULL; | |
| This->chrst = NULL; | |
| This->cleared = FALSE; | |
| rowpos_clearposition(This); | |
| rowposchange_cp_init(&This->cp, This); | |
| *obj = &This->IRowPosition_iface; | |
| return S_OK; | |
| } |