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