| /* |
| * OLE2 COM objects |
| * |
| * Copyright 1998 Eric Kohl |
| * Copyright 1999 Francis Beaudet |
| * |
| * 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 |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "winuser.h" |
| #include "winerror.h" |
| #include "wine/debug.h" |
| #include "ole2.h" |
| |
| #include "compobj_private.h" |
| |
| WINE_DEFAULT_DEBUG_CHANNEL(ole); |
| |
| #define INITIAL_SINKS 10 |
| |
| static void release_statdata(STATDATA *data) |
| { |
| if(data->formatetc.ptd) |
| { |
| CoTaskMemFree(data->formatetc.ptd); |
| data->formatetc.ptd = NULL; |
| } |
| |
| if(data->pAdvSink) |
| { |
| IAdviseSink_Release(data->pAdvSink); |
| data->pAdvSink = NULL; |
| } |
| } |
| |
| static HRESULT copy_statdata(STATDATA *dst, const STATDATA *src) |
| { |
| *dst = *src; |
| if(src->formatetc.ptd) |
| { |
| dst->formatetc.ptd = CoTaskMemAlloc(src->formatetc.ptd->tdSize); |
| if(!dst->formatetc.ptd) return E_OUTOFMEMORY; |
| memcpy(dst->formatetc.ptd, src->formatetc.ptd, src->formatetc.ptd->tdSize); |
| } |
| if(dst->pAdvSink) IAdviseSink_AddRef(dst->pAdvSink); |
| return S_OK; |
| } |
| |
| /************************************************************************** |
| * EnumSTATDATA Implementation |
| */ |
| |
| static HRESULT EnumSTATDATA_Construct(IUnknown *holder, ULONG index, DWORD array_len, STATDATA *data, IEnumSTATDATA **ppenum); |
| |
| typedef struct |
| { |
| IEnumSTATDATA IEnumSTATDATA_iface; |
| LONG ref; |
| |
| ULONG index; |
| DWORD num_of_elems; |
| STATDATA *statdata; |
| IUnknown *holder; |
| } EnumSTATDATA; |
| |
| static inline EnumSTATDATA *impl_from_IEnumSTATDATA(IEnumSTATDATA *iface) |
| { |
| return CONTAINING_RECORD(iface, EnumSTATDATA, IEnumSTATDATA_iface); |
| } |
| |
| static HRESULT WINAPI EnumSTATDATA_QueryInterface(IEnumSTATDATA *iface, REFIID riid, void **ppv) |
| { |
| TRACE("(%s, %p)\n", debugstr_guid(riid), ppv); |
| if (IsEqualIID(riid, &IID_IUnknown) || |
| IsEqualIID(riid, &IID_IEnumSTATDATA)) |
| { |
| IEnumSTATDATA_AddRef(iface); |
| *ppv = iface; |
| return S_OK; |
| } |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI EnumSTATDATA_AddRef(IEnumSTATDATA *iface) |
| { |
| EnumSTATDATA *This = impl_from_IEnumSTATDATA(iface); |
| TRACE("()\n"); |
| return InterlockedIncrement(&This->ref); |
| } |
| |
| static ULONG WINAPI EnumSTATDATA_Release(IEnumSTATDATA *iface) |
| { |
| EnumSTATDATA *This = impl_from_IEnumSTATDATA(iface); |
| LONG refs = InterlockedDecrement(&This->ref); |
| TRACE("()\n"); |
| if (!refs) |
| { |
| DWORD i; |
| for(i = 0; i < This->num_of_elems; i++) |
| release_statdata(This->statdata + i); |
| HeapFree(GetProcessHeap(), 0, This->statdata); |
| IUnknown_Release(This->holder); |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| return refs; |
| } |
| |
| static HRESULT WINAPI EnumSTATDATA_Next(IEnumSTATDATA *iface, ULONG num, LPSTATDATA data, |
| ULONG *fetched) |
| { |
| EnumSTATDATA *This = impl_from_IEnumSTATDATA(iface); |
| DWORD count = 0; |
| HRESULT hr = S_OK; |
| |
| TRACE("(%d, %p, %p)\n", num, data, fetched); |
| |
| while(num--) |
| { |
| if (This->index >= This->num_of_elems) |
| { |
| hr = S_FALSE; |
| break; |
| } |
| |
| copy_statdata(data + count, This->statdata + This->index); |
| |
| count++; |
| This->index++; |
| } |
| |
| if (fetched) *fetched = count; |
| |
| return hr; |
| } |
| |
| static HRESULT WINAPI EnumSTATDATA_Skip(IEnumSTATDATA *iface, ULONG num) |
| { |
| EnumSTATDATA *This = impl_from_IEnumSTATDATA(iface); |
| |
| TRACE("(%d)\n", num); |
| |
| if(This->index + num >= This->num_of_elems) |
| { |
| This->index = This->num_of_elems; |
| return S_FALSE; |
| } |
| |
| This->index += num; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI EnumSTATDATA_Reset(IEnumSTATDATA *iface) |
| { |
| EnumSTATDATA *This = impl_from_IEnumSTATDATA(iface); |
| |
| TRACE("()\n"); |
| |
| This->index = 0; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI EnumSTATDATA_Clone(IEnumSTATDATA *iface, IEnumSTATDATA **ppenum) |
| { |
| EnumSTATDATA *This = impl_from_IEnumSTATDATA(iface); |
| |
| return EnumSTATDATA_Construct(This->holder, This->index, This->num_of_elems, This->statdata, ppenum); |
| } |
| |
| static const IEnumSTATDATAVtbl EnumSTATDATA_VTable = |
| { |
| EnumSTATDATA_QueryInterface, |
| EnumSTATDATA_AddRef, |
| EnumSTATDATA_Release, |
| EnumSTATDATA_Next, |
| EnumSTATDATA_Skip, |
| EnumSTATDATA_Reset, |
| EnumSTATDATA_Clone |
| }; |
| |
| static HRESULT EnumSTATDATA_Construct(IUnknown *holder, ULONG index, DWORD array_len, STATDATA *data, |
| IEnumSTATDATA **ppenum) |
| { |
| EnumSTATDATA *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); |
| DWORD i, count; |
| |
| if (!This) return E_OUTOFMEMORY; |
| |
| This->IEnumSTATDATA_iface.lpVtbl = &EnumSTATDATA_VTable; |
| This->ref = 1; |
| This->index = index; |
| |
| This->statdata = HeapAlloc(GetProcessHeap(), 0, array_len * sizeof(*This->statdata)); |
| if(!This->statdata) |
| { |
| HeapFree(GetProcessHeap(), 0, This); |
| return E_OUTOFMEMORY; |
| } |
| |
| for(i = 0, count = 0; i < array_len; i++) |
| { |
| if(data[i].pAdvSink) |
| { |
| copy_statdata(This->statdata + count, data + i); |
| count++; |
| } |
| } |
| |
| This->num_of_elems = count; |
| This->holder = holder; |
| IUnknown_AddRef(holder); |
| *ppenum = &This->IEnumSTATDATA_iface; |
| return S_OK; |
| } |
| |
| /************************************************************************** |
| * OleAdviseHolder Implementation |
| */ |
| typedef struct |
| { |
| IOleAdviseHolder IOleAdviseHolder_iface; |
| |
| LONG ref; |
| |
| DWORD max_cons; |
| STATDATA *connections; |
| } OleAdviseHolderImpl; |
| |
| static inline OleAdviseHolderImpl *impl_from_IOleAdviseHolder(IOleAdviseHolder *iface) |
| { |
| return CONTAINING_RECORD(iface, OleAdviseHolderImpl, IOleAdviseHolder_iface); |
| } |
| |
| /************************************************************************** |
| * OleAdviseHolderImpl_Destructor |
| */ |
| static void OleAdviseHolderImpl_Destructor(OleAdviseHolderImpl *This) |
| { |
| DWORD index; |
| TRACE("%p\n", This); |
| |
| for (index = 0; index < This->max_cons; index++) |
| { |
| if (This->connections[index].pAdvSink != NULL) |
| release_statdata(This->connections + index); |
| } |
| |
| HeapFree(GetProcessHeap(), 0, This->connections); |
| HeapFree(GetProcessHeap(), 0, This); |
| } |
| |
| /************************************************************************** |
| * OleAdviseHolderImpl_QueryInterface |
| */ |
| static HRESULT WINAPI OleAdviseHolderImpl_QueryInterface(IOleAdviseHolder *iface, |
| REFIID iid, void **obj) |
| { |
| OleAdviseHolderImpl *This = impl_from_IOleAdviseHolder(iface); |
| TRACE("(%p)->(%s,%p)\n",This, debugstr_guid(iid), obj); |
| |
| if (obj == NULL) |
| return E_POINTER; |
| |
| *obj = NULL; |
| |
| if (IsEqualIID(iid, &IID_IUnknown) || |
| IsEqualIID(iid, &IID_IOleAdviseHolder)) |
| { |
| *obj = &This->IOleAdviseHolder_iface; |
| } |
| |
| if(*obj == NULL) |
| return E_NOINTERFACE; |
| |
| IUnknown_AddRef((IUnknown*)*obj); |
| |
| return S_OK; |
| } |
| |
| /****************************************************************************** |
| * OleAdviseHolderImpl_AddRef |
| */ |
| static ULONG WINAPI OleAdviseHolderImpl_AddRef(IOleAdviseHolder *iface) |
| { |
| OleAdviseHolderImpl *This = impl_from_IOleAdviseHolder(iface); |
| ULONG ref = InterlockedIncrement(&This->ref); |
| |
| TRACE("(%p)->(ref=%d)\n", This, ref - 1); |
| |
| return ref; |
| } |
| |
| /****************************************************************************** |
| * OleAdviseHolderImpl_Release |
| */ |
| static ULONG WINAPI OleAdviseHolderImpl_Release(IOleAdviseHolder *iface) |
| { |
| OleAdviseHolderImpl *This = impl_from_IOleAdviseHolder(iface); |
| ULONG ref; |
| TRACE("(%p)->(ref=%d)\n", This, This->ref); |
| ref = InterlockedDecrement(&This->ref); |
| |
| if (ref == 0) OleAdviseHolderImpl_Destructor(This); |
| |
| return ref; |
| } |
| |
| /****************************************************************************** |
| * OleAdviseHolderImpl_Advise |
| */ |
| static HRESULT WINAPI OleAdviseHolderImpl_Advise(IOleAdviseHolder *iface, |
| IAdviseSink *pAdvise, |
| DWORD *pdwConnection) |
| { |
| DWORD index; |
| OleAdviseHolderImpl *This = impl_from_IOleAdviseHolder(iface); |
| STATDATA new_conn; |
| static const FORMATETC empty_fmtetc = {0, NULL, 0, -1, 0}; |
| |
| TRACE("(%p)->(%p, %p)\n", This, pAdvise, pdwConnection); |
| |
| if (pdwConnection==NULL) |
| return E_POINTER; |
| |
| *pdwConnection = 0; |
| |
| for (index = 0; index < This->max_cons; index++) |
| { |
| if (This->connections[index].pAdvSink == NULL) |
| break; |
| } |
| |
| if (index == This->max_cons) |
| { |
| This->max_cons += INITIAL_SINKS; |
| This->connections = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, This->connections, |
| This->max_cons * sizeof(*This->connections)); |
| } |
| |
| new_conn.pAdvSink = pAdvise; |
| new_conn.advf = 0; |
| new_conn.formatetc = empty_fmtetc; |
| new_conn.dwConnection = index + 1; /* 0 is not a valid cookie, so increment the index */ |
| |
| copy_statdata(This->connections + index, &new_conn); |
| |
| *pdwConnection = new_conn.dwConnection; |
| |
| return S_OK; |
| } |
| |
| /****************************************************************************** |
| * OleAdviseHolderImpl_Unadvise |
| */ |
| static HRESULT WINAPI OleAdviseHolderImpl_Unadvise(IOleAdviseHolder *iface, |
| DWORD dwConnection) |
| { |
| OleAdviseHolderImpl *This = impl_from_IOleAdviseHolder(iface); |
| DWORD index; |
| |
| TRACE("(%p)->(%u)\n", This, dwConnection); |
| |
| /* The connection number is 1 more than the index, see OleAdviseHolder_Advise */ |
| index = dwConnection - 1; |
| |
| if (index >= This->max_cons || This->connections[index].pAdvSink == NULL) |
| return OLE_E_NOCONNECTION; |
| |
| release_statdata(This->connections + index); |
| |
| return S_OK; |
| } |
| |
| /****************************************************************************** |
| * OleAdviseHolderImpl_EnumAdvise |
| */ |
| static HRESULT WINAPI OleAdviseHolderImpl_EnumAdvise(IOleAdviseHolder *iface, IEnumSTATDATA **enum_advise) |
| { |
| OleAdviseHolderImpl *This = impl_from_IOleAdviseHolder(iface); |
| IUnknown *unk; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p)\n", This, enum_advise); |
| |
| IOleAdviseHolder_QueryInterface(iface, &IID_IUnknown, (void**)&unk); |
| hr = EnumSTATDATA_Construct(unk, 0, This->max_cons, This->connections, enum_advise); |
| IUnknown_Release(unk); |
| return hr; |
| } |
| |
| /****************************************************************************** |
| * OleAdviseHolderImpl_SendOnRename |
| */ |
| static HRESULT WINAPI OleAdviseHolderImpl_SendOnRename(IOleAdviseHolder *iface, IMoniker *pmk) |
| { |
| IEnumSTATDATA *pEnum; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p)\n", iface, pmk); |
| |
| hr = IOleAdviseHolder_EnumAdvise(iface, &pEnum); |
| if (SUCCEEDED(hr)) |
| { |
| STATDATA statdata; |
| while (IEnumSTATDATA_Next(pEnum, 1, &statdata, NULL) == S_OK) |
| { |
| IAdviseSink_OnRename(statdata.pAdvSink, pmk); |
| |
| IAdviseSink_Release(statdata.pAdvSink); |
| } |
| IEnumSTATDATA_Release(pEnum); |
| } |
| |
| return hr; |
| } |
| |
| /****************************************************************************** |
| * OleAdviseHolderImpl_SendOnSave |
| */ |
| static HRESULT WINAPI OleAdviseHolderImpl_SendOnSave(IOleAdviseHolder *iface) |
| { |
| IEnumSTATDATA *pEnum; |
| HRESULT hr; |
| |
| TRACE("(%p)->()\n", iface); |
| |
| hr = IOleAdviseHolder_EnumAdvise(iface, &pEnum); |
| if (SUCCEEDED(hr)) |
| { |
| STATDATA statdata; |
| while (IEnumSTATDATA_Next(pEnum, 1, &statdata, NULL) == S_OK) |
| { |
| IAdviseSink_OnSave(statdata.pAdvSink); |
| |
| IAdviseSink_Release(statdata.pAdvSink); |
| } |
| IEnumSTATDATA_Release(pEnum); |
| } |
| |
| return hr; |
| } |
| |
| /****************************************************************************** |
| * OleAdviseHolderImpl_SendOnClose |
| */ |
| static HRESULT WINAPI OleAdviseHolderImpl_SendOnClose(IOleAdviseHolder *iface) |
| { |
| IEnumSTATDATA *pEnum; |
| HRESULT hr; |
| |
| TRACE("(%p)->()\n", iface); |
| |
| hr = IOleAdviseHolder_EnumAdvise(iface, &pEnum); |
| if (SUCCEEDED(hr)) |
| { |
| STATDATA statdata; |
| while (IEnumSTATDATA_Next(pEnum, 1, &statdata, NULL) == S_OK) |
| { |
| IAdviseSink_OnClose(statdata.pAdvSink); |
| |
| IAdviseSink_Release(statdata.pAdvSink); |
| } |
| IEnumSTATDATA_Release(pEnum); |
| } |
| |
| return hr; |
| } |
| |
| /************************************************************************** |
| * OleAdviseHolderImpl_VTable |
| */ |
| static const IOleAdviseHolderVtbl oahvt = |
| { |
| OleAdviseHolderImpl_QueryInterface, |
| OleAdviseHolderImpl_AddRef, |
| OleAdviseHolderImpl_Release, |
| OleAdviseHolderImpl_Advise, |
| OleAdviseHolderImpl_Unadvise, |
| OleAdviseHolderImpl_EnumAdvise, |
| OleAdviseHolderImpl_SendOnRename, |
| OleAdviseHolderImpl_SendOnSave, |
| OleAdviseHolderImpl_SendOnClose |
| }; |
| |
| /************************************************************************** |
| * OleAdviseHolderImpl_Constructor |
| */ |
| |
| static IOleAdviseHolder *OleAdviseHolderImpl_Constructor(void) |
| { |
| OleAdviseHolderImpl* lpoah; |
| |
| lpoah = HeapAlloc(GetProcessHeap(), 0, sizeof(OleAdviseHolderImpl)); |
| |
| lpoah->IOleAdviseHolder_iface.lpVtbl = &oahvt; |
| lpoah->ref = 1; |
| lpoah->max_cons = INITIAL_SINKS; |
| lpoah->connections = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, |
| lpoah->max_cons * sizeof(*lpoah->connections)); |
| |
| TRACE("returning %p\n", &lpoah->IOleAdviseHolder_iface); |
| return &lpoah->IOleAdviseHolder_iface; |
| } |
| |
| /************************************************************************** |
| * DataAdviseHolder Implementation |
| */ |
| typedef struct |
| { |
| IDataAdviseHolder IDataAdviseHolder_iface; |
| |
| LONG ref; |
| DWORD maxCons; |
| STATDATA* connections; |
| DWORD* remote_connections; |
| IDataObject* delegate; |
| } DataAdviseHolder; |
| |
| /* this connection has also has been advised to the delegate data object */ |
| #define WINE_ADVF_REMOTE 0x80000000 |
| |
| static inline DataAdviseHolder *impl_from_IDataAdviseHolder(IDataAdviseHolder *iface) |
| { |
| return CONTAINING_RECORD(iface, DataAdviseHolder, IDataAdviseHolder_iface); |
| } |
| |
| /****************************************************************************** |
| * DataAdviseHolder_Destructor |
| */ |
| static void DataAdviseHolder_Destructor(DataAdviseHolder* ptrToDestroy) |
| { |
| DWORD index; |
| TRACE("%p\n", ptrToDestroy); |
| |
| for (index = 0; index < ptrToDestroy->maxCons; index++) |
| { |
| if (ptrToDestroy->connections[index].pAdvSink != NULL) |
| { |
| if (ptrToDestroy->delegate && |
| (ptrToDestroy->connections[index].advf & WINE_ADVF_REMOTE)) |
| IDataObject_DUnadvise(ptrToDestroy->delegate, |
| ptrToDestroy->remote_connections[index]); |
| |
| release_statdata(ptrToDestroy->connections + index); |
| } |
| } |
| |
| HeapFree(GetProcessHeap(), 0, ptrToDestroy->remote_connections); |
| HeapFree(GetProcessHeap(), 0, ptrToDestroy->connections); |
| HeapFree(GetProcessHeap(), 0, ptrToDestroy); |
| } |
| |
| /************************************************************************ |
| * DataAdviseHolder_QueryInterface (IUnknown) |
| */ |
| static HRESULT WINAPI DataAdviseHolder_QueryInterface(IDataAdviseHolder *iface, |
| REFIID riid, void **ppvObject) |
| { |
| DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface); |
| TRACE("(%p)->(%s,%p)\n",This,debugstr_guid(riid),ppvObject); |
| |
| if ( (This==0) || (ppvObject==0) ) |
| return E_INVALIDARG; |
| |
| *ppvObject = 0; |
| |
| if ( IsEqualIID(&IID_IUnknown, riid) || |
| IsEqualIID(&IID_IDataAdviseHolder, riid) ) |
| { |
| *ppvObject = iface; |
| } |
| |
| if ((*ppvObject)==0) |
| { |
| return E_NOINTERFACE; |
| } |
| |
| IUnknown_AddRef((IUnknown*)*ppvObject); |
| return S_OK; |
| } |
| |
| /************************************************************************ |
| * DataAdviseHolder_AddRef (IUnknown) |
| */ |
| static ULONG WINAPI DataAdviseHolder_AddRef(IDataAdviseHolder *iface) |
| { |
| DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface); |
| TRACE("(%p) (ref=%d)\n", This, This->ref); |
| return InterlockedIncrement(&This->ref); |
| } |
| |
| /************************************************************************ |
| * DataAdviseHolder_Release (IUnknown) |
| */ |
| static ULONG WINAPI DataAdviseHolder_Release(IDataAdviseHolder *iface) |
| { |
| DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface); |
| ULONG ref; |
| TRACE("(%p) (ref=%d)\n", This, This->ref); |
| |
| ref = InterlockedDecrement(&This->ref); |
| if (ref==0) DataAdviseHolder_Destructor(This); |
| |
| return ref; |
| } |
| |
| /************************************************************************ |
| * DataAdviseHolder_Advise |
| * |
| */ |
| static HRESULT WINAPI DataAdviseHolder_Advise(IDataAdviseHolder *iface, |
| IDataObject *pDataObject, FORMATETC *pFetc, |
| DWORD advf, IAdviseSink *pAdvise, |
| DWORD *pdwConnection) |
| { |
| DWORD index; |
| STATDATA new_conn; |
| DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface); |
| |
| TRACE("(%p)->(%p, %p, %08x, %p, %p)\n", This, pDataObject, pFetc, advf, |
| pAdvise, pdwConnection); |
| |
| if (pdwConnection==NULL) |
| return E_POINTER; |
| |
| *pdwConnection = 0; |
| |
| for (index = 0; index < This->maxCons; index++) |
| { |
| if (This->connections[index].pAdvSink == NULL) |
| break; |
| } |
| |
| if (index == This->maxCons) |
| { |
| This->maxCons+=INITIAL_SINKS; |
| This->connections = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, |
| This->connections, |
| This->maxCons * sizeof(*This->connections)); |
| This->remote_connections = HeapReAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, |
| This->remote_connections, |
| This->maxCons * sizeof(*This->remote_connections)); |
| } |
| |
| new_conn.pAdvSink = pAdvise; |
| new_conn.advf = advf & ~WINE_ADVF_REMOTE; |
| new_conn.formatetc = *pFetc; |
| new_conn.dwConnection = index + 1; /* 0 is not a valid cookie, so increment the index */ |
| |
| copy_statdata(This->connections + index, &new_conn); |
| |
| if (This->connections[index].pAdvSink != NULL) |
| { |
| /* if we are already connected advise the remote object */ |
| if (This->delegate) |
| { |
| HRESULT hr; |
| |
| hr = IDataObject_DAdvise(This->delegate, &new_conn.formatetc, |
| new_conn.advf, new_conn.pAdvSink, |
| &This->remote_connections[index]); |
| if (FAILED(hr)) |
| { |
| IDataAdviseHolder_Unadvise(iface, new_conn.dwConnection); |
| return hr; |
| } |
| This->connections[index].advf |= WINE_ADVF_REMOTE; |
| } |
| else if(advf & ADVF_PRIMEFIRST) |
| /* only do this if we have no delegate, since in the above case the |
| * delegate will do the priming for us */ |
| IDataAdviseHolder_SendOnDataChange(iface, pDataObject, 0, advf); |
| } |
| |
| *pdwConnection = new_conn.dwConnection; |
| |
| return S_OK; |
| } |
| |
| /****************************************************************************** |
| * DataAdviseHolder_Unadvise |
| */ |
| static HRESULT WINAPI DataAdviseHolder_Unadvise(IDataAdviseHolder *iface, |
| DWORD dwConnection) |
| { |
| DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface); |
| DWORD index; |
| TRACE("(%p)->(%u)\n", This, dwConnection); |
| |
| /* The connection number is 1 more than the index, see DataAdviseHolder_Advise */ |
| index = dwConnection - 1; |
| |
| if (index >= This->maxCons || This->connections[index].pAdvSink == NULL) |
| return OLE_E_NOCONNECTION; |
| |
| if (This->delegate && This->connections[index].advf & WINE_ADVF_REMOTE) |
| { |
| IDataObject_DUnadvise(This->delegate, This->remote_connections[index]); |
| This->remote_connections[index] = 0; |
| } |
| |
| release_statdata(This->connections + index); |
| |
| return S_OK; |
| } |
| |
| /****************************************************************************** |
| * DataAdviseHolder_EnumAdvise |
| */ |
| static HRESULT WINAPI DataAdviseHolder_EnumAdvise(IDataAdviseHolder *iface, |
| IEnumSTATDATA **enum_advise) |
| { |
| DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface); |
| IUnknown *unk; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p)\n", This, enum_advise); |
| |
| IDataAdviseHolder_QueryInterface(iface, &IID_IUnknown, (void**)&unk); |
| hr = EnumSTATDATA_Construct(unk, 0, This->maxCons, This->connections, enum_advise); |
| IUnknown_Release(unk); |
| return hr; |
| } |
| |
| /****************************************************************************** |
| * DataAdviseHolder_SendOnDataChange |
| */ |
| static HRESULT WINAPI DataAdviseHolder_SendOnDataChange(IDataAdviseHolder *iface, |
| IDataObject *data_obj, |
| DWORD dwReserved, DWORD advf) |
| { |
| IEnumSTATDATA *pEnum; |
| HRESULT hr; |
| |
| TRACE("(%p)->(%p, %08x, %08x)\n", iface, data_obj, dwReserved, advf); |
| |
| hr = IDataAdviseHolder_EnumAdvise(iface, &pEnum); |
| if (SUCCEEDED(hr)) |
| { |
| STATDATA statdata; |
| while (IEnumSTATDATA_Next(pEnum, 1, &statdata, NULL) == S_OK) |
| { |
| STGMEDIUM stg; |
| stg.tymed = TYMED_NULL; |
| stg.u.pstg = NULL; |
| stg.pUnkForRelease = NULL; |
| |
| if(!(statdata.advf & ADVF_NODATA)) |
| { |
| hr = IDataObject_GetData(data_obj, &statdata.formatetc, &stg); |
| } |
| |
| IAdviseSink_OnDataChange(statdata.pAdvSink, &statdata.formatetc, &stg); |
| |
| if(statdata.advf & ADVF_ONLYONCE) |
| { |
| IDataAdviseHolder_Unadvise(iface, statdata.dwConnection); |
| } |
| |
| release_statdata(&statdata); |
| } |
| IEnumSTATDATA_Release(pEnum); |
| } |
| |
| return S_OK; |
| } |
| |
| /************************************************************************** |
| * DataAdviseHolderImpl_VTable |
| */ |
| static const IDataAdviseHolderVtbl DataAdviseHolderImpl_VTable = |
| { |
| DataAdviseHolder_QueryInterface, |
| DataAdviseHolder_AddRef, |
| DataAdviseHolder_Release, |
| DataAdviseHolder_Advise, |
| DataAdviseHolder_Unadvise, |
| DataAdviseHolder_EnumAdvise, |
| DataAdviseHolder_SendOnDataChange |
| }; |
| |
| HRESULT DataAdviseHolder_OnConnect(IDataAdviseHolder *iface, IDataObject *pDelegate) |
| { |
| DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface); |
| DWORD index; |
| HRESULT hr = S_OK; |
| |
| for(index = 0; index < This->maxCons; index++) |
| { |
| if(This->connections[index].pAdvSink != NULL) |
| { |
| hr = IDataObject_DAdvise(pDelegate, &This->connections[index].formatetc, |
| This->connections[index].advf, |
| This->connections[index].pAdvSink, |
| &This->remote_connections[index]); |
| if (FAILED(hr)) break; |
| This->connections[index].advf |= WINE_ADVF_REMOTE; |
| } |
| } |
| This->delegate = pDelegate; |
| return hr; |
| } |
| |
| void DataAdviseHolder_OnDisconnect(IDataAdviseHolder *iface) |
| { |
| DataAdviseHolder *This = impl_from_IDataAdviseHolder(iface); |
| DWORD index; |
| |
| for(index = 0; index < This->maxCons; index++) |
| { |
| if((This->connections[index].pAdvSink != NULL) && |
| (This->connections[index].advf & WINE_ADVF_REMOTE)) |
| { |
| IDataObject_DUnadvise(This->delegate, This->remote_connections[index]); |
| This->remote_connections[index] = 0; |
| This->connections[index].advf &= ~WINE_ADVF_REMOTE; |
| } |
| } |
| This->delegate = NULL; |
| } |
| |
| /****************************************************************************** |
| * DataAdviseHolder_Constructor |
| */ |
| static IDataAdviseHolder *DataAdviseHolder_Constructor(void) |
| { |
| DataAdviseHolder* newHolder; |
| |
| newHolder = HeapAlloc(GetProcessHeap(), 0, sizeof(DataAdviseHolder)); |
| |
| newHolder->IDataAdviseHolder_iface.lpVtbl = &DataAdviseHolderImpl_VTable; |
| newHolder->ref = 1; |
| newHolder->maxCons = INITIAL_SINKS; |
| newHolder->connections = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, |
| newHolder->maxCons * sizeof(*newHolder->connections)); |
| newHolder->remote_connections = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, |
| newHolder->maxCons * sizeof(*newHolder->remote_connections)); |
| newHolder->delegate = NULL; |
| |
| TRACE("returning %p\n", &newHolder->IDataAdviseHolder_iface); |
| return &newHolder->IDataAdviseHolder_iface; |
| } |
| |
| /*********************************************************************** |
| * API functions |
| */ |
| |
| /*********************************************************************** |
| * CreateOleAdviseHolder [OLE32.@] |
| */ |
| HRESULT WINAPI CreateOleAdviseHolder(IOleAdviseHolder **ppOAHolder) |
| { |
| TRACE("(%p)\n", ppOAHolder); |
| |
| if (ppOAHolder==NULL) |
| return E_POINTER; |
| |
| *ppOAHolder = OleAdviseHolderImpl_Constructor (); |
| |
| if (*ppOAHolder != NULL) |
| return S_OK; |
| |
| return E_OUTOFMEMORY; |
| } |
| |
| /****************************************************************************** |
| * CreateDataAdviseHolder [OLE32.@] |
| */ |
| HRESULT WINAPI CreateDataAdviseHolder(IDataAdviseHolder **ppDAHolder) |
| { |
| TRACE("(%p)\n", ppDAHolder); |
| |
| if (ppDAHolder==NULL) |
| return E_POINTER; |
| |
| *ppDAHolder = DataAdviseHolder_Constructor(); |
| |
| if (*ppDAHolder != NULL) |
| return S_OK; |
| |
| return E_OUTOFMEMORY; |
| } |