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