| /* |
| * Drag and Drop Tests |
| * |
| * Copyright 2007 Robert Shearman |
| * |
| * 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 |
| */ |
| |
| #define _WIN32_DCOM |
| #define COBJMACROS |
| #define CONST_VTABLE |
| |
| #include <stdarg.h> |
| #include <stdio.h> |
| |
| #include "windef.h" |
| #include "winbase.h" |
| #include "objbase.h" |
| |
| #include "wine/test.h" |
| |
| #define DEFINE_EXPECT(func) \ |
| static BOOL expect_ ## func = FALSE, called_ ## func = FALSE |
| |
| #define SET_EXPECT(func) \ |
| expect_ ## func = TRUE |
| |
| #define CHECK_EXPECT2(func) \ |
| do { \ |
| ok(expect_ ##func, "unexpected call " #func "\n"); \ |
| called_ ## func = TRUE; \ |
| }while(0) |
| |
| #define CHECK_EXPECT(func) \ |
| do { \ |
| CHECK_EXPECT2(func); \ |
| expect_ ## func = FALSE; \ |
| }while(0) |
| |
| #define CHECK_CALLED(func) \ |
| do { \ |
| ok(called_ ## func, "expected " #func "\n"); \ |
| expect_ ## func = called_ ## func = FALSE; \ |
| }while(0) |
| |
| DEFINE_EXPECT(DataObject_EnumFormatEtc); |
| DEFINE_EXPECT(EnumFORMATETC_Next); |
| DEFINE_EXPECT(EnumFORMATETC_Reset); |
| DEFINE_EXPECT(DataObject_QueryGetData); |
| DEFINE_EXPECT(DropSource_QueryContinueDrag); |
| DEFINE_EXPECT(DropTarget_DragEnter); |
| DEFINE_EXPECT(DropSource_GiveFeedback); |
| DEFINE_EXPECT(DropTarget_Drop); |
| DEFINE_EXPECT(DropTarget_DragLeave); |
| |
| static int droptarget_refs; |
| |
| /* helper macros to make tests a bit leaner */ |
| #define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr) |
| |
| static HRESULT WINAPI DropTarget_QueryInterface(IDropTarget* iface, REFIID riid, |
| void** ppvObject) |
| { |
| ok(0, "DropTarget_QueryInterface() shouldn't be called\n"); |
| if (IsEqualIID(riid, &IID_IUnknown) || |
| IsEqualIID(riid, &IID_IDropTarget)) |
| { |
| IDropTarget_AddRef(iface); |
| *ppvObject = iface; |
| return S_OK; |
| } |
| *ppvObject = NULL; |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI DropTarget_AddRef(IDropTarget* iface) |
| { |
| droptarget_refs++; |
| return droptarget_refs; |
| } |
| |
| static ULONG WINAPI DropTarget_Release(IDropTarget* iface) |
| { |
| droptarget_refs--; |
| return droptarget_refs; |
| } |
| |
| static HRESULT WINAPI DropTarget_DragEnter(IDropTarget* iface, |
| IDataObject* pDataObj, |
| DWORD grfKeyState, POINTL pt, |
| DWORD* pdwEffect) |
| { |
| CHECK_EXPECT(DropTarget_DragEnter); |
| *pdwEffect = DROPEFFECT_COPY; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI DropTarget_DragOver(IDropTarget* iface, |
| DWORD grfKeyState, |
| POINTL pt, |
| DWORD* pdwEffect) |
| { |
| ok(0, "unexpected call\n"); |
| *pdwEffect = DROPEFFECT_COPY; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI DropTarget_DragLeave(IDropTarget* iface) |
| { |
| CHECK_EXPECT(DropTarget_DragLeave); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DropTarget_Drop(IDropTarget* iface, |
| IDataObject* pDataObj, DWORD grfKeyState, |
| POINTL pt, DWORD* pdwEffect) |
| { |
| CHECK_EXPECT(DropTarget_Drop); |
| return 0xbeefbeef; |
| } |
| |
| static const IDropTargetVtbl DropTarget_VTbl = |
| { |
| DropTarget_QueryInterface, |
| DropTarget_AddRef, |
| DropTarget_Release, |
| DropTarget_DragEnter, |
| DropTarget_DragOver, |
| DropTarget_DragLeave, |
| DropTarget_Drop |
| }; |
| |
| static IDropTarget DropTarget = { &DropTarget_VTbl }; |
| |
| static HRESULT WINAPI DropSource_QueryInterface(IDropSource *iface, REFIID riid, void **ppObj) |
| { |
| if (IsEqualIID(riid, &IID_IUnknown) || |
| IsEqualIID(riid, &IID_IDropSource)) |
| { |
| *ppObj = iface; |
| IDropSource_AddRef(iface); |
| return S_OK; |
| } |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI DropSource_AddRef(IDropSource *iface) |
| { |
| return 2; |
| } |
| |
| static ULONG WINAPI DropSource_Release(IDropSource *iface) |
| { |
| return 1; |
| } |
| |
| static HRESULT WINAPI DropSource_QueryContinueDrag( |
| IDropSource *iface, |
| BOOL fEscapePressed, |
| DWORD grfKeyState) |
| { |
| CHECK_EXPECT(DropSource_QueryContinueDrag); |
| return DRAGDROP_S_DROP; |
| } |
| |
| static HRESULT WINAPI DropSource_GiveFeedback( |
| IDropSource *iface, |
| DWORD dwEffect) |
| { |
| CHECK_EXPECT(DropSource_GiveFeedback); |
| return DRAGDROP_S_USEDEFAULTCURSORS; |
| } |
| |
| static const IDropSourceVtbl dropsource_vtbl = { |
| DropSource_QueryInterface, |
| DropSource_AddRef, |
| DropSource_Release, |
| DropSource_QueryContinueDrag, |
| DropSource_GiveFeedback |
| }; |
| |
| static IDropSource DropSource = { &dropsource_vtbl }; |
| |
| static HRESULT WINAPI EnumFORMATETC_QueryInterface(IEnumFORMATETC *iface, |
| REFIID riid, void **ppvObj) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static ULONG WINAPI EnumFORMATETC_AddRef(IEnumFORMATETC *iface) |
| { |
| return 2; |
| } |
| |
| static ULONG WINAPI EnumFORMATETC_Release(IEnumFORMATETC *iface) |
| { |
| return 1; |
| } |
| |
| static BOOL formats_enumerated; |
| static HRESULT WINAPI EnumFORMATETC_Next(IEnumFORMATETC *iface, |
| ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched) |
| { |
| static FORMATETC format = { CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; |
| |
| CHECK_EXPECT2(EnumFORMATETC_Next); |
| |
| ok(celt == 1, "celt = %d\n", celt); |
| ok(rgelt != NULL, "rgelt == NULL\n"); |
| ok(pceltFetched == NULL, "pceltFetched != NULL\n"); |
| |
| if(formats_enumerated) |
| return S_FALSE; |
| |
| *rgelt = format; |
| formats_enumerated = TRUE; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI EnumFORMATETC_Skip(IEnumFORMATETC *iface, ULONG celt) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI EnumFORMATETC_Reset(IEnumFORMATETC *iface) |
| { |
| CHECK_EXPECT(EnumFORMATETC_Reset); |
| formats_enumerated = FALSE; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI EnumFORMATETC_Clone(IEnumFORMATETC *iface, |
| IEnumFORMATETC **ppenum) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static const IEnumFORMATETCVtbl enumformatetc_vtbl = { |
| EnumFORMATETC_QueryInterface, |
| EnumFORMATETC_AddRef, |
| EnumFORMATETC_Release, |
| EnumFORMATETC_Next, |
| EnumFORMATETC_Skip, |
| EnumFORMATETC_Reset, |
| EnumFORMATETC_Clone |
| }; |
| |
| static IEnumFORMATETC EnumFORMATETC = { &enumformatetc_vtbl }; |
| |
| static HRESULT WINAPI DataObject_QueryInterface( |
| IDataObject *iface, |
| REFIID riid, |
| void **pObj) |
| { |
| if (IsEqualIID(riid, &IID_IUnknown) || |
| IsEqualIID(riid, &IID_IDataObject)) |
| { |
| *pObj = iface; |
| IDataObject_AddRef(iface); |
| return S_OK; |
| } |
| |
| trace("DataObject_QueryInterface: %s\n", wine_dbgstr_guid(riid)); |
| return E_NOINTERFACE; |
| } |
| |
| static ULONG WINAPI DataObject_AddRef(IDataObject *iface) |
| { |
| return 2; |
| } |
| |
| static ULONG WINAPI DataObject_Release(IDataObject *iface) |
| { |
| return 1; |
| } |
| |
| static HRESULT WINAPI DataObject_GetData( |
| IDataObject *iface, |
| FORMATETC *pformatetcIn, |
| STGMEDIUM *pmedium) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DataObject_GetDataHere( |
| IDataObject *iface, |
| FORMATETC *pformatetc, |
| STGMEDIUM *pmedium) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DataObject_QueryGetData( |
| IDataObject *iface, |
| FORMATETC *pformatetc) |
| { |
| CHECK_EXPECT(DataObject_QueryGetData); |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI DataObject_GetCanonicalFormatEtc( |
| IDataObject *iface, |
| FORMATETC *pformatectIn, |
| FORMATETC *pformatetcOut) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DataObject_SetData( |
| IDataObject *iface, |
| FORMATETC *pformatetc, |
| STGMEDIUM *pmedium, |
| BOOL fRelease) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DataObject_EnumFormatEtc( |
| IDataObject *iface, |
| DWORD dwDirection, |
| IEnumFORMATETC **ppenumFormatEtc) |
| { |
| CHECK_EXPECT(DataObject_EnumFormatEtc); |
| *ppenumFormatEtc = &EnumFORMATETC; |
| formats_enumerated = FALSE; |
| return S_OK; |
| } |
| |
| static HRESULT WINAPI DataObject_DAdvise( |
| IDataObject *iface, |
| FORMATETC *pformatetc, |
| DWORD advf, |
| IAdviseSink *pAdvSink, |
| DWORD *pdwConnection) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DataObject_DUnadvise( |
| IDataObject *iface, |
| DWORD dwConnection) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static HRESULT WINAPI DataObject_EnumDAdvise( |
| IDataObject *iface, |
| IEnumSTATDATA **ppenumAdvise) |
| { |
| ok(0, "unexpected call\n"); |
| return E_NOTIMPL; |
| } |
| |
| static const IDataObjectVtbl dataobject_vtbl = { |
| DataObject_QueryInterface, |
| DataObject_AddRef, |
| DataObject_Release, |
| DataObject_GetData, |
| DataObject_GetDataHere, |
| DataObject_QueryGetData, |
| DataObject_GetCanonicalFormatEtc, |
| DataObject_SetData, |
| DataObject_EnumFormatEtc, |
| DataObject_DAdvise, |
| DataObject_DUnadvise, |
| DataObject_EnumDAdvise |
| }; |
| |
| static IDataObject DataObject = { &dataobject_vtbl }; |
| |
| static ATOM register_dummy_class(void) |
| { |
| WNDCLASSA wc = |
| { |
| 0, |
| DefWindowProcA, |
| 0, |
| 0, |
| GetModuleHandleA(NULL), |
| NULL, |
| LoadCursorA(NULL, (LPSTR)IDC_ARROW), |
| (HBRUSH)(COLOR_BTNFACE+1), |
| NULL, |
| "WineOleTestClass", |
| }; |
| |
| return RegisterClassA(&wc); |
| } |
| |
| static void test_Register_Revoke(void) |
| { |
| HANDLE prop; |
| HRESULT hr; |
| HWND hwnd; |
| |
| hwnd = CreateWindowA("WineOleTestClass", "Test", 0, |
| CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, |
| NULL, NULL, NULL); |
| |
| hr = RegisterDragDrop(hwnd, &DropTarget); |
| ok(hr == E_OUTOFMEMORY || |
| broken(hr == CO_E_NOTINITIALIZED), /* NT4 */ |
| "RegisterDragDrop without OLE initialized should have returned E_OUTOFMEMORY instead of 0x%08x\n", hr); |
| |
| OleInitialize(NULL); |
| |
| hr = RegisterDragDrop(hwnd, NULL); |
| ok(hr == E_INVALIDARG, "RegisterDragDrop with NULL IDropTarget * should return E_INVALIDARG instead of 0x%08x\n", hr); |
| |
| hr = RegisterDragDrop(NULL, &DropTarget); |
| ok(hr == DRAGDROP_E_INVALIDHWND, "RegisterDragDrop with NULL hwnd should return DRAGDROP_E_INVALIDHWND instead of 0x%08x\n", hr); |
| |
| hr = RegisterDragDrop((HWND)0xdeadbeef, &DropTarget); |
| ok(hr == DRAGDROP_E_INVALIDHWND, "RegisterDragDrop with garbage hwnd should return DRAGDROP_E_INVALIDHWND instead of 0x%08x\n", hr); |
| |
| ok(droptarget_refs == 0, "DropTarget refs should be zero not %d\n", droptarget_refs); |
| hr = RegisterDragDrop(hwnd, &DropTarget); |
| ok_ole_success(hr, "RegisterDragDrop"); |
| ok(droptarget_refs >= 1, "DropTarget refs should be at least one\n"); |
| |
| prop = GetPropA(hwnd, "OleDropTargetInterface"); |
| ok(prop == &DropTarget, "expected IDropTarget pointer %p, got %p\n", &DropTarget, prop); |
| |
| hr = RegisterDragDrop(hwnd, &DropTarget); |
| ok(hr == DRAGDROP_E_ALREADYREGISTERED, "RegisterDragDrop with already registered hwnd should return DRAGDROP_E_ALREADYREGISTERED instead of 0x%08x\n", hr); |
| |
| ok(droptarget_refs >= 1, "DropTarget refs should be at least one\n"); |
| OleUninitialize(); |
| |
| /* Win 8 releases the ref in OleUninitialize() */ |
| if (droptarget_refs >= 1) |
| { |
| hr = RevokeDragDrop(hwnd); |
| ok_ole_success(hr, "RevokeDragDrop"); |
| ok(droptarget_refs == 0 || |
| broken(droptarget_refs == 1), /* NT4 */ |
| "DropTarget refs should be zero not %d\n", droptarget_refs); |
| } |
| |
| hr = RevokeDragDrop(NULL); |
| ok(hr == DRAGDROP_E_INVALIDHWND, "RevokeDragDrop with NULL hwnd should return DRAGDROP_E_INVALIDHWND instead of 0x%08x\n", hr); |
| |
| DestroyWindow(hwnd); |
| |
| /* try to revoke with already destroyed window */ |
| OleInitialize(NULL); |
| |
| hwnd = CreateWindowA("WineOleTestClass", "Test", 0, |
| CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, |
| NULL, NULL, NULL); |
| |
| hr = RegisterDragDrop(hwnd, &DropTarget); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| DestroyWindow(hwnd); |
| |
| hr = RevokeDragDrop(hwnd); |
| ok(hr == DRAGDROP_E_INVALIDHWND, "got 0x%08x\n", hr); |
| |
| OleUninitialize(); |
| } |
| |
| static void test_DoDragDrop(void) |
| { |
| DWORD effect; |
| HRESULT hr; |
| HWND hwnd; |
| RECT rect; |
| |
| hwnd = CreateWindowExA(WS_EX_TOPMOST, "WineOleTestClass", "Test", 0, |
| CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, NULL, |
| NULL, NULL, NULL); |
| ok(IsWindow(hwnd), "failed to create window\n"); |
| |
| hr = OleInitialize(NULL); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| hr = RegisterDragDrop(hwnd, &DropTarget); |
| ok(hr == S_OK, "got 0x%08x\n", hr); |
| |
| /* incomplete arguments set */ |
| hr = DoDragDrop(NULL, NULL, 0, NULL); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = DoDragDrop(NULL, &DropSource, 0, NULL); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = DoDragDrop(&DataObject, NULL, 0, NULL); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = DoDragDrop(NULL, NULL, 0, &effect); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = DoDragDrop(&DataObject, &DropSource, 0, NULL); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = DoDragDrop(NULL, &DropSource, 0, &effect); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| hr = DoDragDrop(&DataObject, NULL, 0, &effect); |
| ok(hr == E_INVALIDARG, "got 0x%08x\n", hr); |
| |
| ShowWindow(hwnd, SW_SHOW); |
| GetWindowRect(hwnd, &rect); |
| ok(SetCursorPos(rect.left+50, rect.top+50), "SetCursorPos failed\n"); |
| SET_EXPECT(DataObject_EnumFormatEtc); |
| SET_EXPECT(EnumFORMATETC_Next); |
| SET_EXPECT(EnumFORMATETC_Reset); |
| SET_EXPECT(DataObject_QueryGetData); |
| SET_EXPECT(DropSource_QueryContinueDrag); |
| SET_EXPECT(DropTarget_DragEnter); |
| SET_EXPECT(DropSource_GiveFeedback); |
| SET_EXPECT(DropTarget_Drop); |
| hr = DoDragDrop(&DataObject, &DropSource, DROPEFFECT_COPY, &effect); |
| ok(hr == 0xbeefbeef, "got 0x%08x\n", hr); |
| todo_wine CHECK_CALLED(DataObject_EnumFormatEtc); |
| todo_wine CHECK_CALLED(EnumFORMATETC_Next); |
| todo_wine CHECK_CALLED(EnumFORMATETC_Reset); |
| todo_wine CHECK_CALLED(DataObject_QueryGetData); |
| CHECK_CALLED(DropSource_QueryContinueDrag); |
| CHECK_CALLED(DropTarget_DragEnter); |
| CHECK_CALLED(DropSource_GiveFeedback); |
| CHECK_CALLED(DropTarget_Drop); |
| |
| SET_EXPECT(DataObject_EnumFormatEtc); |
| SET_EXPECT(EnumFORMATETC_Next); |
| SET_EXPECT(EnumFORMATETC_Reset); |
| SET_EXPECT(DataObject_QueryGetData); |
| SET_EXPECT(DropSource_QueryContinueDrag); |
| SET_EXPECT(DropTarget_DragEnter); |
| SET_EXPECT(DropSource_GiveFeedback); |
| SET_EXPECT(DropTarget_DragLeave); |
| hr = DoDragDrop(&DataObject, &DropSource, 0, &effect); |
| ok(hr == DRAGDROP_S_DROP, "got 0x%08x\n", hr); |
| todo_wine CHECK_CALLED(DataObject_EnumFormatEtc); |
| todo_wine CHECK_CALLED(EnumFORMATETC_Next); |
| todo_wine CHECK_CALLED(EnumFORMATETC_Reset); |
| todo_wine CHECK_CALLED(DataObject_QueryGetData); |
| CHECK_CALLED(DropSource_QueryContinueDrag); |
| CHECK_CALLED(DropTarget_DragEnter); |
| CHECK_CALLED(DropSource_GiveFeedback); |
| CHECK_CALLED(DropTarget_DragLeave); |
| |
| OleUninitialize(); |
| |
| DestroyWindow(hwnd); |
| } |
| |
| START_TEST(dragdrop) |
| { |
| register_dummy_class(); |
| |
| test_Register_Revoke(); |
| test_DoDragDrop(); |
| } |