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