diff --git a/dlls/ole32/tests/clipboard.c b/dlls/ole32/tests/clipboard.c index f5ed06aa967..bc7e727f125 100644 --- a/dlls/ole32/tests/clipboard.c +++ b/dlls/ole32/tests/clipboard.c @@ -45,6 +45,8 @@ typedef struct DataObjectImpl { UINT fmtetc_cnt; HANDLE text; + IStream *stm; + IStorage *stg; } DataObjectImpl; typedef struct EnumFormatImpl { @@ -60,6 +62,8 @@ typedef struct EnumFormatImpl { static BOOL expect_DataObjectImpl_QueryGetData = TRUE; static ULONG DataObjectImpl_GetData_calls = 0; +static UINT cf_stream, cf_storage, cf_another, cf_onemore; + static HRESULT EnumFormatImpl_Create(FORMATETC *fmtetc, UINT size, LPENUMFORMATETC *lplpformatetc); static HRESULT WINAPI EnumFormatImpl_QueryInterface(IEnumFORMATETC *iface, REFIID riid, LPVOID *ppvObj) @@ -99,15 +103,21 @@ static HRESULT WINAPI EnumFormatImpl_Next(IEnumFORMATETC *iface, ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) { EnumFormatImpl *This = (EnumFormatImpl*)iface; - ULONG count = 0; + ULONG count, i; if(!rgelt) return E_INVALIDARG; - count = min(celt, This->fmtetc_cnt-This->cur); - if(count > 0) { - memcpy(rgelt, This->fmtetc+This->cur, count*sizeof(FORMATETC)); - This->cur += count; + count = min(celt, This->fmtetc_cnt - This->cur); + for(i = 0; i < count; i++, This->cur++, rgelt++) + { + *rgelt = This->fmtetc[This->cur]; + if(rgelt->ptd) + { + DWORD size = This->fmtetc[This->cur].ptd->tdSize; + rgelt->ptd = CoTaskMemAlloc(size); + memcpy(rgelt->ptd, This->fmtetc[This->cur].ptd, size); + } } if(pceltFetched) *pceltFetched = count; @@ -184,9 +194,15 @@ static ULONG WINAPI DataObjectImpl_Release(IDataObject* iface) DataObjectImpl *This = (DataObjectImpl*)iface; ULONG ref = InterlockedDecrement(&This->ref); - if(!ref) { + if(!ref) + { + int i; if(This->text) GlobalFree(This->text); - if(This->fmtetc) GlobalFree(This->fmtetc); + for(i = 0; i < This->fmtetc_cnt; i++) + HeapFree(GetProcessHeap(), 0, This->fmtetc[i].ptd); + HeapFree(GetProcessHeap(), 0, This->fmtetc); + if(This->stm) IStream_Release(This->stm); + if(This->stg) IStorage_Release(This->stg); HeapFree(GetProcessHeap(), 0, This); } @@ -196,24 +212,36 @@ static ULONG WINAPI DataObjectImpl_Release(IDataObject* iface) static HRESULT WINAPI DataObjectImpl_GetData(IDataObject* iface, FORMATETC *pformatetc, STGMEDIUM *pmedium) { DataObjectImpl *This = (DataObjectImpl*)iface; + UINT i; + BOOL foundFormat = FALSE; DataObjectImpl_GetData_calls++; if(pformatetc->lindex != -1) return DV_E_FORMATETC; - if(!(pformatetc->tymed & TYMED_HGLOBAL)) - return DV_E_TYMED; + for(i = 0; i < This->fmtetc_cnt; i++) + { + if(This->fmtetc[i].cfFormat == pformatetc->cfFormat) + { + foundFormat = TRUE; + if(This->fmtetc[i].tymed & pformatetc->tymed) + { + pmedium->pUnkForRelease = (LPUNKNOWN)iface; + IUnknown_AddRef(pmedium->pUnkForRelease); - if(This->text && pformatetc->cfFormat == CF_TEXT) - U(*pmedium).hGlobal = This->text; - else - return DV_E_FORMATETC; + if(pformatetc->cfFormat == CF_TEXT) + U(*pmedium).hGlobal = This->text; + else if(pformatetc->cfFormat == cf_stream) + U(*pmedium).pstm = This->stm; + else if(pformatetc->cfFormat == cf_storage) + U(*pmedium).pstg = This->stg; + return S_OK; + } + } + } - pmedium->tymed = TYMED_HGLOBAL; - pmedium->pUnkForRelease = (LPUNKNOWN)iface; - IUnknown_AddRef(pmedium->pUnkForRelease); - return S_OK; + return foundFormat ? DV_E_TYMED : DV_E_FORMATETC; } static HRESULT WINAPI DataObjectImpl_GetDataHere(IDataObject* iface, FORMATETC *pformatetc, STGMEDIUM *pmedium) @@ -315,6 +343,8 @@ static HRESULT DataObjectImpl_CreateText(LPCSTR text, LPDATAOBJECT *lplpdataobj) obj->text = GlobalAlloc(GMEM_MOVEABLE, strlen(text) + 1); strcpy(GlobalLock(obj->text), text); GlobalUnlock(obj->text); + obj->stm = NULL; + obj->stg = NULL; obj->fmtetc_cnt = 1; obj->fmtetc = HeapAlloc(GetProcessHeap(), 0, obj->fmtetc_cnt*sizeof(FORMATETC)); @@ -324,6 +354,55 @@ static HRESULT DataObjectImpl_CreateText(LPCSTR text, LPDATAOBJECT *lplpdataobj) return S_OK; } +static HRESULT DataObjectImpl_CreateComplex(LPDATAOBJECT *lplpdataobj) +{ + DataObjectImpl *obj; + const char *stm_data = "complex stream"; + const char *text_data = "complex text"; + ILockBytes *lbs; + static const WCHAR devname[] = {'m','y','d','e','v',0}; + DEVMODEW dm; + + obj = HeapAlloc(GetProcessHeap(), 0, sizeof(DataObjectImpl)); + obj->lpVtbl = &VT_DataObjectImpl; + obj->ref = 1; + obj->text = GlobalAlloc(GMEM_MOVEABLE, strlen(text_data) + 1); + strcpy(GlobalLock(obj->text), text_data); + GlobalUnlock(obj->text); + CreateStreamOnHGlobal(NULL, TRUE, &obj->stm); + IStream_Write(obj->stm, stm_data, strlen(stm_data), NULL); + + CreateILockBytesOnHGlobal(NULL, TRUE, &lbs); + StgCreateDocfileOnILockBytes(lbs, STGM_CREATE|STGM_SHARE_EXCLUSIVE|STGM_READWRITE, 0, &obj->stg); + ILockBytes_Release(lbs); + + obj->fmtetc_cnt = 5; + /* zeroing here since FORMATETC has a hole in it, and it's confusing to have this uninitialised. */ + obj->fmtetc = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, obj->fmtetc_cnt*sizeof(FORMATETC)); + InitFormatEtc(obj->fmtetc[0], CF_TEXT, TYMED_HGLOBAL); + InitFormatEtc(obj->fmtetc[1], cf_stream, TYMED_ISTREAM); + InitFormatEtc(obj->fmtetc[2], cf_storage, TYMED_ISTORAGE); + InitFormatEtc(obj->fmtetc[3], cf_another, TYMED_ISTORAGE|TYMED_ISTREAM|TYMED_HGLOBAL); + memset(&dm, 0, sizeof(dm)); + dm.dmSize = sizeof(dm); + dm.dmDriverExtra = 0; + lstrcpyW(dm.dmDeviceName, devname); + obj->fmtetc[3].ptd = HeapAlloc(GetProcessHeap(), 0, sizeof(DVTARGETDEVICE) + sizeof(devname) + dm.dmSize + dm.dmDriverExtra); + obj->fmtetc[3].ptd->tdSize = sizeof(DVTARGETDEVICE) + sizeof(devname) + dm.dmSize + dm.dmDriverExtra; + obj->fmtetc[3].ptd->tdDriverNameOffset = sizeof(DVTARGETDEVICE); + obj->fmtetc[3].ptd->tdDeviceNameOffset = 0; + obj->fmtetc[3].ptd->tdPortNameOffset = 0; + obj->fmtetc[3].ptd->tdExtDevmodeOffset = sizeof(DVTARGETDEVICE) + sizeof(devname); + lstrcpyW((WCHAR*)obj->fmtetc[3].ptd->tdData, devname); + memcpy(obj->fmtetc[3].ptd->tdData + sizeof(devname), &dm, dm.dmSize + dm.dmDriverExtra); + + InitFormatEtc(obj->fmtetc[4], cf_stream, 0xfffff); + obj->fmtetc[4].dwAspect = DVASPECT_ICON; + + *lplpdataobj = (LPDATAOBJECT)obj; + return S_OK; +} + static void test_get_clipboard(void) { HRESULT hr; @@ -423,11 +502,12 @@ static void test_get_clipboard(void) IDataObject_Release(data_obj); } -static void test_cf_dataobject(BOOL dataobject_active) +static void test_cf_dataobject(IDataObject *data) { UINT cf = 0; UINT cf_dataobject = RegisterClipboardFormatA("DataObject"); - BOOL found_dataobject = FALSE; + UINT cf_ole_priv_data = RegisterClipboardFormatA("Ole Private Data"); + BOOL found_dataobject = FALSE, found_priv_data = FALSE; OpenClipboard(NULL); do @@ -442,28 +522,125 @@ static void test_cf_dataobject(BOOL dataobject_active) found_dataobject = TRUE; ok(size >= sizeof(*ptr), "size %d\n", size); - if(dataobject_active) + if(data) ok(*ptr == clip_owner, "hwnd %p clip_owner %p\n", *ptr, clip_owner); else /* ole clipboard flushed */ ok(*ptr == NULL, "hwnd %p\n", *ptr); GlobalUnlock(h); } + else if(cf == cf_ole_priv_data) + { + found_priv_data = TRUE; + if(data) + { + HGLOBAL h = GetClipboardData(cf); + DWORD *ptr = GlobalLock(h); + DWORD size = GlobalSize(h); + + if(size != ptr[1]) + win_skip("Ole Private Data in win9x format\n"); + else + { + HRESULT hr; + IEnumFORMATETC *enum_fmt; + DWORD count = 0; + FORMATETC fmt; + struct formatetcetc + { + FORMATETC fmt; + BOOL first_use_of_cf; + DWORD res[2]; + } *fmt_ptr; + struct priv_data + { + DWORD res1; + DWORD size; + DWORD res2; + DWORD count; + DWORD res3[2]; + struct formatetcetc fmts[1]; + } *priv = (struct priv_data*)ptr; + CLIPFORMAT cfs_seen[10]; + + hr = IDataObject_EnumFormatEtc(data, DATADIR_GET, &enum_fmt); + ok(hr == S_OK, "got %08x\n", hr); + fmt_ptr = priv->fmts; + + while(IEnumFORMATETC_Next(enum_fmt, 1, &fmt, NULL) == S_OK) + { + int i; + BOOL seen_cf = FALSE; + + ok(fmt_ptr->fmt.cfFormat == fmt.cfFormat, + "got %08x expected %08x\n", fmt_ptr->fmt.cfFormat, fmt.cfFormat); + ok(fmt_ptr->fmt.dwAspect == fmt.dwAspect, "got %08x expected %08x\n", + fmt_ptr->fmt.dwAspect, fmt.dwAspect); + ok(fmt_ptr->fmt.lindex == fmt.lindex, "got %08x expected %08x\n", + fmt_ptr->fmt.lindex, fmt.lindex); + ok(fmt_ptr->fmt.tymed == fmt.tymed, "got %08x expected %08x\n", + fmt_ptr->fmt.tymed, fmt.tymed); + for(i = 0; i < count; i++) + if(fmt_ptr->fmt.cfFormat == cfs_seen[i]) + { + seen_cf = TRUE; + break; + } + cfs_seen[count] = fmt.cfFormat; + ok(fmt_ptr->first_use_of_cf == seen_cf ? FALSE : TRUE, "got %08x expected %08x\n", + fmt_ptr->first_use_of_cf, !seen_cf); + ok(fmt_ptr->res[0] == 0, "got %08x\n", fmt_ptr->res[1]); + ok(fmt_ptr->res[1] == 0, "got %08x\n", fmt_ptr->res[2]); + if(fmt.ptd) + { + DVTARGETDEVICE *target; + + ok(fmt_ptr->fmt.ptd != NULL, "target device offset zero\n"); + target = (DVTARGETDEVICE*)((char*)priv + (DWORD)fmt_ptr->fmt.ptd); + ok(!memcmp(target, fmt.ptd, fmt.ptd->tdSize), "target devices differ\n"); + CoTaskMemFree(fmt.ptd); + } + fmt_ptr++; + count++; + } + ok(priv->res1 == 0, "got %08x\n", priv->res1); + ok(priv->res2 == 1, "got %08x\n", priv->res2); + ok(priv->count == count, "got %08x expected %08x\n", priv->count, count); + ok(priv->res3[0] == 0, "got %08x\n", priv->res3[0]); + ok(priv->res3[1] == 0, "got %08x\n", priv->res3[1]); + + GlobalUnlock(h); + IEnumFORMATETC_Release(enum_fmt); + } + } + } } while(cf); CloseClipboard(); ok(found_dataobject, "didn't find cf_dataobject\n"); +todo_wine + ok(found_priv_data, "didn't find cf_ole_priv_data\n"); } static void test_set_clipboard(void) { HRESULT hr; ULONG ref; - LPDATAOBJECT data1, data2; + LPDATAOBJECT data1, data2, data_cmpl; + + cf_stream = RegisterClipboardFormatA("stream format"); + cf_storage = RegisterClipboardFormatA("storage format"); + cf_another = RegisterClipboardFormatA("another format"); + cf_onemore = RegisterClipboardFormatA("one more format"); + hr = DataObjectImpl_CreateText("data1", &data1); ok(SUCCEEDED(hr), "Failed to create data1 object: 0x%08x\n", hr); if(FAILED(hr)) return; hr = DataObjectImpl_CreateText("data2", &data2); ok(SUCCEEDED(hr), "Failed to create data2 object: 0x%08x\n", hr); + if(FAILED(hr)) + return; + hr = DataObjectImpl_CreateComplex(&data_cmpl); + ok(SUCCEEDED(hr), "Failed to create complex data object: 0x%08x\n", hr); if(FAILED(hr)) return; @@ -484,7 +661,7 @@ static void test_set_clipboard(void) hr = OleSetClipboard(data1); ok(hr == S_OK, "failed to set clipboard to data1, hr = 0x%08x\n", hr); - test_cf_dataobject(TRUE); + test_cf_dataobject(data1); hr = OleIsCurrentClipboard(data1); ok(hr == S_OK, "expected current clipboard to be data1, hr = 0x%08x\n", hr); @@ -513,7 +690,13 @@ static void test_set_clipboard(void) hr = OleIsCurrentClipboard(NULL); ok(hr == S_FALSE, "expect S_FALSE, hr = 0x%08x\n", hr); - test_cf_dataobject(FALSE); + test_cf_dataobject(NULL); + + ok(OleSetClipboard(NULL) == S_OK, "failed to clear clipboard, hr = 0x%08x\n", hr); + + hr = OleSetClipboard(data_cmpl); + ok(hr == S_OK, "failed to set clipboard to complex data, hr = 0x%08x\n", hr); + test_cf_dataobject(data_cmpl); ok(OleSetClipboard(NULL) == S_OK, "failed to clear clipboard, hr = 0x%08x\n", hr); @@ -521,6 +704,8 @@ static void test_set_clipboard(void) ok(ref == 0, "expected data1 ref=0, got %d\n", ref); ref = IDataObject_Release(data2); ok(ref == 0, "expected data2 ref=0, got %d\n", ref); + ref = IDataObject_Release(data_cmpl); + ok(ref == 0, "expected data_cmpl ref=0, got %d\n", ref); OleUninitialize(); }