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