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