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