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