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 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 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 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 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 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 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 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 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