diff --git a/dlls/ole32/clipboard.c b/dlls/ole32/clipboard.c index b7527e94452..c1ef5d31c74 100644 --- a/dlls/ole32/clipboard.c +++ b/dlls/ole32/clipboard.c @@ -134,6 +134,8 @@ typedef struct snapshot LONG ref; DWORD seq_no; /* Clipboard sequence number corresponding to this snapshot */ + + IDataObject *data; /* If we unmarshal a remote data object we hold a ref here */ } snapshot; /**************************************************************************** @@ -830,6 +832,8 @@ static ULONG WINAPI snapshot_Release(IDataObject *iface) ole_clipbrd *clipbrd; HRESULT hr = get_ole_clipbrd(&clipbrd); + if(This->data) IDataObject_Release(This->data); + if(SUCCEEDED(hr) && clipbrd->latest_snapshot == This) clipbrd->latest_snapshot = NULL; HeapFree(GetProcessHeap(), 0, This); @@ -907,25 +911,26 @@ static HRESULT WINAPI snapshot_GetData(IDataObject *iface, FORMATETC *pformatetcIn, STGMEDIUM *pmedium) { - HANDLE h, hData = 0; - HRESULT hr; - IDataObject *data; + snapshot *This = impl_from_IDataObject(iface); + HANDLE h, hData = 0; + HRESULT hr; - TRACE("(%p,%p,%p)\n", iface, pformatetcIn, pmedium); + TRACE("(%p,%p,%p)\n", iface, pformatetcIn, pmedium); - if ( !pformatetcIn || !pmedium ) - return E_INVALIDARG; + if ( !pformatetcIn || !pmedium ) return E_INVALIDARG; - if ( !OpenClipboard(NULL)) return CLIPBRD_E_CANT_OPEN; + if ( !OpenClipboard(NULL)) return CLIPBRD_E_CANT_OPEN; + + if(!This->data) + hr = get_current_dataobject(&This->data); + + if(This->data) + { + hr = IDataObject_GetData(This->data, pformatetcIn, pmedium); + CloseClipboard(); + return hr; + } - hr = get_current_dataobject(&data); - if ( hr == S_OK ) - { - hr = IDataObject_GetData(data, pformatetcIn, pmedium); - IDataObject_Release(data); - CloseClipboard(); - return hr; - } /* * Otherwise, get the data from the windows clipboard using GetClipboardData @@ -1130,6 +1135,7 @@ static snapshot *snapshot_construct(DWORD seq_no) This->lpVtbl = &snapshot_vtable; This->ref = 0; This->seq_no = seq_no; + This->data = NULL; return This; } diff --git a/dlls/ole32/tests/clipboard.c b/dlls/ole32/tests/clipboard.c index eef57bdf93e..c77ec95d1f2 100644 --- a/dlls/ole32/tests/clipboard.c +++ b/dlls/ole32/tests/clipboard.c @@ -862,14 +862,27 @@ static void test_set_clipboard(void) OleUninitialize(); } +static inline ULONG count_refs(IDataObject *d) +{ + IDataObject_AddRef(d); + return IDataObject_Release(d); +} + static void test_consumer_refs(void) { HRESULT hr; IDataObject *src, *get1, *get2, *get3; - LONG refs; + ULONG refs, old_refs; + FORMATETC fmt; + STGMEDIUM med; + + InitFormatEtc(fmt, CF_TEXT, TYMED_HGLOBAL); OleInitialize(NULL); + /* First show that each clipboard state results in + a different data object */ + hr = DataObjectImpl_CreateText("data1", &src); ok(hr == S_OK, "got %08x\n", hr); @@ -888,6 +901,12 @@ static void test_consumer_refs(void) OleFlushClipboard(); + DataObjectImpl_GetData_calls = 0; + hr = IDataObject_GetData(get1, &fmt, &med); + ok(hr == S_OK, "got %08x\n", hr); + ok(DataObjectImpl_GetData_calls == 0, "GetData called\n"); + ReleaseStgMedium(&med); + hr = OleGetClipboard(&get2); ok(hr == S_OK, "got %08x\n", hr); @@ -904,6 +923,46 @@ static void test_consumer_refs(void) IDataObject_Release(get3); IDataObject_Release(get2); IDataObject_Release(get1); + + /* Now call GetData before the flush and show that this + takes a ref on our src data obj. */ + + hr = OleSetClipboard(src); + ok(hr == S_OK, "got %08x\n", hr); + + old_refs = count_refs(src); + trace("old_refs %d\n", old_refs); + + hr = OleGetClipboard(&get1); + ok(hr == S_OK, "got %08x\n", hr); + + refs = count_refs(src); + ok(refs == old_refs, "%d %d\n", refs, old_refs); + + DataObjectImpl_GetData_calls = 0; + hr = IDataObject_GetData(get1, &fmt, &med); + ok(hr == S_OK, "got %08x\n", hr); + ok(DataObjectImpl_GetData_calls == 1, "GetData not called\n"); + ReleaseStgMedium(&med); + refs = count_refs(src); + ok(refs == old_refs + 1, "%d %d\n", refs, old_refs); + + OleFlushClipboard(); + + DataObjectImpl_GetData_calls = 0; + hr = IDataObject_GetData(get1, &fmt, &med); + ok(hr == S_OK, "got %08x\n", hr); + ok(DataObjectImpl_GetData_calls == 1, "GetData not called\n"); + ReleaseStgMedium(&med); + + refs = count_refs(src); + ok(refs == 2, "%d\n", refs); + + IDataObject_Release(get1); + + refs = count_refs(src); + ok(refs == 1, "%d\n", refs); + IDataObject_Release(src); OleUninitialize();