1 2 /* 3 * file system folder drop target 4 * 5 * Copyright 1997 Marcus Meissner 6 * Copyright 1998, 1999, 2002 Juergen Schmied 7 * 8 * This library is free software; you can redistribute it and/or 9 * modify it under the terms of the GNU Lesser General Public 10 * License as published by the Free Software Foundation; either 11 * version 2.1 of the License, or (at your option) any later version. 12 * 13 * This library is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 16 * Lesser General Public License for more details. 17 * 18 * You should have received a copy of the GNU Lesser General Public 19 * License along with this library; if not, write to the Free Software 20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA 21 */ 22 23 #include <precomp.h> 24 25 WINE_DEFAULT_DEBUG_CHANNEL (shell); 26 27 static void SHELL_StripIllegalFsNameCharacters(_Inout_ LPWSTR Buf) 28 { 29 for (LPWSTR src = Buf, dst = src;;) 30 { 31 *dst = *src; 32 if (!*dst) 33 break; 34 else if (wcschr(INVALID_FILETITLE_CHARACTERSW, *dst)) 35 src = CharNextW(src); 36 else 37 ++src, ++dst; 38 } 39 } 40 41 static HRESULT 42 SHELL_LimitDropEffectToItemAttributes(_In_ IDataObject *pDataObject, _Inout_ PDWORD pdwEffect) 43 { 44 DWORD att = *pdwEffect & (SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK); // DROPEFFECT maps perfectly to these SFGAO bits 45 HRESULT hr = SHGetAttributesFromDataObject(pDataObject, att, &att, NULL); 46 if (FAILED(hr)) 47 return S_FALSE; 48 *pdwEffect &= ~(SFGAO_CANCOPY | SFGAO_CANMOVE | SFGAO_CANLINK) | att; 49 return hr; 50 } 51 52 static void GetDefaultCopyMoveEffect() 53 { 54 // FIXME: When the source is on a different volume than the target, change default from move to copy 55 } 56 57 /**************************************************************************** 58 * CFSDropTarget::_CopyItems 59 * 60 * copies or moves items to this folder 61 */ 62 HRESULT CFSDropTarget::_CopyItems(IShellFolder * pSFFrom, UINT cidl, 63 LPCITEMIDLIST * apidl, BOOL bCopy) 64 { 65 HRESULT ret; 66 WCHAR wszDstPath[MAX_PATH + 1] = {0}; 67 PWCHAR pwszSrcPathsList = (PWCHAR) HeapAlloc(GetProcessHeap(), 0, MAX_PATH * sizeof(WCHAR) * cidl + 1); 68 if (!pwszSrcPathsList) 69 return E_OUTOFMEMORY; 70 71 PWCHAR pwszListPos = pwszSrcPathsList; 72 STRRET strretFrom; 73 SHFILEOPSTRUCTW fop; 74 BOOL bRenameOnCollision = FALSE; 75 76 /* Build a double null terminated list of C strings from source paths */ 77 for (UINT i = 0; i < cidl; i++) 78 { 79 ret = pSFFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strretFrom); 80 if (FAILED(ret)) 81 goto cleanup; 82 83 ret = StrRetToBufW(&strretFrom, NULL, pwszListPos, MAX_PATH); 84 if (FAILED(ret)) 85 goto cleanup; 86 87 pwszListPos += lstrlenW(pwszListPos) + 1; 88 } 89 /* Append the final null. */ 90 *pwszListPos = L'\0'; 91 92 /* Build a double null terminated target (this path) */ 93 ret = StringCchCopyW(wszDstPath, MAX_PATH, m_sPathTarget); 94 if (FAILED(ret)) 95 goto cleanup; 96 97 wszDstPath[lstrlenW(wszDstPath) + 1] = UNICODE_NULL; 98 99 /* Set bRenameOnCollision to TRUE if necesssary */ 100 if (bCopy) 101 { 102 WCHAR szPath1[MAX_PATH], szPath2[MAX_PATH]; 103 GetFullPathNameW(pwszSrcPathsList, _countof(szPath1), szPath1, NULL); 104 GetFullPathNameW(wszDstPath, _countof(szPath2), szPath2, NULL); 105 PathRemoveFileSpecW(szPath1); 106 if (_wcsicmp(szPath1, szPath2) == 0) 107 bRenameOnCollision = TRUE; 108 } 109 110 ZeroMemory(&fop, sizeof(fop)); 111 fop.hwnd = m_hwndSite; 112 fop.wFunc = bCopy ? FO_COPY : FO_MOVE; 113 fop.pFrom = pwszSrcPathsList; 114 fop.pTo = wszDstPath; 115 fop.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR; 116 if (bRenameOnCollision) 117 fop.fFlags |= FOF_RENAMEONCOLLISION; 118 119 ret = S_OK; 120 121 if (SHFileOperationW(&fop)) 122 { 123 ERR("SHFileOperationW failed\n"); 124 ret = E_FAIL; 125 } 126 127 cleanup: 128 HeapFree(GetProcessHeap(), 0, pwszSrcPathsList); 129 return ret; 130 } 131 132 CFSDropTarget::CFSDropTarget(): 133 m_cfShellIDList(0), 134 m_fAcceptFmt(FALSE), 135 m_sPathTarget(NULL), 136 m_hwndSite(NULL), 137 m_grfKeyState(0) 138 { 139 } 140 141 HRESULT CFSDropTarget::Initialize(LPWSTR PathTarget) 142 { 143 if (!PathTarget) 144 return E_UNEXPECTED; 145 146 m_cfShellIDList = RegisterClipboardFormatW(CFSTR_SHELLIDLIST); 147 if (!m_cfShellIDList) 148 return E_FAIL; 149 150 m_sPathTarget = (WCHAR *)SHAlloc((wcslen(PathTarget) + 1) * sizeof(WCHAR)); 151 if (!m_sPathTarget) 152 return E_OUTOFMEMORY; 153 154 wcscpy(m_sPathTarget, PathTarget); 155 156 return S_OK; 157 } 158 159 CFSDropTarget::~CFSDropTarget() 160 { 161 SHFree(m_sPathTarget); 162 } 163 164 BOOL 165 CFSDropTarget::_GetUniqueFileName(LPCWSTR pwszBasePath, LPCWSTR pwszExt, LPWSTR pwszTarget, BOOL bShortcut) 166 { 167 WCHAR wszLink[40]; 168 169 if (!bShortcut) 170 { 171 if (!LoadStringW(shell32_hInstance, IDS_LNK_FILE, wszLink, _countof(wszLink))) 172 wszLink[0] = L'\0'; 173 } 174 175 if (!bShortcut) 176 swprintf(pwszTarget, L"%s%s%s", wszLink, pwszBasePath, pwszExt); 177 else 178 swprintf(pwszTarget, L"%s%s", pwszBasePath, pwszExt); 179 180 for (UINT i = 2; PathFileExistsW(pwszTarget); ++i) 181 { 182 if (!bShortcut) 183 swprintf(pwszTarget, L"%s%s (%u)%s", wszLink, pwszBasePath, i, pwszExt); 184 else 185 swprintf(pwszTarget, L"%s (%u)%s", pwszBasePath, i, pwszExt); 186 } 187 188 return TRUE; 189 } 190 191 /**************************************************************************** 192 * IDropTarget implementation 193 */ 194 BOOL CFSDropTarget::_QueryDrop(DWORD dwKeyState, LPDWORD pdwEffect) 195 { 196 /* TODO Windows does different drop effects if dragging across drives. 197 i.e., it will copy instead of move if the directories are on different disks. */ 198 GetDefaultCopyMoveEffect(); 199 200 DWORD dwEffect = m_dwDefaultEffect; 201 202 *pdwEffect = DROPEFFECT_NONE; 203 204 if (m_fAcceptFmt) { /* Does our interpretation of the keystate ... */ 205 *pdwEffect = KeyStateToDropEffect (dwKeyState); 206 207 if (*pdwEffect == DROPEFFECT_NONE) 208 *pdwEffect = dwEffect; 209 210 /* ... matches the desired effect ? */ 211 if (dwEffect & *pdwEffect) { 212 return TRUE; 213 } 214 } 215 return FALSE; 216 } 217 218 HRESULT CFSDropTarget::_GetEffectFromMenu(IDataObject *pDataObject, POINTL pt, DWORD *pdwEffect, DWORD dwAvailableEffects) 219 { 220 HMENU hmenu = LoadMenuW(shell32_hInstance, MAKEINTRESOURCEW(IDM_DRAGFILE)); 221 if (!hmenu) 222 return E_OUTOFMEMORY; 223 224 HMENU hpopupmenu = GetSubMenu(hmenu, 0); 225 226 SHELL_LimitDropEffectToItemAttributes(pDataObject, &dwAvailableEffects); 227 if ((dwAvailableEffects & DROPEFFECT_COPY) == 0) 228 DeleteMenu(hpopupmenu, IDM_COPYHERE, MF_BYCOMMAND); 229 if ((dwAvailableEffects & DROPEFFECT_MOVE) == 0) 230 DeleteMenu(hpopupmenu, IDM_MOVEHERE, MF_BYCOMMAND); 231 if ((dwAvailableEffects & DROPEFFECT_LINK) == 0 && (dwAvailableEffects & (DROPEFFECT_COPY | DROPEFFECT_MOVE))) 232 DeleteMenu(hpopupmenu, IDM_LINKHERE, MF_BYCOMMAND); 233 234 GetDefaultCopyMoveEffect(); 235 if (*pdwEffect & dwAvailableEffects & DROPEFFECT_COPY) 236 SetMenuDefaultItem(hpopupmenu, IDM_COPYHERE, FALSE); 237 else if (*pdwEffect & dwAvailableEffects & DROPEFFECT_MOVE) 238 SetMenuDefaultItem(hpopupmenu, IDM_MOVEHERE, FALSE); 239 else if (dwAvailableEffects & DROPEFFECT_LINK) 240 SetMenuDefaultItem(hpopupmenu, IDM_LINKHERE, FALSE); 241 242 /* FIXME: We need to support shell extensions here */ 243 244 /* We shouldn't use the site window here because the menu should work even when we don't have a site */ 245 HWND hwndDummy = CreateWindowEx(0, 246 WC_STATIC, 247 NULL, 248 WS_OVERLAPPED | WS_DISABLED | WS_CLIPSIBLINGS | WS_BORDER | SS_LEFT, 249 pt.x, 250 pt.y, 251 1, 252 1, 253 NULL, 254 NULL, 255 NULL, 256 NULL); 257 258 SetForegroundWindow(hwndDummy); // Required for aborting by pressing Esc when dragging from Explorer to desktop 259 UINT uCommand = TrackPopupMenu(hpopupmenu, 260 TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY, 261 pt.x, pt.y, 0, hwndDummy, NULL); 262 263 DestroyWindow(hwndDummy); 264 265 if (uCommand == 0) 266 return S_FALSE; 267 else if (uCommand == IDM_COPYHERE) 268 *pdwEffect = DROPEFFECT_COPY; 269 else if (uCommand == IDM_MOVEHERE) 270 *pdwEffect = DROPEFFECT_MOVE; 271 else if (uCommand == IDM_LINKHERE) 272 *pdwEffect = DROPEFFECT_LINK; 273 274 return S_OK; 275 } 276 277 HRESULT CFSDropTarget::_RepositionItems(IShellFolderView *psfv, IDataObject *pdtobj, POINTL pt) 278 { 279 CComPtr<IFolderView> pfv; 280 POINT ptDrag; 281 HRESULT hr = psfv->QueryInterface(IID_PPV_ARG(IFolderView, &pfv)); 282 if (FAILED_UNEXPECTEDLY(hr)) 283 return hr; 284 285 hr = psfv->GetDragPoint(&ptDrag); 286 if (FAILED_UNEXPECTEDLY(hr)) 287 return hr; 288 289 PIDLIST_ABSOLUTE pidlFolder; 290 PUITEMID_CHILD *apidl; 291 UINT cidl; 292 hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl); 293 if (FAILED_UNEXPECTEDLY(hr)) 294 return hr; 295 296 CComHeapPtr<POINT> apt; 297 if (!apt.Allocate(cidl)) 298 { 299 SHFree(pidlFolder); 300 _ILFreeaPidl(apidl, cidl); 301 return E_OUTOFMEMORY; 302 } 303 304 for (UINT i = 0; i<cidl; i++) 305 { 306 pfv->GetItemPosition(apidl[i], &apt[i]); 307 apt[i].x += pt.x - ptDrag.x; 308 apt[i].y += pt.y - ptDrag.y; 309 } 310 311 pfv->SelectAndPositionItems(cidl, apidl, apt, SVSI_SELECT); 312 313 SHFree(pidlFolder); 314 _ILFreeaPidl(apidl, cidl); 315 return S_OK; 316 } 317 318 HRESULT WINAPI CFSDropTarget::DragEnter(IDataObject *pDataObject, 319 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) 320 { 321 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject); 322 323 if (*pdwEffect == DROPEFFECT_NONE) 324 return S_OK; 325 326 FORMATETC fmt; 327 FORMATETC fmt2; 328 m_fAcceptFmt = FALSE; 329 330 InitFormatEtc (fmt, m_cfShellIDList, TYMED_HGLOBAL); 331 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); 332 333 if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) 334 m_fAcceptFmt = TRUE; 335 else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2))) 336 m_fAcceptFmt = TRUE; 337 338 m_grfKeyState = dwKeyState; 339 340 #define D_NONE DROPEFFECT_NONE 341 #define D_COPY DROPEFFECT_COPY 342 #define D_MOVE DROPEFFECT_MOVE 343 #define D_LINK DROPEFFECT_LINK 344 m_dwDefaultEffect = *pdwEffect; 345 switch (*pdwEffect & (D_COPY | D_MOVE | D_LINK)) 346 { 347 case D_COPY | D_MOVE: 348 if (dwKeyState & MK_CONTROL) 349 m_dwDefaultEffect = D_COPY; 350 else 351 m_dwDefaultEffect = D_MOVE; 352 break; 353 case D_COPY | D_MOVE | D_LINK: 354 if ((dwKeyState & (MK_SHIFT | MK_CONTROL)) == (MK_SHIFT | MK_CONTROL)) 355 m_dwDefaultEffect = D_LINK; 356 else if ((dwKeyState & (MK_SHIFT | MK_CONTROL)) == MK_CONTROL) 357 m_dwDefaultEffect = D_COPY; 358 else 359 m_dwDefaultEffect = D_MOVE; 360 break; 361 case D_COPY | D_LINK: 362 if ((dwKeyState & (MK_SHIFT | MK_CONTROL)) == (MK_SHIFT | MK_CONTROL)) 363 m_dwDefaultEffect = D_LINK; 364 else 365 m_dwDefaultEffect = D_COPY; 366 break; 367 case D_MOVE | D_LINK: 368 if ((dwKeyState & (MK_SHIFT | MK_CONTROL)) == (MK_SHIFT | MK_CONTROL)) 369 m_dwDefaultEffect = D_LINK; 370 else 371 m_dwDefaultEffect = D_MOVE; 372 break; 373 } 374 375 STGMEDIUM medium; 376 if (SUCCEEDED(pDataObject->GetData(&fmt2, &medium))) 377 { 378 WCHAR wstrFirstFile[MAX_PATH]; 379 if (DragQueryFileW((HDROP)medium.hGlobal, 0, wstrFirstFile, _countof(wstrFirstFile))) 380 { 381 /* Check if the drive letter is different */ 382 if (wstrFirstFile[0] != m_sPathTarget[0]) 383 { 384 m_dwDefaultEffect = DROPEFFECT_COPY; 385 } 386 } 387 ReleaseStgMedium(&medium); 388 } 389 390 if (!m_fAcceptFmt) 391 *pdwEffect = DROPEFFECT_NONE; 392 else 393 *pdwEffect = m_dwDefaultEffect; 394 395 return S_OK; 396 } 397 398 HRESULT WINAPI CFSDropTarget::DragOver(DWORD dwKeyState, POINTL pt, 399 DWORD *pdwEffect) 400 { 401 TRACE("(%p)\n", this); 402 403 if (!pdwEffect) 404 return E_INVALIDARG; 405 406 m_grfKeyState = dwKeyState; 407 408 _QueryDrop(dwKeyState, pdwEffect); 409 410 return S_OK; 411 } 412 413 HRESULT WINAPI CFSDropTarget::DragLeave() 414 { 415 TRACE("(%p)\n", this); 416 417 m_fAcceptFmt = FALSE; 418 419 return S_OK; 420 } 421 422 HRESULT WINAPI CFSDropTarget::Drop(IDataObject *pDataObject, 423 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) 424 { 425 TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect); 426 427 if (!pdwEffect) 428 return E_INVALIDARG; 429 430 IUnknown_GetWindow(m_site, &m_hwndSite); 431 432 DWORD dwAvailableEffects = *pdwEffect; 433 434 _QueryDrop(dwKeyState, pdwEffect); 435 436 TRACE("pdwEffect: 0x%x, m_dwDefaultEffect: 0x%x, dwAvailableEffects: 0x%x\n", *pdwEffect, m_dwDefaultEffect, dwAvailableEffects); 437 438 if (m_grfKeyState & MK_RBUTTON) 439 { 440 HRESULT hr = _GetEffectFromMenu(pDataObject, pt, pdwEffect, dwAvailableEffects); 441 if (FAILED_UNEXPECTEDLY(hr) || hr == S_FALSE) 442 return hr; 443 } 444 445 if (*pdwEffect == DROPEFFECT_MOVE && m_site) 446 { 447 CComPtr<IShellFolderView> psfv; 448 HRESULT hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellFolderView, &psfv)); 449 if (SUCCEEDED(hr) && psfv->IsDropOnSource(this) == S_OK) 450 { 451 _RepositionItems(psfv, pDataObject, pt); 452 return S_OK; 453 } 454 } 455 456 BOOL fIsOpAsync = FALSE; 457 CComPtr<IAsyncOperation> pAsyncOperation; 458 459 if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation)))) 460 { 461 if (SUCCEEDED(pAsyncOperation->GetAsyncMode(&fIsOpAsync)) && fIsOpAsync) 462 { 463 _DoDropData *data = static_cast<_DoDropData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_DoDropData))); 464 data->This = this; 465 // Need to maintain this class in case the window is closed or the class exists temporarily (when dropping onto a folder). 466 pDataObject->AddRef(); 467 pAsyncOperation->StartOperation(NULL); 468 CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObject, &data->pStream); 469 this->AddRef(); 470 data->dwKeyState = dwKeyState; 471 data->pt = pt; 472 // Need to dereference as pdweffect gets freed. 473 data->pdwEffect = *pdwEffect; 474 SHCreateThread(CFSDropTarget::_DoDropThreadProc, data, NULL, NULL); 475 return S_OK; 476 } 477 } 478 return this->_DoDrop(pDataObject, dwKeyState, pt, pdwEffect); 479 } 480 481 HRESULT 482 WINAPI 483 CFSDropTarget::SetSite(IUnknown *pUnkSite) 484 { 485 m_site = pUnkSite; 486 return S_OK; 487 } 488 489 HRESULT 490 WINAPI 491 CFSDropTarget::GetSite(REFIID riid, void **ppvSite) 492 { 493 if (!m_site) 494 return E_FAIL; 495 496 return m_site->QueryInterface(riid, ppvSite); 497 } 498 499 HRESULT CFSDropTarget::_DoDrop(IDataObject *pDataObject, 500 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) 501 { 502 TRACE("(%p) performing drop, effect %u\n", this, *pdwEffect); 503 FORMATETC fmt; 504 FORMATETC fmt2; 505 STGMEDIUM medium; 506 507 InitFormatEtc (fmt, m_cfShellIDList, TYMED_HGLOBAL); 508 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); 509 510 HRESULT hr; 511 bool bCopy = TRUE; 512 bool bLinking = FALSE; 513 514 /* Figure out what drop operation we're doing */ 515 if (pdwEffect) 516 { 517 TRACE("Current drop effect flag %i\n", *pdwEffect); 518 if ((*pdwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE) 519 bCopy = FALSE; 520 if ((*pdwEffect & DROPEFFECT_LINK) == DROPEFFECT_LINK) 521 bLinking = TRUE; 522 } 523 524 if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) 525 { 526 hr = pDataObject->GetData(&fmt, &medium); 527 TRACE("CFSTR_SHELLIDLIST\n"); 528 if (FAILED(hr)) 529 { 530 ERR("CFSTR_SHELLIDLIST failed\n"); 531 } 532 /* lock the handle */ 533 LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal); 534 if (!lpcida) 535 { 536 ReleaseStgMedium(&medium); 537 return E_FAIL; 538 } 539 540 /* convert the data into pidl */ 541 LPITEMIDLIST pidl; 542 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida); 543 if (!apidl) 544 { 545 ReleaseStgMedium(&medium); 546 return E_FAIL; 547 } 548 549 CComPtr<IShellFolder> psfDesktop; 550 CComPtr<IShellFolder> psfFrom = NULL; 551 552 /* Grab the desktop shell folder */ 553 hr = SHGetDesktopFolder(&psfDesktop); 554 if (FAILED(hr)) 555 { 556 ERR("SHGetDesktopFolder failed\n"); 557 SHFree(pidl); 558 _ILFreeaPidl(apidl, lpcida->cidl); 559 ReleaseStgMedium(&medium); 560 return E_FAIL; 561 } 562 563 /* Find source folder, this is where the clipboard data was copied from */ 564 if (_ILIsDesktop(pidl)) 565 { 566 /* use desktop shell folder */ 567 psfFrom = psfDesktop; 568 } 569 else 570 { 571 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom)); 572 if (FAILED(hr)) 573 { 574 ERR("no IShellFolder\n"); 575 SHFree(pidl); 576 _ILFreeaPidl(apidl, lpcida->cidl); 577 ReleaseStgMedium(&medium); 578 return E_FAIL; 579 } 580 } 581 582 if (bLinking) 583 { 584 WCHAR wszNewLnk[MAX_PATH]; 585 586 TRACE("target path = %s\n", debugstr_w(m_sPathTarget)); 587 588 /* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */ 589 for (UINT i = 0; i < lpcida->cidl; i++) 590 { 591 SFGAOF att = SHGetAttributes(psfFrom, apidl[i], SFGAO_FOLDER | SFGAO_STREAM | SFGAO_FILESYSTEM); 592 CComHeapPtr<ITEMIDLIST_ABSOLUTE> pidlFull; 593 hr = SHILCombine(pidl, apidl[i], &pidlFull); 594 595 WCHAR targetName[MAX_PATH]; 596 if (SUCCEEDED(hr)) 597 { 598 // If the target is a file, we use SHGDN_FORPARSING because "NeverShowExt" will hide the ".lnk" extension. 599 // If the target is a virtual item, we ask for the friendly name because SHGDN_FORPARSING will return a GUID. 600 BOOL UseParsing = (att & (SFGAO_FILESYSTEM | SFGAO_FOLDER)) == SFGAO_FILESYSTEM; 601 DWORD ShgdnFor = UseParsing ? SHGDN_FORPARSING : SHGDN_FOREDITING; 602 hr = Shell_DisplayNameOf(psfFrom, apidl[i], ShgdnFor | SHGDN_INFOLDER, targetName, _countof(targetName)); 603 } 604 if (FAILED_UNEXPECTEDLY(hr)) 605 { 606 SHELL_ErrorBox(m_hwndSite, hr); 607 break; 608 } 609 SHELL_StripIllegalFsNameCharacters(targetName); 610 611 WCHAR wszCombined[MAX_PATH + _countof(targetName)]; 612 PathCombineW(wszCombined, m_sPathTarget, targetName); 613 614 // Check to see if the source is a link 615 BOOL fSourceIsLink = FALSE; 616 if (!wcsicmp(PathFindExtensionW(targetName), L".lnk") && (att & (SFGAO_FOLDER | SFGAO_STREAM)) != SFGAO_FOLDER) 617 { 618 fSourceIsLink = TRUE; 619 PathRemoveExtensionW(wszCombined); 620 } 621 622 // Create a pathname to save the new link. 623 _GetUniqueFileName(wszCombined, L".lnk", wszNewLnk, TRUE); 624 625 CComPtr<IPersistFile> ppf; 626 if (fSourceIsLink) 627 { 628 PWSTR pwszTargetFull; 629 hr = SHGetNameFromIDList(pidlFull, SIGDN_DESKTOPABSOLUTEPARSING, &pwszTargetFull); 630 if (!FAILED_UNEXPECTEDLY(hr)) 631 { 632 hr = IShellLink_ConstructFromPath(pwszTargetFull, IID_PPV_ARG(IPersistFile, &ppf)); 633 FAILED_UNEXPECTEDLY(hr); 634 SHFree(pwszTargetFull); 635 } 636 } 637 else 638 { 639 CComPtr<IShellLinkW> pLink; 640 hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink)); 641 if (!FAILED_UNEXPECTEDLY(hr)) 642 { 643 hr = pLink->SetIDList(pidlFull); 644 if (!FAILED_UNEXPECTEDLY(hr)) 645 hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf)); 646 647 PWSTR pwszPath, pSep; 648 if ((att & SFGAO_FILESYSTEM) && SUCCEEDED(SHGetNameFromIDList(pidlFull, SIGDN_FILESYSPATH, &pwszPath))) 649 { 650 if ((pSep = PathFindFileNameW(pwszPath)) > pwszPath) 651 { 652 pSep[-1] = UNICODE_NULL; 653 pLink->SetWorkingDirectory(pwszPath); 654 } 655 SHFree(pwszPath); 656 } 657 } 658 } 659 if (SUCCEEDED(hr)) 660 hr = ppf->Save(wszNewLnk, !fSourceIsLink); 661 if (FAILED_UNEXPECTEDLY(hr)) 662 { 663 SHELL_ErrorBox(m_hwndSite, hr); 664 break; 665 } 666 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszNewLnk, NULL); 667 } 668 } 669 else 670 { 671 hr = _CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl, bCopy); 672 } 673 674 SHFree(pidl); 675 _ILFreeaPidl(apidl, lpcida->cidl); 676 ReleaseStgMedium(&medium); 677 } 678 else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2))) 679 { 680 FORMATETC fmt2; 681 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); 682 if (SUCCEEDED(pDataObject->GetData(&fmt2, &medium)) /* && SUCCEEDED(pDataObject->GetData(&fmt2, &medium))*/) 683 { 684 WCHAR wszTargetPath[MAX_PATH + 1]; 685 LPWSTR pszSrcList; 686 687 wcscpy(wszTargetPath, m_sPathTarget); 688 //Double NULL terminate. 689 wszTargetPath[wcslen(wszTargetPath) + 1] = '\0'; 690 691 LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal); 692 if (!lpdf) 693 { 694 ERR("Error locking global\n"); 695 return E_FAIL; 696 } 697 pszSrcList = (LPWSTR) (((byte*) lpdf) + lpdf->pFiles); 698 ERR("Source file (just the first) = %s, target path = %s, bCopy: %d\n", debugstr_w(pszSrcList), debugstr_w(wszTargetPath), bCopy); 699 700 SHFILEOPSTRUCTW op; 701 ZeroMemory(&op, sizeof(op)); 702 op.pFrom = pszSrcList; 703 op.pTo = wszTargetPath; 704 op.hwnd = m_hwndSite; 705 op.wFunc = bCopy ? FO_COPY : FO_MOVE; 706 op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR; 707 int res = SHFileOperationW(&op); 708 if (res) 709 { 710 ERR("SHFileOperationW failed with 0x%x\n", res); 711 hr = E_FAIL; 712 } 713 714 return hr; 715 } 716 ERR("Error calling GetData\n"); 717 hr = E_FAIL; 718 } 719 else 720 { 721 ERR("No viable drop format\n"); 722 hr = E_FAIL; 723 } 724 return hr; 725 } 726 727 DWORD WINAPI CFSDropTarget::_DoDropThreadProc(LPVOID lpParameter) 728 { 729 CoInitialize(NULL); 730 _DoDropData *data = static_cast<_DoDropData*>(lpParameter); 731 CComPtr<IDataObject> pDataObject; 732 HRESULT hr = CoGetInterfaceAndReleaseStream (data->pStream, IID_PPV_ARG(IDataObject, &pDataObject)); 733 734 if (SUCCEEDED(hr)) 735 { 736 CComPtr<IAsyncOperation> pAsyncOperation; 737 hr = data->This->_DoDrop(pDataObject, data->dwKeyState, data->pt, &data->pdwEffect); 738 if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation)))) 739 { 740 pAsyncOperation->EndOperation(hr, NULL, data->pdwEffect); 741 } 742 } 743 //Release the CFSFolder and data object holds in the copying thread. 744 data->This->Release(); 745 //Release the parameter from the heap. 746 HeapFree(GetProcessHeap(), 0, data); 747 CoUninitialize(); 748 return 0; 749 } 750 751 HRESULT CFSDropTarget_CreateInstance(LPWSTR sPathTarget, REFIID riid, LPVOID * ppvOut) 752 { 753 return ShellObjectCreatorInit<CFSDropTarget>(sPathTarget, riid, ppvOut); 754 } 755