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, "pidl is NULL\n"); 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 // DragEnter 276 POINTL ptl = { 0, 0 }; 277 DWORD dwKeyState = pEntry->dwKeyState; 278 DWORD dwEffects = pEntry->dwEffects1; 279 hr = pDropTarget->DragEnter(pDataObject, dwKeyState, ptl, &dwEffects); 280 281 ok(hr == pEntry->hr1, "Line %d: hr1 was %08lX\n", line, hr); 282 283 switch (pEntry->dwEffects2) 284 { 285 case D_NONE_OR_COPY: 286 ok((dwEffects == D_NONE || dwEffects == D_COPY), 287 "Line %d: dwEffects2 was %08lX\n", line, dwEffects); 288 break; 289 case D_NONE_OR_MOVE: 290 ok((dwEffects == D_NONE || dwEffects == D_MOVE), 291 "Line %d: dwEffects2 was %08lX\n", line, dwEffects); 292 break; 293 case D_NONE_OR_LINK: 294 ok((dwEffects == D_NONE || dwEffects == D_LINK), 295 "Line %d: dwEffects2 was %08lX\n", line, dwEffects); 296 break; 297 default: 298 ok(dwEffects == pEntry->dwEffects2, 299 "Line %d: dwEffects2 was %08lX\n", line, dwEffects); 300 break; 301 } 302 303 // Drop 304 hr = pDropTarget->Drop(pDataObject, dwKeyState, ptl, &dwEffects); 305 ok(hr == pEntry->hr2, "Line %d: hr2 was %08lX\n", line, hr); 306 307 switch (pEntry->dwEffects3) 308 { 309 case D_NONE_OR_COPY: 310 ok((dwEffects == D_NONE || dwEffects == D_COPY), 311 "Line %d: dwEffects3 was %08lX\n", line, dwEffects); 312 break; 313 case D_NONE_OR_MOVE: 314 ok((dwEffects == D_NONE || dwEffects == D_MOVE), 315 "Line %d: dwEffects3 was %08lX\n", line, dwEffects); 316 break; 317 case D_NONE_OR_LINK: 318 ok((dwEffects == D_NONE || dwEffects == D_LINK), 319 "Line %d: dwEffects3 was %08lX\n", line, dwEffects); 320 break; 321 default: 322 ok(dwEffects == pEntry->dwEffects3, 323 "Line %d: dwEffects3 was %08lX\n", line, dwEffects); 324 break; 325 } 326 327 // check file existence by pEntry->op 328 switch (pEntry->op) 329 { 330 case OP_NONE: 331 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line); 332 ok(!PathFileExistsW(s_szDestTestFile), "Line %d: dest exists\n", line); 333 ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line); 334 break; 335 case OP_COPY: 336 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line); 337 ok(PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line); 338 ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line); 339 break; 340 case OP_MOVE: 341 ok(!PathFileExistsW(s_szSrcTestFile), "Line %d: src exists\n", line); 342 ok(PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line); 343 ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line); 344 break; 345 case OP_LINK: 346 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line); 347 ok(!PathFileExistsW(s_szDestTestFile), "Line %d: dest not exists\n", line); 348 ok(DoSpecExistsW(s_szDestLinkSpec), "Line %d: link not exists\n", line); 349 break; 350 case OP_NONE_OR_COPY: 351 ok(PathFileExistsW(s_szSrcTestFile), "Line %d: src not exists\n", line); 352 ok(!DoSpecExistsW(s_szDestLinkSpec), "Line %d: link exists\n", line); 353 break; 354 case OP_NONE_OR_MOVE: 355 ok(PathFileExistsW(s_szSrcTestFile) != PathFileExistsW(s_szDestTestFile), 356 "Line %d: It must be either None or Move\n", line); 357 break; 358 case OP_NONE_OR_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 break; 362 } 363 364 // clean up 365 DeleteFileW(s_szSrcTestFile); 366 DeleteFileW(s_szDestTestFile); 367 DoDeleteSpecW(s_szDestLinkSpec); 368 ILFree(pidlDesktop); 369 } 370 371 START_TEST(DragDrop) 372 { 373 HRESULT hr = CoInitializeEx(NULL, COINIT_APARTMENTTHREADED); 374 ok_int(SUCCEEDED(hr), TRUE); 375 376 SHGetDesktopFolder(&s_pDesktop); 377 ok(!!s_pDesktop, "s_pDesktop is NULL\n"); 378 379 BOOL ret = SHGetSpecialFolderPathW(NULL, s_szDestFolder, CSIDL_DESKTOP, FALSE); 380 ok_int(ret, TRUE); 381 382 for (size_t i = 0; i < _countof(s_TestEntries); ++i) 383 { 384 DoTestEntry(&s_TestEntries[i]); 385 } 386 387 DeleteFileW(s_szSrcTestFile); 388 DeleteFileW(s_szDestTestFile); 389 DoDeleteSpecW(s_szDestLinkSpec); 390 DeleteFileW(s_szDroppedToItem); 391 392 CoUninitialize(); 393 } 394