1 /*
2 * PROJECT: ReactOS api tests
3 * LICENSE: LGPLv2.1+ - See COPYING.LIB in the top level directory
4 * PURPOSE: Test for Drag & Drop
5 * PROGRAMMER: Katayama Hirofumi MZ <katayama.hirofumi.mz@gmail.com>
6 */
7
8 #include "shelltest.h"
9 #include <shlwapi.h>
10
11 #define NDEBUG
12 #include <debug.h>
13 #include <stdio.h>
14
15 #define TESTFILENAME L"DragDropTest.txt"
16 #define DROPPED_ON_FILE L"DragDroppedOn.lnk"
17
18 static CComPtr<IShellFolder> s_pDesktop;
19
20 static WCHAR s_szSrcTestFile[MAX_PATH];
21 static WCHAR s_szDestFolder[MAX_PATH];
22 static WCHAR s_szDestTestFile[MAX_PATH];
23 static WCHAR s_szDestLinkSpec[MAX_PATH];
24 static WCHAR s_szDroppedToItem[MAX_PATH];
25
26 enum OP
27 {
28 OP_NONE,
29 OP_COPY,
30 OP_MOVE,
31 OP_LINK,
32 OP_NONE_OR_COPY,
33 OP_NONE_OR_MOVE,
34 OP_NONE_OR_LINK
35 };
36
37 #define D_NONE DROPEFFECT_NONE
38 #define D_COPY DROPEFFECT_COPY
39 #define D_MOVE DROPEFFECT_MOVE
40 #define D_LINK DROPEFFECT_LINK
41 #define D_NONE_OR_COPY 0xAABBCCDD
42 #define D_NONE_OR_MOVE 0x11223344
43 #define D_NONE_OR_LINK 0x55667788
44
45 struct TEST_ENTRY
46 {
47 int line;
48 OP op;
49 HRESULT hr1;
50 HRESULT hr2;
51 DWORD dwKeyState;
52 DWORD dwEffects1;
53 DWORD dwEffects2;
54 DWORD dwEffects3;
55 };
56
57 static const TEST_ENTRY s_TestEntries[] =
58 {
59 // MK_LBUTTON
60 { __LINE__, OP_NONE, S_OK, S_OK, MK_LBUTTON, D_NONE, D_NONE, D_NONE },
61 { __LINE__, OP_COPY, S_OK, S_OK, MK_LBUTTON, D_COPY, D_COPY, D_COPY },
62 { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON, D_COPY | D_MOVE, D_MOVE, D_NONE },
63 { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON, D_COPY | D_MOVE | D_LINK, D_MOVE, D_NONE },
64 { __LINE__, OP_COPY, S_OK, S_OK, MK_LBUTTON, D_COPY | D_LINK, D_COPY, D_COPY },
65 { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON, D_MOVE, D_MOVE, D_NONE },
66 { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON, D_MOVE | D_LINK, D_MOVE, D_NONE },
67 { __LINE__, OP_LINK, S_OK, S_OK, MK_LBUTTON, D_LINK, D_LINK, D_LINK },
68
69 // MK_LBUTTON | MK_SHIFT
70 { __LINE__, OP_NONE, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_NONE, D_NONE, D_NONE },
71 { __LINE__, OP_NONE_OR_COPY, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_COPY, D_NONE_OR_COPY, D_NONE_OR_COPY },
72 { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_COPY | D_MOVE, D_MOVE, D_NONE },
73 { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_COPY | D_MOVE | D_LINK, D_MOVE, D_NONE },
74 { __LINE__, OP_NONE_OR_COPY, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_COPY | D_LINK, D_NONE_OR_COPY, D_NONE_OR_COPY },
75 { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_MOVE, D_MOVE, D_NONE },
76 { __LINE__, OP_MOVE, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_MOVE | D_LINK, D_MOVE, D_NONE },
77 { __LINE__, OP_NONE_OR_LINK, S_OK, S_OK, MK_LBUTTON | MK_SHIFT, D_LINK, D_NONE_OR_LINK, D_NONE_OR_LINK },
78
79 // MK_LBUTTON | MK_SHIFT | MK_CONTROL
80 #define MK_LBUTTON_SHIFT_CTRL (MK_LBUTTON | MK_SHIFT | MK_CONTROL)
81 { __LINE__, OP_NONE, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_NONE, D_NONE, D_NONE },
82 { __LINE__, OP_NONE_OR_COPY, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_COPY, D_NONE_OR_COPY, D_NONE_OR_COPY },
83 { __LINE__, OP_NONE_OR_COPY, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_COPY | D_MOVE, D_NONE_OR_COPY, D_NONE_OR_COPY },
84 { __LINE__, OP_LINK, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_COPY | D_MOVE | D_LINK, D_LINK, D_LINK },
85 { __LINE__, OP_LINK, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_COPY | D_LINK, D_LINK, D_LINK },
86 { __LINE__, OP_NONE_OR_MOVE, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_MOVE, D_NONE_OR_MOVE, D_NONE },
87 { __LINE__, OP_LINK, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_MOVE | D_LINK, D_LINK, D_LINK },
88 { __LINE__, OP_LINK, S_OK, S_OK, MK_LBUTTON_SHIFT_CTRL, D_LINK, D_LINK, D_LINK },
89 #undef MK_LBUTTON_SHIFT_CTRL
90
91 // MK_LBUTTON | MK_CONTROL
92 { __LINE__, OP_NONE, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_NONE, D_NONE, D_NONE },
93 { __LINE__, OP_COPY, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_COPY, D_COPY, D_COPY },
94 { __LINE__, OP_COPY, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_COPY | D_MOVE, D_COPY, D_COPY },
95 { __LINE__, OP_COPY, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_COPY | D_MOVE | D_LINK, D_COPY, D_COPY },
96 { __LINE__, OP_COPY, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_COPY | D_LINK, D_COPY, D_COPY },
97 { __LINE__, OP_NONE_OR_MOVE, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_MOVE, D_NONE_OR_MOVE, D_NONE },
98 { __LINE__, OP_NONE_OR_MOVE, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_MOVE | D_LINK, D_NONE_OR_MOVE, D_NONE },
99 { __LINE__, OP_NONE_OR_LINK, S_OK, S_OK, MK_LBUTTON | MK_CONTROL, D_LINK, D_NONE_OR_LINK, D_NONE_OR_LINK },
100 };
101
DoCreateTestFile(LPCWSTR pszFileName)102 static void DoCreateTestFile(LPCWSTR pszFileName)
103 {
104 FILE *fp = _wfopen(pszFileName, L"wb");
105 ok(fp != NULL, "fp is NULL for '%S'\n", pszFileName);
106 fclose(fp);
107 }
108
DoCreateShortcut(LPCWSTR pszLnkFileName,LPCWSTR pszTargetPathName)109 HRESULT DoCreateShortcut(
110 LPCWSTR pszLnkFileName,
111 LPCWSTR pszTargetPathName)
112 {
113 CComPtr<IPersistFile> ppf;
114 CComPtr<IShellLinkW> psl;
115 HRESULT hr;
116
117 hr = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER,
118 IID_IShellLinkW, (LPVOID *)&psl);
119 if (SUCCEEDED(hr))
120 {
121 psl->SetPath(pszTargetPathName);
122
123 hr = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
124 if (SUCCEEDED(hr))
125 {
126 hr = ppf->Save(pszLnkFileName, TRUE);
127 }
128 }
129
130 return hr;
131 }
132
133 static HRESULT
GetUIObjectOfAbsPidl(PIDLIST_ABSOLUTE pidl,REFIID riid,LPVOID * ppvOut)134 GetUIObjectOfAbsPidl(PIDLIST_ABSOLUTE pidl, REFIID riid, LPVOID *ppvOut)
135 {
136 *ppvOut = NULL;
137
138 LPCITEMIDLIST pidlLast;
139 CComPtr<IShellFolder> psf;
140 HRESULT hr = SHBindToParent(pidl, IID_IShellFolder, (LPVOID *)&psf,
141 &pidlLast);
142 if (FAILED(hr))
143 return hr;
144
145 hr = psf->GetUIObjectOf(NULL, 1, &pidlLast, riid, NULL, ppvOut);
146 return hr;
147 }
148
149 static HRESULT
GetUIObjectOfPath(LPCWSTR pszPath,REFIID riid,LPVOID * ppvOut)150 GetUIObjectOfPath(LPCWSTR pszPath, REFIID riid, LPVOID *ppvOut)
151 {
152 *ppvOut = NULL;
153
154 PIDLIST_ABSOLUTE pidl = ILCreateFromPathW(pszPath);
155 if (!pidl)
156 return E_FAIL;
157
158 HRESULT hr = GetUIObjectOfAbsPidl(pidl, riid, ppvOut);
159
160 CoTaskMemFree(pidl);
161
162 return hr;
163 }
164
DoSpecExistsW(LPCWSTR pszSpec)165 BOOL DoSpecExistsW(LPCWSTR pszSpec)
166 {
167 WIN32_FIND_DATAW find;
168 HANDLE hFind = FindFirstFileW(pszSpec, &find);
169 if (hFind != INVALID_HANDLE_VALUE)
170 {
171 FindClose(hFind);
172 return TRUE;
173 }
174 return FALSE;
175 }
176
DoDeleteSpecW(LPCWSTR pszSpec)177 void DoDeleteSpecW(LPCWSTR pszSpec)
178 {
179 WCHAR szPath[MAX_PATH], szFile[MAX_PATH];
180 lstrcpyW(szPath, pszSpec);
181 PathRemoveFileSpecW(szPath);
182
183 WIN32_FIND_DATAW find;
184 HANDLE hFind = FindFirstFileW(pszSpec, &find);
185 if (hFind != INVALID_HANDLE_VALUE)
186 {
187 do
188 {
189 lstrcpyW(szFile, szPath);
190 PathAppendW(szFile, find.cFileName);
191 DeleteFileW(szFile);
192 } while (FindNextFileW(hFind, &find));
193
194 FindClose(hFind);
195 }
196 }
197
DoTestEntry(const TEST_ENTRY * pEntry)198 static void DoTestEntry(const TEST_ENTRY *pEntry)
199 {
200 int line = pEntry->line;
201 HRESULT hr;
202 PIDLIST_ABSOLUTE pidlDesktop = NULL;
203 CComPtr<IDropTarget> pDropTarget;
204 CComPtr<IDataObject> pDataObject;
205
206 // get the desktop PIDL
207 SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pidlDesktop);
208 ok(!!pidlDesktop, "pidlDesktop is NULL\n");
209
210 // build paths
211 //
212 SHGetPathFromIDListW(pidlDesktop, s_szDroppedToItem);
213 PathAppendW(s_szDroppedToItem, DROPPED_ON_FILE);
214
215 GetModuleFileNameW(NULL, s_szSrcTestFile, _countof(s_szSrcTestFile));
216 PathRemoveFileSpecW(s_szSrcTestFile);
217 PathAppendW(s_szSrcTestFile, TESTFILENAME);
218
219 lstrcpyW(s_szDestTestFile, s_szDestFolder);
220 PathAppendW(s_szDestTestFile, TESTFILENAME);
221
222 lstrcpyW(s_szDestLinkSpec, s_szDestFolder);
223 PathAppendW(s_szDestLinkSpec, L"*DragDropTest*.lnk");
224
225 //trace("s_szSrcTestFile: '%S'\n", s_szSrcTestFile);
226 //trace("s_szDestTestFile: '%S'\n", s_szDestTestFile);
227 //trace("s_szDestLinkSpec: '%S'\n", s_szDestLinkSpec);
228 //trace("s_szDroppedToItem: '%S'\n", s_szDroppedToItem);
229
230 // create or delete files
231 //
232 DoCreateTestFile(s_szSrcTestFile);
233 DeleteFileW(s_szDestTestFile);
234 DoDeleteSpecW(s_szDestLinkSpec);
235 DeleteFileW(s_szDroppedToItem);
236 DoCreateShortcut(s_szDroppedToItem, s_szDestFolder);
237
238 // check file existence
239 //
240 ok(PathIsDirectoryW(s_szDestFolder), "s_szDestFolder is not directory\n");
241 ok(PathFileExistsW(s_szSrcTestFile), "s_szSrcTestFile doesn't exist\n");
242 ok(!DoSpecExistsW(s_szDestLinkSpec), "s_szDestLinkSpec doesn't exist\n");
243 ok(!PathFileExistsW(s_szDestTestFile), "s_szDestTestFile exists\n");
244
245 // get an IDataObject
246 pDataObject = NULL;
247 hr = GetUIObjectOfPath(s_szSrcTestFile, IID_IDataObject, (LPVOID *)&pDataObject);
248 ok_long(hr, S_OK);
249
250 // get an IDropTarget
251 CComPtr<IEnumIDList> pEnumIDList;
252 PIDLIST_ABSOLUTE pidl = NULL;
253 hr = s_pDesktop->EnumObjects(NULL, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS,
254 &pEnumIDList);
255 ok_long(hr, S_OK);
256 while (pEnumIDList->Next(1, &pidl, NULL) == S_OK)
257 {
258 WCHAR szText[MAX_PATH];
259 SHGetPathFromIDListW(pidl, szText);
260 if (wcsstr(szText, DROPPED_ON_FILE) != NULL)
261 {
262 break;
263 }
264 CoTaskMemFree(pidl);
265 pidl = NULL;
266 }
267 ok(pidl != NULL, "Line %d: pidl is NULL\n", line);
268 pDropTarget = NULL;
269 PITEMID_CHILD pidlLast = ILFindLastID(pidl);
270 hr = s_pDesktop->GetUIObjectOf(NULL, 1, &pidlLast, IID_IDropTarget,
271 NULL, (LPVOID *)&pDropTarget);
272 CoTaskMemFree(pidl);
273 ok_long(hr, S_OK);
274
275 if (!pDropTarget)
276 {
277 skip("Line %d: pDropTarget was NULL\n", line);
278
279 // clean up
280 DeleteFileW(s_szSrcTestFile);
281 DeleteFileW(s_szDestTestFile);
282 DoDeleteSpecW(s_szDestLinkSpec);
283 ILFree(pidlDesktop);
284
285 return;
286 }
287
288 // DragEnter
289 POINTL ptl = { 0, 0 };
290 DWORD dwKeyState = pEntry->dwKeyState;
291 DWORD dwEffects = pEntry->dwEffects1;
292 hr = pDropTarget->DragEnter(pDataObject, dwKeyState, ptl, &dwEffects);
293
294 ok(hr == pEntry->hr1, "Line %d: hr1 was %08lX\n", line, hr);
295
296 switch (pEntry->dwEffects2)
297 {
298 case D_NONE_OR_COPY:
299 ok((dwEffects == D_NONE || dwEffects == D_COPY),
300 "Line %d: dwEffects2 was %08lX\n", line, dwEffects);
301 break;
302 case D_NONE_OR_MOVE:
303 ok((dwEffects == D_NONE || dwEffects == D_MOVE),
304 "Line %d: dwEffects2 was %08lX\n", line, dwEffects);
305 break;
306 case D_NONE_OR_LINK:
307 ok((dwEffects == D_NONE || dwEffects == D_LINK),
308 "Line %d: dwEffects2 was %08lX\n", line, dwEffects);
309 break;
310 default:
311 ok(dwEffects == pEntry->dwEffects2,
312 "Line %d: dwEffects2 was %08lX\n", line, dwEffects);
313 break;
314 }
315
316 // Drop
317 hr = pDropTarget->Drop(pDataObject, dwKeyState, ptl, &dwEffects);
318 ok(hr == pEntry->hr2, "Line %d: hr2 was %08lX\n", line, hr);
319
320 switch (pEntry->dwEffects3)
321 {
322 case D_NONE_OR_COPY:
323 ok((dwEffects == D_NONE || dwEffects == D_COPY),
324 "Line %d: dwEffects3 was %08lX\n", line, dwEffects);
325 break;
326 case D_NONE_OR_MOVE:
327 ok((dwEffects == D_NONE || dwEffects == D_MOVE),
328 "Line %d: dwEffects3 was %08lX\n", line, dwEffects);
329 break;
330 case D_NONE_OR_LINK:
331 ok((dwEffects == D_NONE || dwEffects == D_LINK),
332 "Line %d: dwEffects3 was %08lX\n", line, dwEffects);
333 break;
334 default:
335 ok(dwEffects == pEntry->dwEffects3,
336 "Line %d: dwEffects3 was %08lX\n", line, dwEffects);
337 break;
338 }
339
340 // check file existence by pEntry->op
341 switch (pEntry->op)
342 {
343 case OP_NONE:
344 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line);
345 ok(!PathFileExistsW(s_szDestTestFile), "Line %d: dest exists\n", line);
346 ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line);
347 break;
348 case OP_COPY:
349 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line);
350 ok(PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line);
351 ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line);
352 break;
353 case OP_MOVE:
354 ok(!PathFileExistsW(s_szSrcTestFile), "Line %d: src exists\n", line);
355 ok(PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line);
356 ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line);
357 break;
358 case OP_LINK:
359 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line);
360 ok(!PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line);
361 ok(DoSpecExistsW(s_szDestLinkSpec), "Line %d: link not exists\n", line);
362 break;
363 case OP_NONE_OR_COPY:
364 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line);
365 ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line);
366 break;
367 case OP_NONE_OR_MOVE:
368 ok(PathFileExistsW(s_szSrcTestFile) != PathFileExistsW(s_szDestTestFile),
369 "Line %d: It must be either None or Move\n", line);
370 break;
371 case OP_NONE_OR_LINK:
372 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line);
373 ok(!PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line);
374 break;
375 }
376
377 // clean up
378 DeleteFileW(s_szSrcTestFile);
379 DeleteFileW(s_szDestTestFile);
380 DoDeleteSpecW(s_szDestLinkSpec);
381 ILFree(pidlDesktop);
382 }
383
START_TEST(DragDrop)384 START_TEST(DragDrop)
385 {
386 if (!winetest_interactive)
387 {
388 skip("Skipping the test due to crash. CORE-17057\n");
389 return;
390 }
391
392 HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
393 ok_int(SUCCEEDED(hr), TRUE);
394
395 SHGetDesktopFolder(&s_pDesktop);
396 ok(!!s_pDesktop, "s_pDesktop is NULL\n");
397
398 BOOL ret = SHGetSpecialFolderPathW(NULL, s_szDestFolder, CSIDL_DESKTOP, FALSE);
399 ok_int(ret, TRUE);
400
401 for (size_t i = 0; i < _countof(s_TestEntries); ++i)
402 {
403 DoTestEntry(&s_TestEntries[i]);
404 }
405
406 DeleteFileW(s_szSrcTestFile);
407 DeleteFileW(s_szDestTestFile);
408 DoDeleteSpecW(s_szDestLinkSpec);
409 DeleteFileW(s_szDroppedToItem);
410
411 CoUninitialize();
412 }
413