/* * Row and rowset servers / proxies. * * Copyright 2010 Huw Davies * * 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 #define NONAMELESSUNION #define NONAMELESSSTRUCT #include "windef.h" #include "winbase.h" #include "wingdi.h" #include "winuser.h" #include "winerror.h" #include "objbase.h" #include "oleauto.h" #include "oledb.h" #include "row_server.h" #include "wine/debug.h" WINE_DEFAULT_DEBUG_CHANNEL(oledb); static inline DBLENGTH db_type_size(DBTYPE type, DBLENGTH var_len) { switch(type) { case DBTYPE_I1: case DBTYPE_UI1: return 1; case DBTYPE_I2: case DBTYPE_UI2: return 2; case DBTYPE_I4: case DBTYPE_UI4: case DBTYPE_R4: return 4; case DBTYPE_I8: case DBTYPE_UI8: case DBTYPE_R8: return 8; case DBTYPE_CY: return sizeof(CY); case DBTYPE_FILETIME: return sizeof(FILETIME); case DBTYPE_BSTR: return sizeof(BSTR); case DBTYPE_GUID: return sizeof(GUID); case DBTYPE_WSTR: return var_len; default: FIXME("Unhandled type %04x\n", type); return 0; } } typedef struct { IWineRowServer IWineRowServer_iface; LONG ref; CLSID class; IMarshal *marshal; IUnknown *inner_unk; } server; static inline server *impl_from_IWineRowServer(IWineRowServer *iface) { return CONTAINING_RECORD(iface, server, IWineRowServer_iface); } static HRESULT WINAPI server_QueryInterface(IWineRowServer *iface, REFIID riid, void **obj) { server *This = impl_from_IWineRowServer(iface); TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(riid), obj); *obj = NULL; if(IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IWineRowServer)) { *obj = iface; } else { if(!IsEqualIID(riid, &IID_IMarshal)) /* We use standard marshalling */ FIXME("interface %s not implemented\n", debugstr_guid(riid)); return E_NOINTERFACE; } IWineRowServer_AddRef(iface); return S_OK; } static ULONG WINAPI server_AddRef(IWineRowServer *iface) { server *This = impl_from_IWineRowServer(iface); TRACE("(%p)\n", This); return InterlockedIncrement(&This->ref); } static ULONG WINAPI server_Release(IWineRowServer *iface) { server *This = impl_from_IWineRowServer(iface); LONG ref; TRACE("(%p)\n", This); ref = InterlockedDecrement(&This->ref); if(ref == 0) { IMarshal_Release(This->marshal); if(This->inner_unk) IUnknown_Release(This->inner_unk); HeapFree(GetProcessHeap(), 0, This); } return ref; } static HRESULT WINAPI server_SetInnerUnk(IWineRowServer *iface, IUnknown *inner) { server *This = impl_from_IWineRowServer(iface); if(This->inner_unk) IUnknown_Release(This->inner_unk); if(inner) IUnknown_AddRef(inner); This->inner_unk = inner; return S_OK; } static HRESULT WINAPI server_GetMarshal(IWineRowServer *iface, IMarshal **marshal) { server *This = impl_from_IWineRowServer(iface); IMarshal_AddRef(This->marshal); *marshal = This->marshal; return S_OK; } static HRESULT WINAPI server_GetColumns(IWineRowServer* iface, DBORDINAL num_cols, wine_getcolumns_in *in_data, wine_getcolumns_out *out_data) { server *This = impl_from_IWineRowServer(iface); HRESULT hr; DBORDINAL i; DBCOLUMNACCESS *cols; IRow *row; TRACE("(%p)->(%ld, %p, %p)\n", This, num_cols, in_data, out_data); hr = IUnknown_QueryInterface(This->inner_unk, &IID_IRow, (void**)&row); if(FAILED(hr)) return hr; cols = CoTaskMemAlloc(num_cols * sizeof(cols[0])); for(i = 0; i < num_cols; i++) { TRACE("%ld:\tmax_len %ld type %04x\n", i, in_data[i].max_len, in_data[i].type); cols[i].pData = CoTaskMemAlloc(db_type_size(in_data[i].type, in_data[i].max_len)); cols[i].columnid = in_data[i].columnid; cols[i].cbMaxLen = in_data[i].max_len; cols[i].wType = in_data[i].type; cols[i].bPrecision = in_data[i].precision; cols[i].bScale = in_data[i].scale; } hr = IRow_GetColumns(row, num_cols, cols); IRow_Release(row); for(i = 0; i < num_cols; i++) { VariantInit(&out_data[i].v); if(cols[i].dwStatus == DBSTATUS_S_OK) { V_VT(&out_data[i].v) = in_data[i].type; memcpy(&V_I1(&out_data[i].v), cols[i].pData, cols[i].cbDataLen); } CoTaskMemFree(cols[i].pData); out_data[i].data_len = cols[i].cbDataLen; out_data[i].status = cols[i].dwStatus; } CoTaskMemFree(cols); return hr; } static HRESULT WINAPI server_GetSourceRowset(IWineRowServer* iface, REFIID riid, IUnknown **ppRowset, HROW *phRow) { server *This = impl_from_IWineRowServer(iface); FIXME("(%p): stub\n", This); return E_NOTIMPL; } static HRESULT WINAPI server_Open(IWineRowServer* iface, IUnknown *pUnkOuter, DBID *pColumnID, REFGUID rguidColumnType, DWORD dwBindFlags, REFIID riid, IUnknown **ppUnk) { server *This = impl_from_IWineRowServer(iface); IRow *row; HRESULT hr; IWineRowServer *new_server; IMarshal *marshal; IUnknown *obj; TRACE("(%p)->(%p, %p, %s, %08x, %s, %p)\n", This, pUnkOuter, pColumnID, debugstr_guid(rguidColumnType), dwBindFlags, debugstr_guid(riid), ppUnk); *ppUnk = NULL; hr = IUnknown_QueryInterface(This->inner_unk, &IID_IRow, (void**)&row); if(FAILED(hr)) return hr; if(IsEqualGUID(rguidColumnType, &DBGUID_ROWSET)) hr = CoCreateInstance(&CLSID_wine_rowset_server, NULL, CLSCTX_INPROC_SERVER, &IID_IWineRowServer, (void**)&new_server); else { FIXME("Unhandled object %s\n", debugstr_guid(rguidColumnType)); hr = E_NOTIMPL; } if(FAILED(hr)) { IRow_Release(row); return hr; } IWineRowServer_GetMarshal(new_server, &marshal); hr = IRow_Open(row, (IUnknown*)marshal, pColumnID, rguidColumnType, dwBindFlags, &IID_IUnknown, &obj); IMarshal_Release(marshal); IRow_Release(row); if(FAILED(hr)) { IWineRowServer_Release(new_server); return hr; } IWineRowServer_SetInnerUnk(new_server, obj); hr = IUnknown_QueryInterface(obj, riid, (void**)ppUnk); IUnknown_Release(obj); TRACE("returning %08x\n", hr); return hr; } static HRESULT WINAPI server_SetColumns(IWineRowServer* iface, DBORDINAL num_cols, wine_setcolumns_in *in_data, DBSTATUS *status) { server *This = impl_from_IWineRowServer(iface); HRESULT hr; DBORDINAL i; DBCOLUMNACCESS *cols; IRowChange *row_change; TRACE("(%p)->(%ld, %p, %p)\n", This, num_cols, in_data, status); hr = IUnknown_QueryInterface(This->inner_unk, &IID_IRowChange, (void**)&row_change); if(FAILED(hr)) return hr; cols = CoTaskMemAlloc(num_cols * sizeof(cols[0])); for(i = 0; i < num_cols; i++) { TRACE("%ld:\ttype %04x\n", i, in_data[i].type); cols[i].pData = CoTaskMemAlloc(db_type_size(in_data[i].type, in_data[i].max_len)); memcpy(cols[i].pData, &V_I1(&in_data[i].v), db_type_size(in_data[i].type, in_data[i].max_len)); cols[i].columnid = in_data[i].columnid; cols[i].cbDataLen = in_data[i].data_len; cols[i].dwStatus = in_data[i].status; cols[i].cbMaxLen = in_data[i].max_len; cols[i].wType = in_data[i].type; cols[i].bPrecision = in_data[i].precision; cols[i].bScale = in_data[i].scale; } hr = IRowChange_SetColumns(row_change, num_cols, cols); IRowChange_Release(row_change); for(i = 0; i < num_cols; i++) { CoTaskMemFree(cols[i].pData); status[i] = cols[i].dwStatus; } CoTaskMemFree(cols); return hr; } static HRESULT WINAPI server_AddRefRows(IWineRowServer* iface, DBCOUNTITEM cRows, const HROW rghRows[], DBREFCOUNT rgRefCounts[], DBROWSTATUS rgRowStatus[]) { server *This = impl_from_IWineRowServer(iface); IRowset *rowset; HRESULT hr; TRACE("(%p)->(%ld, %p, %p, %p)\n", This, cRows, rghRows, rgRefCounts, rgRowStatus); hr = IUnknown_QueryInterface(This->inner_unk, &IID_IRowset, (void**)&rowset); if(FAILED(hr)) return hr; hr = IRowset_AddRefRows(rowset, cRows, rghRows, rgRefCounts, rgRowStatus); IRowset_Release(rowset); TRACE("returning %08x\n", hr); return hr; } static HRESULT WINAPI server_GetData(IWineRowServer* iface, HROW hRow, HACCESSOR hAccessor, BYTE *pData, DWORD size) { server *This = impl_from_IWineRowServer(iface); IRowset *rowset; HRESULT hr; TRACE("(%p)->(%08lx, %08lx, %p, %d)\n", This, hRow, hAccessor, pData, size); hr = IUnknown_QueryInterface(This->inner_unk, &IID_IRowset, (void**)&rowset); if(FAILED(hr)) return hr; hr = IRowset_GetData(rowset, hRow, hAccessor, pData); IRowset_Release(rowset); TRACE("returning %08x\n", hr); return hr; } static HRESULT WINAPI server_GetNextRows(IWineRowServer* iface, HCHAPTER hReserved, DBROWOFFSET lRowsOffset, DBROWCOUNT cRows, DBCOUNTITEM *pcRowObtained, HROW **prghRows) { server *This = impl_from_IWineRowServer(iface); IRowset *rowset; HRESULT hr; TRACE("(%p)->(%08lx, %ld, %ld, %p, %p)\n", This, hReserved, lRowsOffset, cRows, pcRowObtained, prghRows); hr = IUnknown_QueryInterface(This->inner_unk, &IID_IRowset, (void**)&rowset); if(FAILED(hr)) return hr; *prghRows = NULL; hr = IRowset_GetNextRows(rowset, hReserved, lRowsOffset, cRows, pcRowObtained, prghRows); IRowset_Release(rowset); TRACE("returning %08x, got %ld rows\n", hr, *pcRowObtained); return hr; } static HRESULT WINAPI server_ReleaseRows(IWineRowServer* iface, DBCOUNTITEM cRows, const HROW rghRows[], DBROWOPTIONS rgRowOptions[], DBREFCOUNT rgRefCounts[], DBROWSTATUS rgRowStatus[]) { server *This = impl_from_IWineRowServer(iface); IRowset *rowset; HRESULT hr; TRACE("(%p)->(%ld, %p, %p, %p, %p)\n", This, cRows, rghRows, rgRowOptions, rgRefCounts, rgRowStatus); hr = IUnknown_QueryInterface(This->inner_unk, &IID_IRowset, (void**)&rowset); if(FAILED(hr)) return hr; hr = IRowset_ReleaseRows(rowset, cRows, rghRows, rgRowOptions, rgRefCounts, rgRowStatus); IRowset_Release(rowset); TRACE("returning %08x\n", hr); return hr; } static HRESULT WINAPI server_RestartPosition(IWineRowServer* iface, HCHAPTER hReserved) { server *This = impl_from_IWineRowServer(iface); FIXME("(%p)->(%08lx): stub\n", This, hReserved); return E_NOTIMPL; } static HRESULT WINAPI server_Compare(IWineRowServer *iface, HCHAPTER hReserved, DBBKMARK cbBookmark1, const BYTE *pBookmark1, DBBKMARK cbBookmark2, const BYTE *pBookmark2, DBCOMPARE *pComparison) { server *This = impl_from_IWineRowServer(iface); FIXME("(%p): stub\n", This); return E_NOTIMPL; } static HRESULT WINAPI server_GetRowsAt(IWineRowServer *iface, HWATCHREGION hReserved1, HCHAPTER hReserved2, DBBKMARK cbBookmark, const BYTE *pBookmark, DBROWOFFSET lRowsOffset, DBROWCOUNT cRows, DBCOUNTITEM *pcRowsObtained, HROW **prghRows) { server *This = impl_from_IWineRowServer(iface); IRowsetLocate *rowsetlocate; HRESULT hr; TRACE("(%p)->(%08lx, %08lx, %ld, %p, %ld, %ld, %p, %p\n", This, hReserved1, hReserved2, cbBookmark, pBookmark, lRowsOffset, cRows, pcRowsObtained, prghRows); *prghRows = NULL; hr = IUnknown_QueryInterface(This->inner_unk, &IID_IRowsetLocate, (void**)&rowsetlocate); if(FAILED(hr)) return hr; hr = IRowsetLocate_GetRowsAt(rowsetlocate, hReserved1, hReserved2, cbBookmark, pBookmark, lRowsOffset, cRows, pcRowsObtained, prghRows); IRowsetLocate_Release(rowsetlocate); TRACE("returning %08x\n", hr); return hr; } static HRESULT WINAPI server_GetRowsByBookmark(IWineRowServer *iface, HCHAPTER hReserved, DBCOUNTITEM cRows, const DBBKMARK rgcbBookmarks[], const BYTE * rgpBookmarks[], HROW rghRows[], DBROWSTATUS rgRowStatus[]) { server *This = impl_from_IWineRowServer(iface); FIXME("(%p): stub\n", This); return E_NOTIMPL; } static HRESULT WINAPI server_Hash(IWineRowServer *iface, HCHAPTER hReserved, DBBKMARK cBookmarks, const DBBKMARK rgcbBookmarks[], const BYTE * rgpBookmarks[], DBHASHVALUE rgHashedValues[], DBROWSTATUS rgBookmarkStatus[]) { server *This = impl_from_IWineRowServer(iface); FIXME("(%p): stub\n", This); return E_NOTIMPL; } static HRESULT WINAPI server_GetProperties(IWineRowServer* iface, ULONG cPropertyIDSets, const DBPROPIDSET *rgPropertyIDSets, ULONG *pcPropertySets, DBPROPSET **prgPropertySets) { server *This = impl_from_IWineRowServer(iface); IRowsetInfo *rowsetinfo; HRESULT hr; TRACE("(%p)->(%d, %p, %p, %p)\n", This, cPropertyIDSets, rgPropertyIDSets, pcPropertySets, prgPropertySets); hr = IUnknown_QueryInterface(This->inner_unk, &IID_IRowsetInfo, (void**)&rowsetinfo); if(FAILED(hr)) return hr; hr = IRowsetInfo_GetProperties(rowsetinfo, cPropertyIDSets, rgPropertyIDSets, pcPropertySets, prgPropertySets); IRowsetInfo_Release(rowsetinfo); TRACE("returning %08x\n", hr); return hr; } static HRESULT WINAPI server_GetReferencedRowset(IWineRowServer* iface, DBORDINAL iOrdinal, REFIID riid, IUnknown **ppReferencedRowset) { server *This = impl_from_IWineRowServer(iface); FIXME("(%p): stub\n", This); return E_NOTIMPL; } static HRESULT WINAPI server_GetSpecification(IWineRowServer* iface, REFIID riid, IUnknown **ppSpecification) { server *This = impl_from_IWineRowServer(iface); FIXME("(%p): stub\n", This); return E_NOTIMPL; } static HRESULT WINAPI server_AddRefAccessor(IWineRowServer* iface, HACCESSOR hAccessor, DBREFCOUNT *pcRefCount) { server *This = impl_from_IWineRowServer(iface); FIXME("(%p): stub\n", This); return E_NOTIMPL; } static HRESULT WINAPI server_CreateAccessor(IWineRowServer* iface, DBACCESSORFLAGS dwAccessorFlags, DBCOUNTITEM cBindings, const DBBINDING *rgBindings, DBLENGTH cbRowSize, HACCESSOR *phAccessor, DBBINDSTATUS *rgStatus) { server *This = impl_from_IWineRowServer(iface); HRESULT hr; IAccessor *accessor; TRACE("(%p)->(%08x, %ld, %p, %ld, %p, %p)\n", This, dwAccessorFlags, cBindings, rgBindings, cbRowSize, phAccessor, rgStatus); hr = IUnknown_QueryInterface(This->inner_unk, &IID_IAccessor, (void**)&accessor); if(FAILED(hr)) return hr; hr = IAccessor_CreateAccessor(accessor, dwAccessorFlags, cBindings, rgBindings, cbRowSize, phAccessor, rgStatus); IAccessor_Release(accessor); TRACE("returning %08x, accessor %08lx\n", hr, *phAccessor); return hr; } static HRESULT WINAPI server_GetBindings(IWineRowServer* iface, HACCESSOR hAccessor, DBACCESSORFLAGS *pdwAccessorFlags, DBCOUNTITEM *pcBindings, DBBINDING **prgBindings) { server *This = impl_from_IWineRowServer(iface); HRESULT hr; IAccessor *accessor; TRACE("(%p)->(%08lx, %p, %p, %p)\n", This, hAccessor, pdwAccessorFlags, pcBindings, prgBindings); hr = IUnknown_QueryInterface(This->inner_unk, &IID_IAccessor, (void**)&accessor); if(FAILED(hr)) return hr; hr = IAccessor_GetBindings(accessor, hAccessor, pdwAccessorFlags, pcBindings, prgBindings); IAccessor_Release(accessor); TRACE("returning %08x\n", hr); return hr; } static HRESULT WINAPI server_ReleaseAccessor(IWineRowServer* iface, HACCESSOR hAccessor, DBREFCOUNT *pcRefCount) { server *This = impl_from_IWineRowServer(iface); HRESULT hr; IAccessor *accessor; TRACE("(%p)->(%08lx, %p)\n", This, hAccessor, pcRefCount); hr = IUnknown_QueryInterface(This->inner_unk, &IID_IAccessor, (void**)&accessor); if(FAILED(hr)) return hr; hr = IAccessor_ReleaseAccessor(accessor, hAccessor, pcRefCount); IAccessor_Release(accessor); return hr; } static const IWineRowServerVtbl server_vtbl = { server_QueryInterface, server_AddRef, server_Release, server_SetInnerUnk, server_GetMarshal, server_GetColumns, server_GetSourceRowset, server_Open, server_SetColumns, server_AddRefRows, server_GetData, server_GetNextRows, server_ReleaseRows, server_RestartPosition, server_Compare, server_GetRowsAt, server_GetRowsByBookmark, server_Hash, server_GetProperties, server_GetReferencedRowset, server_GetSpecification, server_AddRefAccessor, server_CreateAccessor, server_GetBindings, server_ReleaseAccessor }; static HRESULT create_server(IUnknown *outer, const CLSID *class, void **obj) { server *server; TRACE("(%p, %s, %p)\n", outer, debugstr_guid(class), obj); *obj = NULL; server = HeapAlloc(GetProcessHeap(), 0, sizeof(*server)); if(!server) return E_OUTOFMEMORY; server->IWineRowServer_iface.lpVtbl = &server_vtbl; server->ref = 1; server->class = *class; server->inner_unk = NULL; if(IsEqualGUID(class, &CLSID_wine_row_server)) create_row_marshal((IUnknown*)server, (void**)&server->marshal); else if(IsEqualGUID(class, &CLSID_wine_rowset_server)) create_rowset_marshal((IUnknown*)server, (void**)&server->marshal); else ERR("create_server called with class %s\n", debugstr_guid(class)); *obj = server; return S_OK; } HRESULT create_row_server(IUnknown *outer, void **obj) { return create_server(outer, &CLSID_wine_row_server, obj); } HRESULT create_rowset_server(IUnknown *outer, void **obj) { return create_server(outer, &CLSID_wine_rowset_server, obj); } typedef struct { IRow IRow_iface; IRowChange IRowChange_iface; LONG ref; IWineRowServer *server; } row_proxy; static inline row_proxy *impl_from_IRow(IRow *iface) { return CONTAINING_RECORD(iface, row_proxy, IRow_iface); } static inline row_proxy *impl_from_IRowChange(IRowChange *iface) { return CONTAINING_RECORD(iface, row_proxy, IRowChange_iface); } static HRESULT WINAPI row_QueryInterface(IRow *iface, REFIID iid, void **obj) { row_proxy *This = impl_from_IRow(iface); TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(iid), obj); if(IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IRow)) { *obj = &This->IRow_iface; } else if(IsEqualIID(iid, &IID_IRowChange)) { *obj = &This->IRowChange_iface; } else { FIXME("interface %s not implemented\n", debugstr_guid(iid)); return E_NOINTERFACE; } IRow_AddRef(iface); return S_OK; } static ULONG WINAPI row_AddRef(IRow *iface) { row_proxy *This = impl_from_IRow(iface); TRACE("(%p)\n", This); return InterlockedIncrement(&This->ref); } static ULONG WINAPI row_Release(IRow *iface) { row_proxy *This = impl_from_IRow(iface); LONG ref; TRACE("(%p)\n", This); ref = InterlockedDecrement(&This->ref); if(ref == 0) { if(This->server) IWineRowServer_Release(This->server); HeapFree(GetProcessHeap(), 0, This); } return ref; } static HRESULT WINAPI row_GetColumns(IRow* iface, DBORDINAL cColumns, DBCOLUMNACCESS rgColumns[]) { row_proxy *This = impl_from_IRow(iface); DBORDINAL i; wine_getcolumns_in *in_data; wine_getcolumns_out *out_data; HRESULT hr; TRACE("(%p)->(%ld, %p)\n", This, cColumns, rgColumns); in_data = CoTaskMemAlloc(cColumns * sizeof(in_data[0])); out_data = CoTaskMemAlloc(cColumns * sizeof(out_data[0])); for(i = 0; i < cColumns; i++) { TRACE("%ld:\tdata %p data_len %ld status %08x max_len %ld type %04x\n", i, rgColumns[i].pData, rgColumns[i].cbDataLen, rgColumns[i].dwStatus, rgColumns[i].cbMaxLen, rgColumns[i].wType); in_data[i].columnid = rgColumns[i].columnid; in_data[i].max_len = rgColumns[i].cbMaxLen; in_data[i].type = rgColumns[i].wType; in_data[i].precision = rgColumns[i].bPrecision; in_data[i].scale = rgColumns[i].bScale; } hr = IWineRowServer_GetColumns(This->server, cColumns, in_data, out_data); for(i = 0; i < cColumns; i++) { rgColumns[i].cbDataLen = out_data[i].data_len; rgColumns[i].dwStatus = out_data[i].status; if(rgColumns[i].dwStatus == DBSTATUS_S_OK) memcpy(rgColumns[i].pData, &V_I1(&out_data[i].v), out_data[i].data_len); } CoTaskMemFree(out_data); CoTaskMemFree(in_data); return hr; } static HRESULT WINAPI row_GetSourceRowset(IRow* iface, REFIID riid, IUnknown **ppRowset, HROW *phRow) { row_proxy *This = impl_from_IRow(iface); FIXME("(%p)->(%s, %p, %p): stub\n", This, debugstr_guid(riid), ppRowset, phRow); return E_NOTIMPL; } static HRESULT WINAPI row_Open(IRow* iface, IUnknown *pUnkOuter, DBID *pColumnID, REFGUID rguidColumnType, DWORD dwBindFlags, REFIID riid, IUnknown **ppUnk) { row_proxy *This = impl_from_IRow(iface); TRACE("(%p)->(%p, %p, %s, %08x, %s, %p)\n", This, pUnkOuter, pColumnID, debugstr_guid(rguidColumnType), dwBindFlags, debugstr_guid(riid), ppUnk); if(pUnkOuter) { FIXME("Aggregation not supported\n"); return CLASS_E_NOAGGREGATION; } return IWineRowServer_Open(This->server, pUnkOuter, pColumnID, rguidColumnType, dwBindFlags, riid, ppUnk); } static const IRowVtbl row_vtbl = { row_QueryInterface, row_AddRef, row_Release, row_GetColumns, row_GetSourceRowset, row_Open }; static HRESULT WINAPI row_change_QueryInterface(IRowChange *iface, REFIID iid, void **obj) { row_proxy *This = impl_from_IRowChange(iface); return IUnknown_QueryInterface((IUnknown *)This, iid, obj); } static ULONG WINAPI row_change_AddRef(IRowChange *iface) { row_proxy *This = impl_from_IRowChange(iface); return IUnknown_AddRef((IUnknown*)This); } static ULONG WINAPI row_change_Release(IRowChange *iface) { row_proxy *This = impl_from_IRowChange(iface); return IUnknown_Release((IUnknown*)This); } static HRESULT WINAPI row_change_SetColumns(IRowChange *iface, DBORDINAL cColumns, DBCOLUMNACCESS rgColumns[]) { row_proxy *This = impl_from_IRowChange(iface); HRESULT hr; wine_setcolumns_in *in_data; DBSTATUS *status; DBORDINAL i; TRACE("(%p)->(%ld, %p)\n", This, cColumns, rgColumns); in_data = CoTaskMemAlloc(cColumns * sizeof(in_data[0])); status = CoTaskMemAlloc(cColumns * sizeof(status[0])); for(i = 0; i < cColumns; i++) { TRACE("%ld: wtype %04x max %08lx len %08lx\n", i, rgColumns[i].wType, rgColumns[i].cbMaxLen, rgColumns[i].cbDataLen); V_VT(&in_data[i].v) = rgColumns[i].wType; memcpy(&V_I1(&in_data[i].v), rgColumns[i].pData, db_type_size(rgColumns[i].wType, rgColumns[i].cbDataLen)); in_data[i].columnid = rgColumns[i].columnid; in_data[i].data_len = rgColumns[i].cbDataLen; in_data[i].status = rgColumns[i].dwStatus; in_data[i].max_len = rgColumns[i].cbMaxLen; in_data[i].type = rgColumns[i].wType; in_data[i].precision = rgColumns[i].bPrecision; in_data[i].scale = rgColumns[i].bScale; } hr = IWineRowServer_SetColumns(This->server, cColumns, in_data, status); for(i = 0; i < cColumns; i++) rgColumns[i].dwStatus = status[i]; CoTaskMemFree(status); CoTaskMemFree(in_data); return hr; } static const IRowChangeVtbl row_change_vtbl = { row_change_QueryInterface, row_change_AddRef, row_change_Release, row_change_SetColumns }; static HRESULT create_row_proxy(IWineRowServer *server, IUnknown **obj) { row_proxy *proxy; TRACE("(%p, %p)\n", server, obj); *obj = NULL; proxy = HeapAlloc(GetProcessHeap(), 0, sizeof(*proxy)); if(!proxy) return E_OUTOFMEMORY; proxy->IRow_iface.lpVtbl = &row_vtbl; proxy->IRowChange_iface.lpVtbl = &row_change_vtbl; proxy->ref = 1; IWineRowServer_AddRef(server); proxy->server = server; *obj = (IUnknown*)&proxy->IRow_iface; TRACE("returning %p\n", *obj); return S_OK; } typedef struct { IRowsetLocate IRowsetLocate_iface; IRowsetInfo IRowsetInfo_iface; IAccessor IAccessor_iface; LONG ref; IWineRowServer *server; } rowset_proxy; static inline rowset_proxy *impl_from_IRowsetLocate(IRowsetLocate *iface) { return CONTAINING_RECORD(iface, rowset_proxy, IRowsetLocate_iface); } static inline rowset_proxy *impl_from_IRowsetInfo(IRowsetInfo *iface) { return CONTAINING_RECORD(iface, rowset_proxy, IRowsetInfo_iface); } static inline rowset_proxy *impl_from_IAccessor(IAccessor *iface) { return CONTAINING_RECORD(iface, rowset_proxy, IAccessor_iface); } static HRESULT WINAPI rowsetlocate_QueryInterface(IRowsetLocate *iface, REFIID iid, void **obj) { rowset_proxy *This = impl_from_IRowsetLocate(iface); TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(iid), obj); *obj = NULL; if(IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IRowset) || IsEqualIID(iid, &IID_IRowsetLocate)) { *obj = &This->IRowsetLocate_iface; } else if(IsEqualIID(iid, &IID_IRowsetInfo)) { *obj = &This->IRowsetInfo_iface; } else if(IsEqualIID(iid, &IID_IAccessor)) { *obj = &This->IAccessor_iface; } else { FIXME("interface %s not implemented\n", debugstr_guid(iid)); return E_NOINTERFACE; } IRowsetLocate_AddRef(iface); return S_OK; } static ULONG WINAPI rowsetlocate_AddRef(IRowsetLocate *iface) { rowset_proxy *This = impl_from_IRowsetLocate(iface); TRACE("(%p)\n", This); return InterlockedIncrement(&This->ref); } static ULONG WINAPI rowsetlocate_Release(IRowsetLocate *iface) { rowset_proxy *This = impl_from_IRowsetLocate(iface); LONG ref; TRACE("(%p)\n", This); ref = InterlockedDecrement(&This->ref); if(ref == 0) { if(This->server) IWineRowServer_Release(This->server); HeapFree(GetProcessHeap(), 0, This); } return ref; } static HRESULT WINAPI rowsetlocate_AddRefRows(IRowsetLocate *iface, DBCOUNTITEM cRows, const HROW rghRows[], DBREFCOUNT rgRefCounts[], DBROWSTATUS rgRowStatus[]) { rowset_proxy *This = impl_from_IRowsetLocate(iface); HRESULT hr; DBREFCOUNT *refs = rgRefCounts; DBSTATUS *stats = rgRowStatus; TRACE("(%p)->(%ld, %p, %p, %p)\n", This, cRows, rghRows, rgRefCounts, rgRowStatus); if(!refs) refs = CoTaskMemAlloc(cRows * sizeof(refs[0])); if(!stats) stats = CoTaskMemAlloc(cRows * sizeof(stats[0])); hr = IWineRowServer_AddRefRows(This->server, cRows, rghRows, refs, stats); if(refs != rgRefCounts) CoTaskMemFree(refs); if(stats != rgRowStatus) CoTaskMemFree(stats); return hr; } static HRESULT WINAPI rowsetlocate_GetData(IRowsetLocate *iface, HROW hRow, HACCESSOR hAccessor, void *pData) { rowset_proxy *This = impl_from_IRowsetLocate(iface); HRESULT hr; IAccessor *accessor; DBACCESSORFLAGS flags; DBCOUNTITEM count, i; DBBINDING *bindings; DWORD max_len = 0; TRACE("(%p)->(%lx, %lx, %p)\n", This, hRow, hAccessor, pData); hr = IRowsetLocate_QueryInterface(iface, &IID_IAccessor, (void**)&accessor); if(FAILED(hr)) return hr; hr = IAccessor_GetBindings(accessor, hAccessor, &flags, &count, &bindings); IAccessor_Release(accessor); if(FAILED(hr)) return hr; TRACE("got %ld bindings\n", count); for(i = 0; i < count; i++) { TRACE("%ld\tord %ld offs: val %ld len %ld stat %ld, part %x, max len %ld type %04x\n", i, bindings[i].iOrdinal, bindings[i].obValue, bindings[i].obLength, bindings[i].obStatus, bindings[i].dwPart, bindings[i].cbMaxLen, bindings[i].wType); if(bindings[i].dwPart & DBPART_LENGTH && bindings[i].obLength >= max_len) max_len = bindings[i].obLength + sizeof(DBLENGTH); if(bindings[i].dwPart & DBPART_STATUS && bindings[i].obStatus >= max_len) max_len = bindings[i].obStatus + sizeof(DWORD); if(bindings[i].dwPart & DBPART_VALUE && bindings[i].obValue >= max_len) max_len = bindings[i].obValue + db_type_size(bindings[i].wType, bindings[i].cbMaxLen); } TRACE("max_len %d\n", max_len); CoTaskMemFree(bindings); hr = IWineRowServer_GetData(This->server, hRow, hAccessor, pData, max_len); return hr; } static HRESULT WINAPI rowsetlocate_GetNextRows(IRowsetLocate *iface, HCHAPTER hReserved, DBROWOFFSET lRowsOffset, DBROWCOUNT cRows, DBCOUNTITEM *pcRowObtained, HROW **prghRows) { rowset_proxy *This = impl_from_IRowsetLocate(iface); HRESULT hr; HROW *rows = NULL; TRACE("(%p)->(%08lx, %ld, %ld, %p, %p)\n", This, hReserved, lRowsOffset, cRows, pcRowObtained, prghRows); hr = IWineRowServer_GetNextRows(This->server, hReserved, lRowsOffset, cRows, pcRowObtained, &rows); if(*prghRows) { memcpy(*prghRows, rows, *pcRowObtained * sizeof(rows[0])); CoTaskMemFree(rows); } else *prghRows = rows; return hr; } static HRESULT WINAPI rowsetlocate_ReleaseRows(IRowsetLocate *iface, DBCOUNTITEM cRows, const HROW rghRows[], DBROWOPTIONS rgRowOptions[], DBREFCOUNT rgRefCounts[], DBROWSTATUS rgRowStatus[]) { rowset_proxy *This = impl_from_IRowsetLocate(iface); HRESULT hr; DBROWOPTIONS *options = rgRowOptions; DBREFCOUNT *refs = rgRefCounts; DBROWSTATUS *status = rgRowStatus; TRACE("(%p)->(%ld, %p, %p, %p, %p)\n", This, cRows, rghRows, rgRowOptions, rgRefCounts, rgRowStatus); if(!options) { options = CoTaskMemAlloc(cRows * sizeof(options[0])); memset(options, 0, cRows * sizeof(options[0])); } if(!refs) refs = CoTaskMemAlloc(cRows * sizeof(refs[0])); if(!status) status = CoTaskMemAlloc(cRows * sizeof(status[0])); hr = IWineRowServer_ReleaseRows(This->server, cRows, rghRows, options, refs, status); if(status != rgRowStatus) CoTaskMemFree(status); if(refs != rgRefCounts) CoTaskMemFree(refs); if(options != rgRowOptions) CoTaskMemFree(options); return hr; } static HRESULT WINAPI rowsetlocate_RestartPosition(IRowsetLocate* iface, HCHAPTER hReserved) { rowset_proxy *This = impl_from_IRowsetLocate(iface); FIXME("(%p)->(%lx): stub\n", This, hReserved); return E_NOTIMPL; } static HRESULT WINAPI rowsetlocate_Compare(IRowsetLocate *iface, HCHAPTER hReserved, DBBKMARK cbBookmark1, const BYTE *pBookmark1, DBBKMARK cbBookmark2, const BYTE *pBookmark2, DBCOMPARE *pComparison) { rowset_proxy *This = impl_from_IRowsetLocate(iface); FIXME("(%p)\n", This); return E_NOTIMPL; } static HRESULT WINAPI rowsetlocate_GetRowsAt(IRowsetLocate *iface, HWATCHREGION hReserved1, HCHAPTER hReserved2, DBBKMARK cbBookmark, const BYTE *pBookmark, DBROWOFFSET lRowsOffset, DBROWCOUNT cRows, DBCOUNTITEM *pcRowsObtained, HROW **prghRows) { rowset_proxy *This = impl_from_IRowsetLocate(iface); HRESULT hr; HROW *rows = NULL; TRACE("(%p)->(%08lx, %08lx, %ld, %p, %ld, %ld, %p, %p\n", This, hReserved1, hReserved2, cbBookmark, pBookmark, lRowsOffset, cRows, pcRowsObtained, prghRows); hr = IWineRowServer_GetRowsAt(This->server, hReserved1, hReserved2, cbBookmark, pBookmark, lRowsOffset, cRows, pcRowsObtained, &rows); if(*prghRows) { memcpy(*prghRows, rows, *pcRowsObtained * sizeof(rows[0])); CoTaskMemFree(rows); } else *prghRows = rows; return hr; } static HRESULT WINAPI rowsetlocate_GetRowsByBookmark(IRowsetLocate *iface, HCHAPTER hReserved, DBCOUNTITEM cRows, const DBBKMARK rgcbBookmarks[], const BYTE * rgpBookmarks[], HROW rghRows[], DBROWSTATUS rgRowStatus[]) { rowset_proxy *This = impl_from_IRowsetLocate(iface); FIXME("(%p)\n", This); return E_NOTIMPL; } static HRESULT WINAPI rowsetlocate_Hash(IRowsetLocate *iface, HCHAPTER hReserved, DBBKMARK cBookmarks, const DBBKMARK rgcbBookmarks[], const BYTE * rgpBookmarks[], DBHASHVALUE rgHashedValues[], DBROWSTATUS rgBookmarkStatus[]) { rowset_proxy *This = impl_from_IRowsetLocate(iface); FIXME("(%p)\n", This); return E_NOTIMPL; } static const IRowsetLocateVtbl rowsetlocate_vtbl = { rowsetlocate_QueryInterface, rowsetlocate_AddRef, rowsetlocate_Release, rowsetlocate_AddRefRows, rowsetlocate_GetData, rowsetlocate_GetNextRows, rowsetlocate_ReleaseRows, rowsetlocate_RestartPosition, rowsetlocate_Compare, rowsetlocate_GetRowsAt, rowsetlocate_GetRowsByBookmark, rowsetlocate_Hash }; static HRESULT WINAPI rowsetinfo_QueryInterface(IRowsetInfo *iface, REFIID iid, void **obj) { rowset_proxy *This = impl_from_IRowsetInfo(iface); return IUnknown_QueryInterface((IUnknown *)This, iid, obj); } static ULONG WINAPI rowsetinfo_AddRef(IRowsetInfo *iface) { rowset_proxy *This = impl_from_IRowsetInfo(iface); return IUnknown_AddRef((IUnknown *)This); } static ULONG WINAPI rowsetinfo_Release(IRowsetInfo *iface) { rowset_proxy *This = impl_from_IRowsetInfo(iface); return IUnknown_Release((IUnknown *)This); } static HRESULT WINAPI rowsetinfo_GetProperties(IRowsetInfo *iface, const ULONG cPropertyIDSets, const DBPROPIDSET rgPropertyIDSets[], ULONG *pcPropertySets, DBPROPSET **prgPropertySets) { rowset_proxy *This = impl_from_IRowsetInfo(iface); HRESULT hr; TRACE("(%p)->(%d, %p, %p, %p)\n", This, cPropertyIDSets, rgPropertyIDSets, pcPropertySets, prgPropertySets); hr = IWineRowServer_GetProperties(This->server, cPropertyIDSets, rgPropertyIDSets, pcPropertySets, prgPropertySets); return hr; } static HRESULT WINAPI rowsetinfo_GetReferencedRowset(IRowsetInfo *iface, DBORDINAL iOrdinal, REFIID riid, IUnknown **ppReferencedRowset) { rowset_proxy *This = impl_from_IRowsetInfo(iface); FIXME("(%p)\n", This); return E_NOTIMPL; } static HRESULT WINAPI rowsetinfo_GetSpecification(IRowsetInfo *iface, REFIID riid, IUnknown **ppSpecification) { rowset_proxy *This = impl_from_IRowsetInfo(iface); FIXME("(%p)\n", This); return E_NOTIMPL; } static const IRowsetInfoVtbl rowsetinfo_vtbl = { rowsetinfo_QueryInterface, rowsetinfo_AddRef, rowsetinfo_Release, rowsetinfo_GetProperties, rowsetinfo_GetReferencedRowset, rowsetinfo_GetSpecification }; static HRESULT WINAPI accessor_QueryInterface(IAccessor *iface, REFIID iid, void **obj) { rowset_proxy *This = impl_from_IAccessor(iface); return IUnknown_QueryInterface((IUnknown *)This, iid, obj); } static ULONG WINAPI accessor_AddRef(IAccessor *iface) { rowset_proxy *This = impl_from_IAccessor(iface); return IUnknown_AddRef((IUnknown *)This); } static ULONG WINAPI accessor_Release(IAccessor *iface) { rowset_proxy *This = impl_from_IAccessor(iface); return IUnknown_Release((IUnknown *)This); } static HRESULT WINAPI accessor_AddRefAccessor(IAccessor *iface, HACCESSOR hAccessor, DBREFCOUNT *pcRefCount) { rowset_proxy *This = impl_from_IAccessor(iface); FIXME("(%p)\n", This); return E_NOTIMPL; } static HRESULT WINAPI accessor_CreateAccessor(IAccessor *iface, DBACCESSORFLAGS dwAccessorFlags, DBCOUNTITEM cBindings, const DBBINDING rgBindings[], DBLENGTH cbRowSize, HACCESSOR *phAccessor, DBBINDSTATUS rgStatus[]) { rowset_proxy *This = impl_from_IAccessor(iface); HRESULT hr; DBBINDSTATUS *status; TRACE("(%p)->(%08x, %ld, %p, %ld, %p, %p)\n", This, dwAccessorFlags, cBindings, rgBindings, cbRowSize, phAccessor, rgStatus); if(!rgStatus) status = CoTaskMemAlloc(cBindings * sizeof(status[0])); else status = rgStatus; hr = IWineRowServer_CreateAccessor(This->server, dwAccessorFlags, cBindings, rgBindings, cbRowSize, phAccessor, status); if(!rgStatus) CoTaskMemFree(status); return hr; } static HRESULT WINAPI accessor_GetBindings(IAccessor *iface, HACCESSOR hAccessor, DBACCESSORFLAGS *pdwAccessorFlags, DBCOUNTITEM *pcBindings, DBBINDING **prgBindings) { rowset_proxy *This = impl_from_IAccessor(iface); HRESULT hr; TRACE("(%p)->(%08lx, %p, %p, %p)\n", This, hAccessor, pdwAccessorFlags, pcBindings, prgBindings); hr = IWineRowServer_GetBindings(This->server, hAccessor, pdwAccessorFlags, pcBindings, prgBindings); return hr; } static HRESULT WINAPI accessor_ReleaseAccessor(IAccessor *iface, HACCESSOR hAccessor, DBREFCOUNT *pcRefCount) { rowset_proxy *This = impl_from_IAccessor(iface); HRESULT hr; DBREFCOUNT ref; TRACE("(%p)->(%08lx, %p)\n", This, hAccessor, pcRefCount); hr = IWineRowServer_ReleaseAccessor(This->server, hAccessor, &ref); if(pcRefCount) *pcRefCount = ref; return hr; } static const IAccessorVtbl accessor_vtbl = { accessor_QueryInterface, accessor_AddRef, accessor_Release, accessor_AddRefAccessor, accessor_CreateAccessor, accessor_GetBindings, accessor_ReleaseAccessor }; static HRESULT create_rowset_proxy(IWineRowServer *server, IUnknown **obj) { rowset_proxy *proxy; TRACE("(%p, %p)\n", server, obj); *obj = NULL; proxy = HeapAlloc(GetProcessHeap(), 0, sizeof(*proxy)); if(!proxy) return E_OUTOFMEMORY; proxy->IRowsetLocate_iface.lpVtbl = &rowsetlocate_vtbl; proxy->IRowsetInfo_iface.lpVtbl = &rowsetinfo_vtbl; proxy->IAccessor_iface.lpVtbl = &accessor_vtbl; proxy->ref = 1; IWineRowServer_AddRef(server); proxy->server = server; *obj = (IUnknown *)&proxy->IRowsetLocate_iface; TRACE("returning %p\n", *obj); return S_OK; } static HRESULT create_proxy(IWineRowServer *server, const CLSID *class, IUnknown **obj) { *obj = NULL; if(IsEqualGUID(class, &CLSID_wine_row_proxy)) return create_row_proxy(server, obj); else if(IsEqualGUID(class, &CLSID_wine_rowset_proxy)) return create_rowset_proxy(server, obj); else FIXME("Unhandled proxy class %s\n", debugstr_guid(class)); return E_NOTIMPL; } /* Marshal impl */ typedef struct { IMarshal IMarshal_iface; LONG ref; CLSID unmarshal_class; IUnknown *outer; } marshal; static inline marshal *impl_from_IMarshal(IMarshal *iface) { return CONTAINING_RECORD(iface, marshal, IMarshal_iface); } static HRESULT WINAPI marshal_QueryInterface(IMarshal *iface, REFIID iid, void **obj) { marshal *This = impl_from_IMarshal(iface); TRACE("(%p)->(%s, %p)\n", This, debugstr_guid(iid), obj); if(IsEqualIID(iid, &IID_IUnknown) || IsEqualIID(iid, &IID_IMarshal)) { *obj = iface; } else { FIXME("interface %s not implemented\n", debugstr_guid(iid)); *obj = NULL; return E_NOINTERFACE; } IMarshal_AddRef(iface); return S_OK; } static ULONG WINAPI marshal_AddRef(IMarshal *iface) { marshal *This = impl_from_IMarshal(iface); TRACE("(%p)\n", This); return InterlockedIncrement(&This->ref); } static ULONG WINAPI marshal_Release(IMarshal *iface) { marshal *This = impl_from_IMarshal(iface); LONG ref; TRACE("(%p)\n", This); ref = InterlockedDecrement(&This->ref); if(ref == 0) { HeapFree(GetProcessHeap(), 0, This); } return ref; } static HRESULT WINAPI marshal_GetUnmarshalClass(IMarshal *iface, REFIID iid, void *obj, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *clsid) { marshal *This = impl_from_IMarshal(iface); TRACE("(%p)->(%s, %p, %08x, %p, %08x, %p)\n", This, debugstr_guid(iid), obj, dwDestContext, pvDestContext, mshlflags, clsid); *clsid = This->unmarshal_class; return S_OK; } static HRESULT WINAPI marshal_GetMarshalSizeMax(IMarshal *iface, REFIID iid, void *obj, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, DWORD *size) { marshal *This = impl_from_IMarshal(iface); TRACE("(%p)->(%s, %p, %08x, %p, %08x, %p)\n", This, debugstr_guid(iid), obj, dwDestContext, pvDestContext, mshlflags, size); return CoGetMarshalSizeMax(size, &IID_IWineRowServer, This->outer, dwDestContext, pvDestContext, mshlflags); } static HRESULT WINAPI marshal_MarshalInterface(IMarshal *iface, IStream *stream, REFIID iid, void *obj, DWORD dwDestContext, void *pvDestContext, DWORD mshlflags) { marshal *This = impl_from_IMarshal(iface); TRACE("(%p)->(%p, %s, %p, %08x, %p, %08x)\n", This, stream, debugstr_guid(iid), obj, dwDestContext, pvDestContext, mshlflags); return CoMarshalInterface(stream, &IID_IWineRowServer, This->outer, dwDestContext, pvDestContext, mshlflags); } static HRESULT WINAPI marshal_UnmarshalInterface(IMarshal *iface, IStream *stream, REFIID iid, void **obj) { marshal *This = impl_from_IMarshal(iface); HRESULT hr; IWineRowServer *server; IUnknown *proxy; TRACE("(%p)->(%p, %s, %p)\n", This, stream, debugstr_guid(iid), obj); *obj = NULL; hr = CoUnmarshalInterface(stream, &IID_IWineRowServer, (void**)&server); if(SUCCEEDED(hr)) { hr = create_proxy(server, &This->unmarshal_class, &proxy); if(SUCCEEDED(hr)) { hr = IUnknown_QueryInterface(proxy, iid, obj); IUnknown_Release(proxy); } IWineRowServer_Release(server); } TRACE("returning %p\n", *obj); return hr; } static HRESULT WINAPI marshal_ReleaseMarshalData(IMarshal *iface, IStream *stream) { marshal *This = impl_from_IMarshal(iface); TRACE("(%p)->(%p)\n", This, stream); return CoReleaseMarshalData(stream); } static HRESULT WINAPI marshal_DisconnectObject(IMarshal *iface, DWORD dwReserved) { marshal *This = impl_from_IMarshal(iface); FIXME("(%p)->(%08x)\n", This, dwReserved); return E_NOTIMPL; } static const IMarshalVtbl marshal_vtbl = { marshal_QueryInterface, marshal_AddRef, marshal_Release, marshal_GetUnmarshalClass, marshal_GetMarshalSizeMax, marshal_MarshalInterface, marshal_UnmarshalInterface, marshal_ReleaseMarshalData, marshal_DisconnectObject }; static HRESULT create_marshal(IUnknown *outer, const CLSID *class, void **obj) { marshal *marshal; TRACE("(%p, %p)\n", outer, obj); *obj = NULL; marshal = HeapAlloc(GetProcessHeap(), 0, sizeof(*marshal)); if(!marshal) return E_OUTOFMEMORY; marshal->unmarshal_class = *class; marshal->outer = outer; /* don't ref outer unk */ marshal->IMarshal_iface.lpVtbl = &marshal_vtbl; marshal->ref = 1; *obj = &marshal->IMarshal_iface; TRACE("returning %p\n", *obj); return S_OK; } HRESULT create_row_marshal(IUnknown *outer, void **obj) { TRACE("(%p, %p)\n", outer, obj); return create_marshal(outer, &CLSID_wine_row_proxy, obj); } HRESULT create_rowset_marshal(IUnknown *outer, void **obj) { TRACE("(%p, %p)\n", outer, obj); return create_marshal(outer, &CLSID_wine_rowset_proxy, obj); }