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