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 UINT uCommand = TrackPopupMenu(hpopupmenu, 210 TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY, 211 pt.x, pt.y, 0, m_hwndSite, NULL); 212 if (uCommand == 0) 213 return S_FALSE; 214 else if (uCommand == IDM_COPYHERE) 215 *pdwEffect = DROPEFFECT_COPY; 216 else if (uCommand == IDM_MOVEHERE) 217 *pdwEffect = DROPEFFECT_MOVE; 218 else if (uCommand == IDM_LINKHERE) 219 *pdwEffect = DROPEFFECT_LINK; 220 221 return S_OK; 222 } 223 224 HRESULT CFSDropTarget::_RepositionItems(IShellFolderView *psfv, IDataObject *pdtobj, POINTL pt) 225 { 226 CComPtr<IFolderView> pfv; 227 POINT ptDrag; 228 HRESULT hr = psfv->QueryInterface(IID_PPV_ARG(IFolderView, &pfv)); 229 if (FAILED_UNEXPECTEDLY(hr)) 230 return hr; 231 232 hr = psfv->GetDragPoint(&ptDrag); 233 if (FAILED_UNEXPECTEDLY(hr)) 234 return hr; 235 236 PIDLIST_ABSOLUTE pidlFolder; 237 PUITEMID_CHILD *apidl; 238 UINT cidl; 239 hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl); 240 if (FAILED_UNEXPECTEDLY(hr)) 241 return hr; 242 243 CComHeapPtr<POINT> apt; 244 if (!apt.Allocate(cidl)) 245 { 246 SHFree(pidlFolder); 247 _ILFreeaPidl(apidl, cidl); 248 return E_OUTOFMEMORY; 249 } 250 251 for (UINT i = 0; i<cidl; i++) 252 { 253 pfv->GetItemPosition(apidl[i], &apt[i]); 254 apt[i].x += pt.x - ptDrag.x; 255 apt[i].y += pt.y - ptDrag.y; 256 } 257 258 pfv->SelectAndPositionItems(cidl, apidl, apt, SVSI_SELECT); 259 260 SHFree(pidlFolder); 261 _ILFreeaPidl(apidl, cidl); 262 return S_OK; 263 } 264 265 HRESULT WINAPI CFSDropTarget::DragEnter(IDataObject *pDataObject, 266 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) 267 { 268 TRACE("(%p)->(DataObject=%p)\n", this, pDataObject); 269 FORMATETC fmt; 270 FORMATETC fmt2; 271 m_fAcceptFmt = FALSE; 272 273 InitFormatEtc (fmt, m_cfShellIDList, TYMED_HGLOBAL); 274 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); 275 276 if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) 277 m_fAcceptFmt = TRUE; 278 else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2))) 279 m_fAcceptFmt = TRUE; 280 281 m_grfKeyState = dwKeyState; 282 m_dwDefaultEffect = DROPEFFECT_MOVE; 283 284 STGMEDIUM medium; 285 if (SUCCEEDED(pDataObject->GetData(&fmt2, &medium))) 286 { 287 WCHAR wstrFirstFile[MAX_PATH]; 288 if (DragQueryFileW((HDROP)medium.hGlobal, 0, wstrFirstFile, _countof(wstrFirstFile))) 289 { 290 /* Check if the drive letter is different */ 291 if (wstrFirstFile[0] != m_sPathTarget[0]) 292 { 293 m_dwDefaultEffect = DROPEFFECT_COPY; 294 } 295 } 296 ReleaseStgMedium(&medium); 297 } 298 299 _QueryDrop(dwKeyState, pdwEffect); 300 return S_OK; 301 } 302 303 HRESULT WINAPI CFSDropTarget::DragOver(DWORD dwKeyState, POINTL pt, 304 DWORD *pdwEffect) 305 { 306 TRACE("(%p)\n", this); 307 308 if (!pdwEffect) 309 return E_INVALIDARG; 310 311 m_grfKeyState = dwKeyState; 312 313 _QueryDrop(dwKeyState, pdwEffect); 314 315 return S_OK; 316 } 317 318 HRESULT WINAPI CFSDropTarget::DragLeave() 319 { 320 TRACE("(%p)\n", this); 321 322 m_fAcceptFmt = FALSE; 323 324 return S_OK; 325 } 326 327 HRESULT WINAPI CFSDropTarget::Drop(IDataObject *pDataObject, 328 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) 329 { 330 TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect); 331 332 if (!pdwEffect) 333 return E_INVALIDARG; 334 335 IUnknown_GetWindow(m_site, &m_hwndSite); 336 337 DWORD dwAvailableEffects = *pdwEffect; 338 339 _QueryDrop(dwKeyState, pdwEffect); 340 341 TRACE("pdwEffect: 0x%x, m_dwDefaultEffect: 0x%x, dwAvailableEffects: 0x%x\n", *pdwEffect, m_dwDefaultEffect, dwAvailableEffects); 342 343 if (m_grfKeyState & MK_RBUTTON) 344 { 345 HRESULT hr = _GetEffectFromMenu(pDataObject, pt, pdwEffect, dwAvailableEffects); 346 if (FAILED_UNEXPECTEDLY(hr) || hr == S_FALSE) 347 return hr; 348 } 349 350 if (*pdwEffect == DROPEFFECT_MOVE && m_site) 351 { 352 CComPtr<IShellFolderView> psfv; 353 HRESULT hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellFolderView, &psfv)); 354 if (SUCCEEDED(hr) && psfv->IsDropOnSource(this) == S_OK) 355 { 356 _RepositionItems(psfv, pDataObject, pt); 357 return S_OK; 358 } 359 } 360 361 BOOL fIsOpAsync = FALSE; 362 CComPtr<IAsyncOperation> pAsyncOperation; 363 364 if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation)))) 365 { 366 if (SUCCEEDED(pAsyncOperation->GetAsyncMode(&fIsOpAsync)) && fIsOpAsync) 367 { 368 _DoDropData *data = static_cast<_DoDropData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_DoDropData))); 369 data->This = this; 370 // Need to maintain this class in case the window is closed or the class exists temporarily (when dropping onto a folder). 371 pDataObject->AddRef(); 372 pAsyncOperation->StartOperation(NULL); 373 CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObject, &data->pStream); 374 this->AddRef(); 375 data->dwKeyState = dwKeyState; 376 data->pt = pt; 377 // Need to dereference as pdweffect gets freed. 378 data->pdwEffect = *pdwEffect; 379 SHCreateThread(CFSDropTarget::_DoDropThreadProc, data, NULL, NULL); 380 return S_OK; 381 } 382 } 383 return this->_DoDrop(pDataObject, dwKeyState, pt, pdwEffect); 384 } 385 386 HRESULT 387 WINAPI 388 CFSDropTarget::SetSite(IUnknown *pUnkSite) 389 { 390 m_site = pUnkSite; 391 return S_OK; 392 } 393 394 HRESULT 395 WINAPI 396 CFSDropTarget::GetSite(REFIID riid, void **ppvSite) 397 { 398 if (!m_site) 399 return E_FAIL; 400 401 return m_site->QueryInterface(riid, ppvSite); 402 } 403 404 HRESULT CFSDropTarget::_DoDrop(IDataObject *pDataObject, 405 DWORD dwKeyState, POINTL pt, DWORD *pdwEffect) 406 { 407 TRACE("(%p) performing drop, effect %u\n", this, *pdwEffect); 408 FORMATETC fmt; 409 FORMATETC fmt2; 410 STGMEDIUM medium; 411 412 InitFormatEtc (fmt, m_cfShellIDList, TYMED_HGLOBAL); 413 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); 414 415 HRESULT hr; 416 bool bCopy = TRUE; 417 bool bLinking = FALSE; 418 419 /* Figure out what drop operation we're doing */ 420 if (pdwEffect) 421 { 422 TRACE("Current drop effect flag %i\n", *pdwEffect); 423 if ((*pdwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE) 424 bCopy = FALSE; 425 if ((*pdwEffect & DROPEFFECT_LINK) == DROPEFFECT_LINK) 426 bLinking = TRUE; 427 } 428 429 if (SUCCEEDED(pDataObject->QueryGetData(&fmt))) 430 { 431 hr = pDataObject->GetData(&fmt, &medium); 432 TRACE("CFSTR_SHELLIDLIST.\n"); 433 434 /* lock the handle */ 435 LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal); 436 if (!lpcida) 437 { 438 ReleaseStgMedium(&medium); 439 return E_FAIL; 440 } 441 442 /* convert the data into pidl */ 443 LPITEMIDLIST pidl; 444 LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida); 445 if (!apidl) 446 { 447 ReleaseStgMedium(&medium); 448 return E_FAIL; 449 } 450 451 CComPtr<IShellFolder> psfDesktop; 452 CComPtr<IShellFolder> psfFrom = NULL; 453 454 /* Grab the desktop shell folder */ 455 hr = SHGetDesktopFolder(&psfDesktop); 456 if (FAILED(hr)) 457 { 458 ERR("SHGetDesktopFolder failed\n"); 459 SHFree(pidl); 460 _ILFreeaPidl(apidl, lpcida->cidl); 461 ReleaseStgMedium(&medium); 462 return E_FAIL; 463 } 464 465 /* Find source folder, this is where the clipboard data was copied from */ 466 if (_ILIsDesktop(pidl)) 467 { 468 /* use desktop shell folder */ 469 psfFrom = psfDesktop; 470 } 471 else 472 { 473 hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom)); 474 if (FAILED(hr)) 475 { 476 ERR("no IShellFolder\n"); 477 SHFree(pidl); 478 _ILFreeaPidl(apidl, lpcida->cidl); 479 ReleaseStgMedium(&medium); 480 return E_FAIL; 481 } 482 } 483 484 if (bLinking) 485 { 486 WCHAR wszTargetPath[MAX_PATH]; 487 WCHAR wszPath[MAX_PATH]; 488 WCHAR wszTarget[MAX_PATH]; 489 490 wcscpy(wszTargetPath, m_sPathTarget); 491 492 TRACE("target path = %s", debugstr_w(wszTargetPath)); 493 494 /* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */ 495 for (UINT i = 0; i < lpcida->cidl; i++) 496 { 497 //Find out which file we're copying 498 STRRET strFile; 499 hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile); 500 if (FAILED(hr)) 501 { 502 ERR("Error source obtaining path"); 503 break; 504 } 505 506 hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath)); 507 if (FAILED(hr)) 508 { 509 ERR("Error putting source path into buffer"); 510 break; 511 } 512 TRACE("source path = %s", debugstr_w(wszPath)); 513 514 // Creating a buffer to hold the combined path 515 WCHAR buffer_1[MAX_PATH] = L""; 516 WCHAR *lpStr1; 517 lpStr1 = buffer_1; 518 519 LPWSTR pwszFileName = PathFindFileNameW(wszPath); 520 LPWSTR pwszExt = PathFindExtensionW(wszPath); 521 LPWSTR placementPath = PathCombineW(lpStr1, m_sPathTarget, pwszFileName); 522 CComPtr<IPersistFile> ppf; 523 524 //Check to see if it's already a link. 525 if (!wcsicmp(pwszExt, L".lnk")) 526 { 527 //It's a link so, we create a new one which copies the old. 528 if(!_GetUniqueFileName(placementPath, pwszExt, wszTarget, TRUE)) 529 { 530 ERR("Error getting unique file name"); 531 hr = E_FAIL; 532 break; 533 } 534 hr = IShellLink_ConstructFromPath(wszPath, IID_PPV_ARG(IPersistFile, &ppf)); 535 if (FAILED(hr)) { 536 ERR("Error constructing link from file"); 537 break; 538 } 539 540 hr = ppf->Save(wszTarget, FALSE); 541 if (FAILED(hr)) 542 break; 543 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL); 544 } 545 else 546 { 547 //It's not a link, so build a new link using the creator class and fill it in. 548 //Create a file name for the link 549 if (!_GetUniqueFileName(placementPath, L".lnk", wszTarget, TRUE)) 550 { 551 ERR("Error creating unique file name"); 552 hr = E_FAIL; 553 break; 554 } 555 556 CComPtr<IShellLinkW> pLink; 557 hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink)); 558 if (FAILED(hr)) { 559 ERR("Error instantiating IShellLinkW"); 560 break; 561 } 562 563 WCHAR szDirPath[MAX_PATH], *pwszFile; 564 GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile); 565 if (pwszFile) pwszFile[0] = 0; 566 567 hr = pLink->SetPath(wszPath); 568 if(FAILED(hr)) 569 break; 570 571 hr = pLink->SetWorkingDirectory(szDirPath); 572 if(FAILED(hr)) 573 break; 574 575 hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf)); 576 if(FAILED(hr)) 577 break; 578 579 hr = ppf->Save(wszTarget, TRUE); 580 if (FAILED(hr)) 581 break; 582 SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL); 583 } 584 } 585 } 586 else 587 { 588 hr = _CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl, bCopy); 589 } 590 591 SHFree(pidl); 592 _ILFreeaPidl(apidl, lpcida->cidl); 593 ReleaseStgMedium(&medium); 594 } 595 else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2))) 596 { 597 FORMATETC fmt2; 598 InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL); 599 if (SUCCEEDED(pDataObject->GetData(&fmt2, &medium)) /* && SUCCEEDED(pDataObject->GetData(&fmt2, &medium))*/) 600 { 601 WCHAR wszTargetPath[MAX_PATH + 1]; 602 LPWSTR pszSrcList; 603 604 wcscpy(wszTargetPath, m_sPathTarget); 605 //Double NULL terminate. 606 wszTargetPath[wcslen(wszTargetPath) + 1] = '\0'; 607 608 LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal); 609 if (!lpdf) 610 { 611 ERR("Error locking global\n"); 612 return E_FAIL; 613 } 614 pszSrcList = (LPWSTR) (((byte*) lpdf) + lpdf->pFiles); 615 ERR("Source file (just the first) = %s, target path = %s, bCopy: %d\n", debugstr_w(pszSrcList), debugstr_w(wszTargetPath), bCopy); 616 617 SHFILEOPSTRUCTW op; 618 ZeroMemory(&op, sizeof(op)); 619 op.pFrom = pszSrcList; 620 op.pTo = wszTargetPath; 621 op.hwnd = m_hwndSite; 622 op.wFunc = bCopy ? FO_COPY : FO_MOVE; 623 op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR; 624 hr = SHFileOperationW(&op); 625 return hr; 626 } 627 ERR("Error calling GetData\n"); 628 hr = E_FAIL; 629 } 630 else 631 { 632 ERR("No viable drop format.\n"); 633 hr = E_FAIL; 634 } 635 return hr; 636 } 637 638 DWORD WINAPI CFSDropTarget::_DoDropThreadProc(LPVOID lpParameter) 639 { 640 CoInitialize(NULL); 641 _DoDropData *data = static_cast<_DoDropData*>(lpParameter); 642 CComPtr<IDataObject> pDataObject; 643 HRESULT hr = CoGetInterfaceAndReleaseStream (data->pStream, IID_PPV_ARG(IDataObject, &pDataObject)); 644 645 if (SUCCEEDED(hr)) 646 { 647 CComPtr<IAsyncOperation> pAsyncOperation; 648 hr = data->This->_DoDrop(pDataObject, data->dwKeyState, data->pt, &data->pdwEffect); 649 if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation)))) 650 { 651 pAsyncOperation->EndOperation(hr, NULL, data->pdwEffect); 652 } 653 } 654 //Release the CFSFolder and data object holds in the copying thread. 655 data->This->Release(); 656 //Release the parameter from the heap. 657 HeapFree(GetProcessHeap(), 0, data); 658 CoUninitialize(); 659 return 0; 660 } 661 662 HRESULT CFSDropTarget_CreateInstance(LPWSTR sPathTarget, REFIID riid, LPVOID * ppvOut) 663 { 664 return ShellObjectCreatorInit<CFSDropTarget>(sPathTarget, riid, ppvOut); 665 }