/* 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" #include "wine/heap.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; *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; }