1 /*
2  * Drag and Drop Tests
3  *
4  * Copyright 2007 Robert Shearman
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
20 
21 #include "precomp.h"
22 
23 #define METHOD_LIST \
24     METHOD(DO_EnumFormatEtc), \
25     METHOD(DO_QueryGetData), \
26     METHOD(EnumFMT_Next), \
27     METHOD(EnumFMT_Reset), \
28     METHOD(EnumFMT_Skip), \
29     METHOD(DS_QueryContinueDrag), \
30     METHOD(DS_GiveFeedback), \
31     METHOD(DT_DragEnter), \
32     METHOD(DT_Drop), \
33     METHOD(DT_DragLeave), \
34     METHOD(DT_DragOver), \
35     METHOD(DoDragDrop_effect_in), \
36     METHOD(DoDragDrop_ret), \
37     METHOD(DoDragDrop_effect_out), \
38     METHOD(end_seq)
39 
40 #define METHOD(x) x
41 enum method
42 {
43     METHOD_LIST
44 };
45 #undef METHOD
46 
47 #define METHOD(x) #x
48 static const char *method_names[] =
49 {
50     METHOD_LIST
51 };
52 #undef METHOD
53 #undef METHOD_LIST
54 
55 struct method_call
56 {
57     enum method method;
58     DWORD expect_param;
59 
60     HRESULT set_ret;
61     DWORD set_param;
62 
63     int called_todo : 1;
64 };
65 
66 const struct method_call *call_ptr;
67 
68 static HRESULT check_expect_(enum method func, DWORD expect_param, DWORD *set_param, const char *file, int line )
69 {
70     HRESULT hr;
71 
72     do
73     {
74         todo_wine_if(call_ptr->called_todo)
75             ok_( file, line )( func == call_ptr->method, "unexpected call %s instead of %s\n",
76                                method_names[func], method_names[call_ptr->method] );
77         if (call_ptr->method == func) break;
78     } while ((++call_ptr)->method != end_seq);
79 
80     ok_( file, line )( expect_param == call_ptr->expect_param, "%s: unexpected param %08x expected %08x\n",
81                        method_names[func], expect_param, call_ptr->expect_param );
82     if (set_param) *set_param = call_ptr->set_param;
83     hr = call_ptr->set_ret;
84     if (call_ptr->method != end_seq) call_ptr++;
85    return hr;
86 }
87 
88 #define check_expect(func, expect_param, set_param) \
89     check_expect_((func), (expect_param), (set_param), __FILE__, __LINE__)
90 
91 
92 struct method_call call_lists[][30] =
93 {
94     { /* First QueryContinueDrag rets DRAGDROP_S_DROP */
95         { DoDragDrop_effect_in, 0, 0, DROPEFFECT_COPY, 0 },
96         { DO_EnumFormatEtc, 0, S_OK, 0, 1 },
97         { EnumFMT_Next, 0, S_OK, 0, 1 },
98         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
99         { EnumFMT_Reset, 0, S_OK, 0, 1 },
100         { EnumFMT_Next, 0, S_OK, 0, 1 },
101         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
102         { DO_QueryGetData, 0, S_OK, 0, 1 },
103 
104         { DS_QueryContinueDrag, 0, DRAGDROP_S_DROP, 0, 0 },
105         { DT_DragEnter, DROPEFFECT_COPY, S_OK, DROPEFFECT_COPY, 0 },
106         { DS_GiveFeedback, DROPEFFECT_COPY, DRAGDROP_S_USEDEFAULTCURSORS, 0, 0 },
107         { DT_Drop, DROPEFFECT_COPY, 0xbeefbeef, DROPEFFECT_COPY, 0 },
108 
109         { DoDragDrop_ret, 0xbeefbeef, 0, 0, 0, },
110         { DoDragDrop_effect_out, DROPEFFECT_COPY, 0, 0, 0 },
111         { end_seq, 0, 0, 0, 0 }
112     },
113     { /* As above, but initial effects == 0 */
114         { DoDragDrop_effect_in, 0, 0, 0, 0 },
115         { DO_EnumFormatEtc, 0, S_OK, 0, 1 },
116         { EnumFMT_Next, 0, S_OK, 0, 1 },
117         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
118         { EnumFMT_Reset, 0, S_OK, 0, 1 },
119         { EnumFMT_Next, 0, S_OK, 0, 1 },
120         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
121         { DO_QueryGetData, 0, S_OK, 0, 1 },
122 
123         { DS_QueryContinueDrag, 0, DRAGDROP_S_DROP, 0, 0 },
124         { DT_DragEnter, 0, S_OK, DROPEFFECT_COPY, 0 },
125         { DS_GiveFeedback, 0, DRAGDROP_S_USEDEFAULTCURSORS, 0, 0 },
126         { DT_DragLeave, 0, 0, 0, 0 },
127 
128         { DoDragDrop_ret, DRAGDROP_S_DROP, 0, 0, 0 },
129         { DoDragDrop_effect_out, 0, 0, 0, 0 },
130         { end_seq, 0, 0, 0, 0 }
131     },
132     { /* Multiple initial effects */
133         { DoDragDrop_effect_in, 0, 0, DROPEFFECT_COPY | DROPEFFECT_MOVE, 0 },
134         { DO_EnumFormatEtc, 0, S_OK, 0, 1 },
135         { EnumFMT_Next, 0, S_OK, 0, 1 },
136         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
137         { EnumFMT_Reset, 0, S_OK, 0, 1 },
138         { EnumFMT_Next, 0, S_OK, 0, 1 },
139         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
140         { DO_QueryGetData, 0, S_OK, 0, 1 },
141 
142         { DS_QueryContinueDrag, 0, DRAGDROP_S_DROP, 0, 0 },
143         { DT_DragEnter, DROPEFFECT_COPY | DROPEFFECT_MOVE, S_OK, DROPEFFECT_COPY, 0 },
144         { DS_GiveFeedback, DROPEFFECT_COPY, DRAGDROP_S_USEDEFAULTCURSORS, 0, 0 },
145         { DT_Drop, DROPEFFECT_COPY | DROPEFFECT_MOVE, 0, 0, 0 },
146 
147         { DoDragDrop_ret, DRAGDROP_S_DROP, 0, 0, 0 },
148         { DoDragDrop_effect_out, 0, 0, 0, 0 },
149         { end_seq, 0, 0, 0, 0 }
150     },
151     { /* First couple of QueryContinueDrag return S_OK followed by a drop */
152         { DoDragDrop_effect_in, 0, 0, DROPEFFECT_COPY | DROPEFFECT_MOVE, 0 },
153         { DO_EnumFormatEtc, 0, S_OK, 0, 1 },
154         { EnumFMT_Next, 0, S_OK, 0, 1 },
155         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
156         { EnumFMT_Reset, 0, S_OK, 0, 1 },
157         { EnumFMT_Next, 0, S_OK, 0, 1 },
158         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
159         { DO_QueryGetData, 0, S_OK, 0, 1 },
160 
161         { DS_QueryContinueDrag, 0, S_OK, 0, 0 },
162         { DT_DragEnter, DROPEFFECT_COPY | DROPEFFECT_MOVE, S_OK, DROPEFFECT_COPY, 0 },
163         { DS_GiveFeedback, DROPEFFECT_COPY, DRAGDROP_S_USEDEFAULTCURSORS, 0, 0 },
164         { DT_DragOver, DROPEFFECT_COPY | DROPEFFECT_MOVE, S_OK, DROPEFFECT_COPY, 0 },
165         { DS_GiveFeedback, DROPEFFECT_COPY, DRAGDROP_S_USEDEFAULTCURSORS, 0, 0 },
166 
167         { DS_QueryContinueDrag, 0, S_OK, 0, 0 },
168         { DT_DragOver, DROPEFFECT_COPY | DROPEFFECT_MOVE, S_OK, DROPEFFECT_COPY, 0 },
169         { DS_GiveFeedback, DROPEFFECT_COPY, DRAGDROP_S_USEDEFAULTCURSORS, 0, 0 },
170 
171         { DS_QueryContinueDrag, 0, DRAGDROP_S_DROP, 0, 0 },
172         { DT_Drop, DROPEFFECT_COPY | DROPEFFECT_MOVE, 0, 0, 0 },
173 
174         { DoDragDrop_ret, DRAGDROP_S_DROP, 0, 0, 0 },
175         { DoDragDrop_effect_out, 0, 0, 0, 0 },
176         { end_seq, 0, 0, 0, 0 }
177     },
178     { /* First QueryContinueDrag cancels */
179         { DoDragDrop_effect_in, 0, 0, DROPEFFECT_COPY | DROPEFFECT_MOVE, 0 },
180         { DO_EnumFormatEtc, 0, S_OK, 0, 1 },
181         { EnumFMT_Next, 0, S_OK, 0, 1 },
182         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
183         { EnumFMT_Reset, 0, S_OK, 0, 1 },
184         { EnumFMT_Next, 0, S_OK, 0, 1 },
185         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
186         { DO_QueryGetData, 0, S_OK, 0, 1 },
187 
188         { DS_QueryContinueDrag, 0, DRAGDROP_S_CANCEL, 0, 0 },
189 
190         { DoDragDrop_ret, DRAGDROP_S_CANCEL, 0, 0, 0 },
191         { DoDragDrop_effect_out, 0, 0, 0, 0 },
192         { end_seq, 0, 0, 0, 0 }
193     },
194     { /* First couple of QueryContinueDrag return S_OK followed by a cancel */
195         { DoDragDrop_effect_in, 0, 0, DROPEFFECT_COPY | DROPEFFECT_MOVE, 0 },
196         { DO_EnumFormatEtc, 0, S_OK, 0, 1 },
197         { EnumFMT_Next, 0, S_OK, 0, 1 },
198         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
199         { EnumFMT_Reset, 0, S_OK, 0, 1 },
200         { EnumFMT_Next, 0, S_OK, 0, 1 },
201         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
202         { DO_QueryGetData, 0, S_OK, 0, 1 },
203 
204         { DS_QueryContinueDrag, 0, S_OK, 0, 0 },
205         { DT_DragEnter, DROPEFFECT_COPY | DROPEFFECT_MOVE, S_OK, DROPEFFECT_COPY, 0 },
206         { DS_GiveFeedback, DROPEFFECT_COPY, DRAGDROP_S_USEDEFAULTCURSORS, 0, 0 },
207         { DT_DragOver, DROPEFFECT_COPY | DROPEFFECT_MOVE, S_OK, DROPEFFECT_COPY, 0 },
208         { DS_GiveFeedback, DROPEFFECT_COPY, DRAGDROP_S_USEDEFAULTCURSORS, 0, 0 },
209 
210         { DS_QueryContinueDrag, 0, S_OK, 0, 0 },
211         { DT_DragOver, DROPEFFECT_COPY | DROPEFFECT_MOVE, S_OK, DROPEFFECT_COPY, 0 },
212         { DS_GiveFeedback, DROPEFFECT_COPY, DRAGDROP_S_USEDEFAULTCURSORS, 0, 0 },
213 
214         { DS_QueryContinueDrag, 0, DRAGDROP_S_CANCEL, 0, 0 },
215         { DT_DragLeave, 0, 0, 0, 0 },
216 
217         { DoDragDrop_ret, DRAGDROP_S_CANCEL, 0, 0, 0 },
218         { DoDragDrop_effect_out, 0, 0, 0, 0 },
219         { end_seq, 0, 0, 0, 0 }
220     },
221     { /* First couple of QueryContinueDrag return S_OK followed by a E_FAIL */
222         { DoDragDrop_effect_in, 0, 0, DROPEFFECT_COPY | DROPEFFECT_MOVE, 0 },
223         { DO_EnumFormatEtc, 0, S_OK, 0, 1 },
224         { EnumFMT_Next, 0, S_OK, 0, 1 },
225         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
226         { EnumFMT_Reset, 0, S_OK, 0, 1 },
227         { EnumFMT_Next, 0, S_OK, 0, 1 },
228         { EnumFMT_Next, 0, S_FALSE, 0, 1 },
229         { DO_QueryGetData, 0, S_OK, 0, 1 },
230 
231         { DS_QueryContinueDrag, 0, S_OK, 0, 0 },
232         { DT_DragEnter, DROPEFFECT_COPY | DROPEFFECT_MOVE, S_OK, DROPEFFECT_COPY, 0 },
233         { DS_GiveFeedback, DROPEFFECT_COPY, DRAGDROP_S_USEDEFAULTCURSORS, 0, 0 },
234         { DT_DragOver, DROPEFFECT_COPY | DROPEFFECT_MOVE, S_OK, DROPEFFECT_COPY, 0 },
235         { DS_GiveFeedback, DROPEFFECT_COPY, DRAGDROP_S_USEDEFAULTCURSORS, 0, 0 },
236 
237         { DS_QueryContinueDrag, 0, S_OK, 0, 0 },
238         { DT_DragOver, DROPEFFECT_COPY | DROPEFFECT_MOVE, S_OK, DROPEFFECT_COPY, 0 },
239         { DS_GiveFeedback, DROPEFFECT_COPY, DRAGDROP_S_USEDEFAULTCURSORS, 0, 0 },
240 
241         { DS_QueryContinueDrag, 0, E_FAIL, 0, 0 },
242         { DT_DragLeave, 0, 0, 0, 0 },
243 
244         { DoDragDrop_ret, E_FAIL, 0, 0, 0 },
245         { DoDragDrop_effect_out, 0, 0, 0, 0 },
246         { end_seq, 0, 0, 0, 0 }
247     },
248 };
249 
250 static int droptarget_refs;
251 
252 /* helper macros to make tests a bit leaner */
253 #define ok_ole_success(hr, func) ok(hr == S_OK, func " failed with error 0x%08x\n", hr)
254 
255 static HRESULT WINAPI DropTarget_QueryInterface(IDropTarget* iface, REFIID riid,
256                                                 void** ppvObject)
257 {
258     ok(0, "DropTarget_QueryInterface() shouldn't be called\n");
259     if (IsEqualIID(riid, &IID_IUnknown) ||
260         IsEqualIID(riid, &IID_IDropTarget))
261     {
262         IDropTarget_AddRef(iface);
263         *ppvObject = iface;
264         return S_OK;
265     }
266     *ppvObject = NULL;
267     return E_NOINTERFACE;
268 }
269 
270 static ULONG WINAPI DropTarget_AddRef(IDropTarget* iface)
271 {
272     droptarget_refs++;
273     return droptarget_refs;
274 }
275 
276 static ULONG WINAPI DropTarget_Release(IDropTarget* iface)
277 {
278     droptarget_refs--;
279     return droptarget_refs;
280 }
281 
282 static HRESULT WINAPI DropTarget_DragEnter(IDropTarget* iface,
283                                            IDataObject* pDataObj,
284                                            DWORD grfKeyState, POINTL pt,
285                                            DWORD* pdwEffect)
286 {
287     return check_expect(DT_DragEnter, *pdwEffect, pdwEffect);
288 }
289 
290 static HRESULT WINAPI DropTarget_DragOver(IDropTarget* iface,
291                                           DWORD grfKeyState,
292                                           POINTL pt,
293                                           DWORD* pdwEffect)
294 {
295     return check_expect(DT_DragOver, *pdwEffect, pdwEffect);
296 }
297 
298 static HRESULT WINAPI DropTarget_DragLeave(IDropTarget* iface)
299 {
300     return check_expect(DT_DragLeave, 0, NULL);
301 }
302 
303 static HRESULT WINAPI DropTarget_Drop(IDropTarget* iface,
304                                       IDataObject* pDataObj, DWORD grfKeyState,
305                                       POINTL pt, DWORD* pdwEffect)
306 {
307     return check_expect(DT_Drop, *pdwEffect, pdwEffect);
308 }
309 
310 static const IDropTargetVtbl DropTarget_VTbl =
311 {
312     DropTarget_QueryInterface,
313     DropTarget_AddRef,
314     DropTarget_Release,
315     DropTarget_DragEnter,
316     DropTarget_DragOver,
317     DropTarget_DragLeave,
318     DropTarget_Drop
319 };
320 
321 static IDropTarget DropTarget = { &DropTarget_VTbl };
322 
323 static HRESULT WINAPI DropSource_QueryInterface(IDropSource *iface, REFIID riid, void **ppObj)
324 {
325     if (IsEqualIID(riid, &IID_IUnknown) ||
326         IsEqualIID(riid, &IID_IDropSource))
327     {
328         *ppObj = iface;
329         IDropSource_AddRef(iface);
330         return S_OK;
331     }
332     return E_NOINTERFACE;
333 }
334 
335 static ULONG WINAPI DropSource_AddRef(IDropSource *iface)
336 {
337     return 2;
338 }
339 
340 static ULONG WINAPI DropSource_Release(IDropSource *iface)
341 {
342     return 1;
343 }
344 
345 static HRESULT WINAPI DropSource_QueryContinueDrag(
346     IDropSource *iface,
347     BOOL fEscapePressed,
348     DWORD grfKeyState)
349 {
350     return check_expect(DS_QueryContinueDrag, 0, NULL);
351 }
352 
353 static HRESULT WINAPI DropSource_GiveFeedback(
354     IDropSource *iface,
355     DWORD dwEffect)
356 {
357     return check_expect(DS_GiveFeedback, dwEffect, NULL);
358 }
359 
360 static const IDropSourceVtbl dropsource_vtbl = {
361     DropSource_QueryInterface,
362     DropSource_AddRef,
363     DropSource_Release,
364     DropSource_QueryContinueDrag,
365     DropSource_GiveFeedback
366 };
367 
368 static IDropSource DropSource = { &dropsource_vtbl };
369 
370 static HRESULT WINAPI EnumFORMATETC_QueryInterface(IEnumFORMATETC *iface,
371         REFIID riid, void **ppvObj)
372 {
373     ok(0, "unexpected call\n");
374     return E_NOTIMPL;
375 }
376 
377 static ULONG WINAPI EnumFORMATETC_AddRef(IEnumFORMATETC *iface)
378 {
379     return 2;
380 }
381 
382 static ULONG WINAPI EnumFORMATETC_Release(IEnumFORMATETC *iface)
383 {
384     return 1;
385 }
386 
387 static HRESULT WINAPI EnumFORMATETC_Next(IEnumFORMATETC *iface,
388         ULONG celt, FORMATETC *rgelt, ULONG *pceltFetched)
389 {
390     static FORMATETC format = { CF_TEXT, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
391     HRESULT hr = check_expect(EnumFMT_Next, 0, NULL);
392 
393     ok(celt == 1, "celt = %d\n", celt);
394     ok(rgelt != NULL, "rgelt == NULL\n");
395     ok(pceltFetched == NULL, "pceltFetched != NULL\n");
396 
397     *rgelt = format;
398     return hr;
399 }
400 
401 static HRESULT WINAPI EnumFORMATETC_Skip(IEnumFORMATETC *iface, ULONG celt)
402 {
403     return check_expect(EnumFMT_Skip, 0, NULL);
404 }
405 
406 static HRESULT WINAPI EnumFORMATETC_Reset(IEnumFORMATETC *iface)
407 {
408     return check_expect(EnumFMT_Reset, 0, NULL);
409 }
410 
411 static HRESULT WINAPI EnumFORMATETC_Clone(IEnumFORMATETC *iface,
412         IEnumFORMATETC **ppenum)
413 {
414     ok(0, "unexpected call\n");
415     return E_NOTIMPL;
416 }
417 
418 static const IEnumFORMATETCVtbl enumformatetc_vtbl = {
419     EnumFORMATETC_QueryInterface,
420     EnumFORMATETC_AddRef,
421     EnumFORMATETC_Release,
422     EnumFORMATETC_Next,
423     EnumFORMATETC_Skip,
424     EnumFORMATETC_Reset,
425     EnumFORMATETC_Clone
426 };
427 
428 static IEnumFORMATETC EnumFORMATETC = { &enumformatetc_vtbl };
429 
430 static HRESULT WINAPI DataObject_QueryInterface(
431     IDataObject *iface,
432     REFIID riid,
433     void **pObj)
434 {
435     if (IsEqualIID(riid, &IID_IUnknown) ||
436         IsEqualIID(riid, &IID_IDataObject))
437     {
438         *pObj = iface;
439         IDataObject_AddRef(iface);
440         return S_OK;
441     }
442 
443     trace("DataObject_QueryInterface: %s\n", wine_dbgstr_guid(riid));
444     return E_NOINTERFACE;
445 }
446 
447 static ULONG WINAPI DataObject_AddRef(IDataObject *iface)
448 {
449     return 2;
450 }
451 
452 static ULONG WINAPI DataObject_Release(IDataObject *iface)
453 {
454     return 1;
455 }
456 
457 static HRESULT WINAPI DataObject_GetData(
458     IDataObject *iface,
459     FORMATETC *pformatetcIn,
460     STGMEDIUM *pmedium)
461 {
462     ok(0, "unexpected call\n");
463     return E_NOTIMPL;
464 }
465 
466 static HRESULT WINAPI DataObject_GetDataHere(
467     IDataObject *iface,
468     FORMATETC *pformatetc,
469     STGMEDIUM *pmedium)
470 {
471     ok(0, "unexpected call\n");
472     return E_NOTIMPL;
473 }
474 
475 static HRESULT WINAPI DataObject_QueryGetData(
476     IDataObject *iface,
477     FORMATETC *pformatetc)
478 {
479     return check_expect(DO_QueryGetData, 0, NULL);
480 }
481 
482 static HRESULT WINAPI DataObject_GetCanonicalFormatEtc(
483     IDataObject *iface,
484     FORMATETC *pformatectIn,
485     FORMATETC *pformatetcOut)
486 {
487     ok(0, "unexpected call\n");
488     return E_NOTIMPL;
489 }
490 
491 static HRESULT WINAPI DataObject_SetData(
492     IDataObject *iface,
493     FORMATETC *pformatetc,
494     STGMEDIUM *pmedium,
495     BOOL fRelease)
496 {
497     ok(0, "unexpected call\n");
498     return E_NOTIMPL;
499 }
500 
501 static HRESULT WINAPI DataObject_EnumFormatEtc(
502     IDataObject *iface,
503     DWORD dwDirection,
504     IEnumFORMATETC **ppenumFormatEtc)
505 {
506     HRESULT hr = check_expect(DO_EnumFormatEtc, 0, NULL);
507     *ppenumFormatEtc = &EnumFORMATETC;
508     return hr;
509 }
510 
511 static HRESULT WINAPI DataObject_DAdvise(
512     IDataObject *iface,
513     FORMATETC *pformatetc,
514     DWORD advf,
515     IAdviseSink *pAdvSink,
516     DWORD *pdwConnection)
517 {
518     ok(0, "unexpected call\n");
519     return E_NOTIMPL;
520 }
521 
522 static HRESULT WINAPI DataObject_DUnadvise(
523     IDataObject *iface,
524     DWORD dwConnection)
525 {
526     ok(0, "unexpected call\n");
527     return E_NOTIMPL;
528 }
529 
530 static HRESULT WINAPI DataObject_EnumDAdvise(
531     IDataObject *iface,
532     IEnumSTATDATA **ppenumAdvise)
533 {
534     ok(0, "unexpected call\n");
535     return E_NOTIMPL;
536 }
537 
538 static const IDataObjectVtbl dataobject_vtbl = {
539     DataObject_QueryInterface,
540     DataObject_AddRef,
541     DataObject_Release,
542     DataObject_GetData,
543     DataObject_GetDataHere,
544     DataObject_QueryGetData,
545     DataObject_GetCanonicalFormatEtc,
546     DataObject_SetData,
547     DataObject_EnumFormatEtc,
548     DataObject_DAdvise,
549     DataObject_DUnadvise,
550     DataObject_EnumDAdvise
551 };
552 
553 static IDataObject DataObject = { &dataobject_vtbl };
554 
555 static ATOM register_dummy_class(void)
556 {
557     WNDCLASSA wc =
558     {
559         0,
560         DefWindowProcA,
561         0,
562         0,
563         GetModuleHandleA(NULL),
564         NULL,
565         LoadCursorA(NULL, (LPSTR)IDC_ARROW),
566         (HBRUSH)(COLOR_BTNFACE+1),
567         NULL,
568         "WineOleTestClass",
569     };
570 
571     return RegisterClassA(&wc);
572 }
573 
574 static void test_Register_Revoke(void)
575 {
576     HANDLE prop;
577     HRESULT hr;
578     HWND hwnd;
579 
580     hwnd = CreateWindowA("WineOleTestClass", "Test", 0,
581         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL,
582         NULL, NULL, NULL);
583 
584     hr = RegisterDragDrop(hwnd, &DropTarget);
585     ok(hr == E_OUTOFMEMORY ||
586         broken(hr == CO_E_NOTINITIALIZED), /* NT4 */
587         "RegisterDragDrop without OLE initialized should have returned E_OUTOFMEMORY instead of 0x%08x\n", hr);
588 
589     OleInitialize(NULL);
590 
591     hr = RegisterDragDrop(hwnd, NULL);
592     ok(hr == E_INVALIDARG, "RegisterDragDrop with NULL IDropTarget * should return E_INVALIDARG instead of 0x%08x\n", hr);
593 
594     hr = RegisterDragDrop(NULL, &DropTarget);
595     ok(hr == DRAGDROP_E_INVALIDHWND, "RegisterDragDrop with NULL hwnd should return DRAGDROP_E_INVALIDHWND instead of 0x%08x\n", hr);
596 
597     hr = RegisterDragDrop((HWND)0xdeadbeef, &DropTarget);
598     ok(hr == DRAGDROP_E_INVALIDHWND, "RegisterDragDrop with garbage hwnd should return DRAGDROP_E_INVALIDHWND instead of 0x%08x\n", hr);
599 
600     ok(droptarget_refs == 0, "DropTarget refs should be zero not %d\n", droptarget_refs);
601     hr = RegisterDragDrop(hwnd, &DropTarget);
602     ok_ole_success(hr, "RegisterDragDrop");
603     ok(droptarget_refs >= 1, "DropTarget refs should be at least one\n");
604 
605     prop = GetPropA(hwnd, "OleDropTargetInterface");
606     ok(prop == &DropTarget, "expected IDropTarget pointer %p, got %p\n", &DropTarget, prop);
607 
608     hr = RegisterDragDrop(hwnd, &DropTarget);
609     ok(hr == DRAGDROP_E_ALREADYREGISTERED, "RegisterDragDrop with already registered hwnd should return DRAGDROP_E_ALREADYREGISTERED instead of 0x%08x\n", hr);
610 
611     ok(droptarget_refs >= 1, "DropTarget refs should be at least one\n");
612     OleUninitialize();
613 
614     /* Win 8 releases the ref in OleUninitialize() */
615     if (droptarget_refs >= 1)
616     {
617         hr = RevokeDragDrop(hwnd);
618         ok_ole_success(hr, "RevokeDragDrop");
619         ok(droptarget_refs == 0 ||
620            broken(droptarget_refs == 1), /* NT4 */
621            "DropTarget refs should be zero not %d\n", droptarget_refs);
622     }
623 
624     hr = RevokeDragDrop(NULL);
625     ok(hr == DRAGDROP_E_INVALIDHWND, "RevokeDragDrop with NULL hwnd should return DRAGDROP_E_INVALIDHWND instead of 0x%08x\n", hr);
626 
627     DestroyWindow(hwnd);
628 
629     /* try to revoke with already destroyed window */
630     OleInitialize(NULL);
631 
632     hwnd = CreateWindowA("WineOleTestClass", "Test", 0,
633         CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL,
634         NULL, NULL, NULL);
635 
636     hr = RegisterDragDrop(hwnd, &DropTarget);
637     ok(hr == S_OK, "got 0x%08x\n", hr);
638 
639     DestroyWindow(hwnd);
640 
641     hr = RevokeDragDrop(hwnd);
642     ok(hr == DRAGDROP_E_INVALIDHWND, "got 0x%08x\n", hr);
643 
644     OleUninitialize();
645 }
646 
647 static void test_DoDragDrop(void)
648 {
649     DWORD effect;
650     HRESULT hr;
651     HWND hwnd;
652     RECT rect;
653     int seq;
654 
655     hwnd = CreateWindowExA(WS_EX_TOPMOST, "WineOleTestClass", "Test", 0,
656         CW_USEDEFAULT, CW_USEDEFAULT, 100, 100, NULL,
657         NULL, NULL, NULL);
658     ok(IsWindow(hwnd), "failed to create window\n");
659 
660     hr = OleInitialize(NULL);
661     ok(hr == S_OK, "got 0x%08x\n", hr);
662 
663     hr = RegisterDragDrop(hwnd, &DropTarget);
664     ok(hr == S_OK, "got 0x%08x\n", hr);
665 
666     /* incomplete arguments set */
667     hr = DoDragDrop(NULL, NULL, 0, NULL);
668     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
669 
670     hr = DoDragDrop(NULL, &DropSource, 0, NULL);
671     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
672 
673     hr = DoDragDrop(&DataObject, NULL, 0, NULL);
674     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
675 
676     hr = DoDragDrop(NULL, NULL, 0, &effect);
677     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
678 
679     hr = DoDragDrop(&DataObject, &DropSource, 0, NULL);
680     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
681 
682     hr = DoDragDrop(NULL, &DropSource, 0, &effect);
683     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
684 
685     hr = DoDragDrop(&DataObject, NULL, 0, &effect);
686     ok(hr == E_INVALIDARG, "got 0x%08x\n", hr);
687 
688     ShowWindow(hwnd, SW_SHOW);
689     GetWindowRect(hwnd, &rect);
690     ok(SetCursorPos(rect.left+50, rect.top+50), "SetCursorPos failed\n");
691 
692     for (seq = 0; seq < sizeof(call_lists) / sizeof(call_lists[0]); seq++)
693     {
694         DWORD effect_in;
695         trace("%d\n", seq);
696         call_ptr = call_lists[seq];
697         effect_in = call_ptr->set_param;
698         call_ptr++;
699 
700         hr = DoDragDrop(&DataObject, &DropSource, effect_in, &effect);
701         check_expect(DoDragDrop_ret, hr, NULL);
702         check_expect(DoDragDrop_effect_out, effect, NULL);
703     }
704 
705     OleUninitialize();
706 
707     DestroyWindow(hwnd);
708 }
709 
710 START_TEST(dragdrop)
711 {
712     register_dummy_class();
713 
714     test_Register_Revoke();
715 #ifdef __REACTOS__
716     if (!winetest_interactive &&
717         !strcmp(winetest_platform, "windows"))
718     {
719         skip("ROSTESTS-182: Skipping ole32_winetest:dragdrop test_DoDragDrop because it hangs on WHS-Testbot. Set winetest_interactive to run it anyway.\n");
720         return;
721     }
722 #endif
723     test_DoDragDrop();
724 }
725