diff --git a/dlls/ole32/clipboard.c b/dlls/ole32/clipboard.c index 4d7a7db705..1e72ec2121 100644 --- a/dlls/ole32/clipboard.c +++ b/dlls/ole32/clipboard.c @@ -1197,7 +1197,7 @@ static HRESULT WINAPI snapshot_GetData(IDataObject *iface, FORMATETC *fmt, hr = get_stgmed_for_stream(h, med); else { - FIXME("Unhandled tymed - emum tymed %x req tymed %x\n", entry->fmtetc.tymed, fmt->tymed); + FIXME("Unhandled tymed - mask %x req tymed %x\n", mask, fmt->tymed); hr = E_FAIL; goto end; } @@ -1214,8 +1214,108 @@ end: static HRESULT WINAPI snapshot_GetDataHere(IDataObject *iface, FORMATETC *fmt, STGMEDIUM *med) { - FIXME("(%p, %p {%s}, %p): stub\n", iface, fmt, dump_fmtetc(fmt), med); - return E_NOTIMPL; + snapshot *This = impl_from_IDataObject(iface); + HANDLE h; + HRESULT hr; + ole_priv_data *enum_data = NULL; + ole_priv_data_entry *entry; + TYMED supported; + + TRACE("(%p, %p {%s}, %p (tymed %x)\n", iface, fmt, dump_fmtetc(fmt), med, med->tymed); + + if ( !fmt || !med ) return E_INVALIDARG; + + if ( !OpenClipboard(NULL)) return CLIPBRD_E_CANT_OPEN; + + if(!This->data) + hr = get_current_dataobject(&This->data); + + if(This->data) + { + hr = IDataObject_GetDataHere(This->data, fmt, med); + if(SUCCEEDED(hr)) + { + CloseClipboard(); + return hr; + } + } + + h = GetClipboardData(fmt->cfFormat); + if(!h) + { + hr = DV_E_FORMATETC; + goto end; + } + + hr = get_priv_data(&enum_data); + if(FAILED(hr)) goto end; + + entry = find_format_in_list(enum_data->entries, enum_data->count, fmt->cfFormat); + if(entry) + { + if(!td_equal(fmt->ptd, entry->fmtetc.ptd)) + { + hr = DV_E_FORMATETC; + goto end; + } + supported = entry->fmtetc.tymed; + } + else /* non-Ole format */ + supported = TYMED_HGLOBAL; + + switch(med->tymed) + { + case TYMED_HGLOBAL: + { + DWORD src_size = GlobalSize(h); + DWORD dst_size = GlobalSize(med->u.hGlobal); + hr = E_FAIL; + if(dst_size >= src_size) + { + void *src = GlobalLock(h); + void *dst = GlobalLock(med->u.hGlobal); + + memcpy(dst, src, src_size); + GlobalUnlock(med->u.hGlobal); + GlobalUnlock(h); + hr = S_OK; + } + break; + } + case TYMED_ISTREAM: + { + DWORD src_size = GlobalSize(h); + void *src = GlobalLock(h); + hr = IStream_Write(med->u.pstm, src, src_size, NULL); + GlobalUnlock(h); + break; + } + case TYMED_ISTORAGE: + { + STGMEDIUM copy; + if(!(supported & TYMED_ISTORAGE)) + { + hr = E_FAIL; + goto end; + } + hr = get_stgmed_for_storage(h, ©); + if(SUCCEEDED(hr)) + { + hr = IStorage_CopyTo(copy.u.pstg, 0, NULL, NULL, med->u.pstg); + ReleaseStgMedium(©); + } + break; + } + default: + FIXME("Unhandled tymed - supported %x req tymed %x\n", supported, med->tymed); + hr = E_FAIL; + goto end; + } + +end: + HeapFree(GetProcessHeap(), 0, enum_data); + if ( !CloseClipboard() ) hr = CLIPBRD_E_CANT_CLOSE; + return hr; } /************************************************************************ diff --git a/dlls/ole32/tests/clipboard.c b/dlls/ole32/tests/clipboard.c index 2b5c711d19..f06a4823ac 100644 --- a/dlls/ole32/tests/clipboard.c +++ b/dlls/ole32/tests/clipboard.c @@ -1361,10 +1361,160 @@ static void test_nonole_clipboard(void) OleUninitialize(); } +static void test_getdatahere(void) +{ + HRESULT hr; + IDataObject *src, *get; + FORMATETC fmt; + STGMEDIUM med; + + OleInitialize(NULL); + + hr = DataObjectImpl_CreateComplex(&src); + ok(hr == S_OK, "got %08x\n", hr); + + hr = OleSetClipboard(src); + ok(hr == S_OK, "got %08x\n", hr); + + hr = OleGetClipboard(&get); + ok(hr == S_OK, "got %08x\n", hr); + + /* global format -> global & stream */ + + DataObjectImpl_GetData_calls = 0; + DataObjectImpl_GetDataHere_calls = 0; + + InitFormatEtc(fmt, CF_TEXT, TYMED_HGLOBAL); + + med.pUnkForRelease = NULL; + med.tymed = TYMED_HGLOBAL; + U(med).hGlobal = GlobalAlloc(GMEM_MOVEABLE, 100); + hr = IDataObject_GetDataHere(get, &fmt, &med); + ok(hr == S_OK, "got %08x\n", hr); + ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed); + ReleaseStgMedium(&med); + ok(DataObjectImpl_GetDataHere_calls == 1, "called %d\n", DataObjectImpl_GetDataHere_calls); + ok(DataObjectImpl_GetData_calls == 1, "called %d\n", DataObjectImpl_GetData_calls); + + InitFormatEtc(fmt, CF_TEXT, 0); + + med.pUnkForRelease = NULL; + med.tymed = TYMED_HGLOBAL; + U(med).hGlobal = GlobalAlloc(GMEM_MOVEABLE, 100); + hr = IDataObject_GetDataHere(get, &fmt, &med); + ok(hr == S_OK, "got %08x\n", hr); + ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed); + ReleaseStgMedium(&med); + ok(DataObjectImpl_GetDataHere_calls == 2, "called %d\n", DataObjectImpl_GetDataHere_calls); + ok(DataObjectImpl_GetData_calls == 1, "called %d\n", DataObjectImpl_GetData_calls); + + med.pUnkForRelease = NULL; + med.tymed = TYMED_HGLOBAL; + U(med).hGlobal = GlobalAlloc(GMEM_MOVEABLE, 1); + hr = IDataObject_GetDataHere(get, &fmt, &med); + ok(hr == E_FAIL, "got %08x\n", hr); + ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed); + ReleaseStgMedium(&med); + ok(DataObjectImpl_GetDataHere_calls == 3, "called %d\n", DataObjectImpl_GetDataHere_calls); + ok(DataObjectImpl_GetData_calls == 1, "called %d\n", DataObjectImpl_GetData_calls); + + med.pUnkForRelease = NULL; + med.tymed = TYMED_ISTREAM; + CreateStreamOnHGlobal(NULL, TRUE, &U(med).pstm); + hr = IDataObject_GetDataHere(get, &fmt, &med); + ok(hr == S_OK, "got %08x\n", hr); + ok(med.tymed == TYMED_ISTREAM, "got %x\n", med.tymed); + ReleaseStgMedium(&med); + ok(DataObjectImpl_GetDataHere_calls == 4, "called %d\n", DataObjectImpl_GetDataHere_calls); + ok(DataObjectImpl_GetData_calls == 1, "called %d\n", DataObjectImpl_GetData_calls); + + med.pUnkForRelease = NULL; + med.tymed = TYMED_ISTORAGE; + StgCreateDocfile(NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE, 0, &U(med).pstg); + hr = IDataObject_GetDataHere(get, &fmt, &med); + ok(hr == E_FAIL, "got %08x\n", hr); + ok(med.tymed == TYMED_ISTORAGE, "got %x\n", med.tymed); + ReleaseStgMedium(&med); + ok(DataObjectImpl_GetDataHere_calls == 5, "called %d\n", DataObjectImpl_GetDataHere_calls); + ok(DataObjectImpl_GetData_calls == 1, "called %d\n", DataObjectImpl_GetData_calls); + + InitFormatEtc(fmt, cf_stream, 0); + + med.pUnkForRelease = NULL; + med.tymed = TYMED_HGLOBAL; + U(med).hGlobal = GlobalAlloc(GMEM_MOVEABLE, 100); + hr = IDataObject_GetDataHere(get, &fmt, &med); + ok(hr == S_OK, "got %08x\n", hr); + ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed); + ReleaseStgMedium(&med); + ok(DataObjectImpl_GetDataHere_calls == 7, "called %d\n", DataObjectImpl_GetDataHere_calls); + ok(DataObjectImpl_GetData_calls == 2, "called %d\n", DataObjectImpl_GetData_calls); + + med.pUnkForRelease = NULL; + med.tymed = TYMED_ISTREAM; + CreateStreamOnHGlobal(NULL, TRUE, &U(med).pstm); + hr = IDataObject_GetDataHere(get, &fmt, &med); + ok(hr == S_OK, "got %08x\n", hr); + ok(med.tymed == TYMED_ISTREAM, "got %x\n", med.tymed); + ReleaseStgMedium(&med); + ok(DataObjectImpl_GetDataHere_calls == 8, "called %d\n", DataObjectImpl_GetDataHere_calls); + ok(DataObjectImpl_GetData_calls == 2, "called %d\n", DataObjectImpl_GetData_calls); + + med.pUnkForRelease = NULL; + med.tymed = TYMED_ISTORAGE; + StgCreateDocfile(NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE, 0, &U(med).pstg); + hr = IDataObject_GetDataHere(get, &fmt, &med); + ok(hr == E_FAIL, "got %08x\n", hr); + ok(med.tymed == TYMED_ISTORAGE, "got %x\n", med.tymed); + ReleaseStgMedium(&med); + ok(DataObjectImpl_GetDataHere_calls == 9, "called %d\n", DataObjectImpl_GetDataHere_calls); + ok(DataObjectImpl_GetData_calls == 2, "called %d\n", DataObjectImpl_GetData_calls); + + InitFormatEtc(fmt, cf_storage, 0); + + med.pUnkForRelease = NULL; + med.tymed = TYMED_HGLOBAL; + U(med).hGlobal = GlobalAlloc(GMEM_MOVEABLE, 3000); + hr = IDataObject_GetDataHere(get, &fmt, &med); + ok(hr == S_OK, "got %08x\n", hr); + ok(med.tymed == TYMED_HGLOBAL, "got %x\n", med.tymed); + ReleaseStgMedium(&med); + ok(DataObjectImpl_GetDataHere_calls == 11, "called %d\n", DataObjectImpl_GetDataHere_calls); + ok(DataObjectImpl_GetData_calls == 3, "called %d\n", DataObjectImpl_GetData_calls); + + med.pUnkForRelease = NULL; + med.tymed = TYMED_ISTREAM; + CreateStreamOnHGlobal(NULL, TRUE, &U(med).pstm); + hr = IDataObject_GetDataHere(get, &fmt, &med); + ok(hr == S_OK, "got %08x\n", hr); + ok(med.tymed == TYMED_ISTREAM, "got %x\n", med.tymed); + ReleaseStgMedium(&med); + ok(DataObjectImpl_GetDataHere_calls == 12, "called %d\n", DataObjectImpl_GetDataHere_calls); + ok(DataObjectImpl_GetData_calls == 3, "called %d\n", DataObjectImpl_GetData_calls); + + med.pUnkForRelease = NULL; + med.tymed = TYMED_ISTORAGE; + StgCreateDocfile(NULL, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DELETEONRELEASE, 0, &U(med).pstg); + hr = IDataObject_GetDataHere(get, &fmt, &med); + ok(hr == S_OK, "got %08x\n", hr); + ok(med.tymed == TYMED_ISTORAGE, "got %x\n", med.tymed); + ReleaseStgMedium(&med); + ok(DataObjectImpl_GetDataHere_calls == 13, "called %d\n", DataObjectImpl_GetDataHere_calls); + ok(DataObjectImpl_GetData_calls == 3, "called %d\n", DataObjectImpl_GetData_calls); + + + IDataObject_Release(get); + IDataObject_Release(src); + + OleUninitialize(); + +} + START_TEST(clipboard) { test_set_clipboard(); test_consumer_refs(); test_flushed_getdata(); test_nonole_clipboard(); + test_getdatahere(); }