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