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