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     UINT uCommand = TrackPopupMenu(hpopupmenu,
210                                    TPM_LEFTALIGN | TPM_RETURNCMD | TPM_LEFTBUTTON | TPM_RIGHTBUTTON | TPM_NONOTIFY,
211                                    pt.x, pt.y, 0, m_hwndSite, NULL);
212     if (uCommand == 0)
213         return S_FALSE;
214     else if (uCommand == IDM_COPYHERE)
215         *pdwEffect = DROPEFFECT_COPY;
216     else if (uCommand == IDM_MOVEHERE)
217         *pdwEffect = DROPEFFECT_MOVE;
218     else if (uCommand == IDM_LINKHERE)
219         *pdwEffect = DROPEFFECT_LINK;
220 
221     return S_OK;
222 }
223 
224 HRESULT CFSDropTarget::_RepositionItems(IShellFolderView *psfv, IDataObject *pdtobj, POINTL pt)
225 {
226     CComPtr<IFolderView> pfv;
227     POINT ptDrag;
228     HRESULT hr = psfv->QueryInterface(IID_PPV_ARG(IFolderView, &pfv));
229     if (FAILED_UNEXPECTEDLY(hr))
230         return hr;
231 
232     hr = psfv->GetDragPoint(&ptDrag);
233     if (FAILED_UNEXPECTEDLY(hr))
234         return hr;
235 
236     PIDLIST_ABSOLUTE pidlFolder;
237     PUITEMID_CHILD *apidl;
238     UINT cidl;
239     hr = SH_GetApidlFromDataObject(pdtobj, &pidlFolder, &apidl, &cidl);
240     if (FAILED_UNEXPECTEDLY(hr))
241         return hr;
242 
243     CComHeapPtr<POINT> apt;
244     if (!apt.Allocate(cidl))
245     {
246         SHFree(pidlFolder);
247         _ILFreeaPidl(apidl, cidl);
248         return E_OUTOFMEMORY;
249     }
250 
251     for (UINT i = 0; i<cidl; i++)
252     {
253         pfv->GetItemPosition(apidl[i], &apt[i]);
254         apt[i].x += pt.x - ptDrag.x;
255         apt[i].y += pt.y - ptDrag.y;
256     }
257 
258     pfv->SelectAndPositionItems(cidl, apidl, apt, SVSI_SELECT);
259 
260     SHFree(pidlFolder);
261     _ILFreeaPidl(apidl, cidl);
262     return S_OK;
263 }
264 
265 HRESULT WINAPI CFSDropTarget::DragEnter(IDataObject *pDataObject,
266                                         DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
267 {
268     TRACE("(%p)->(DataObject=%p)\n", this, pDataObject);
269     FORMATETC fmt;
270     FORMATETC fmt2;
271     m_fAcceptFmt = FALSE;
272 
273     InitFormatEtc (fmt, m_cfShellIDList, TYMED_HGLOBAL);
274     InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
275 
276     if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
277         m_fAcceptFmt = TRUE;
278     else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2)))
279         m_fAcceptFmt = TRUE;
280 
281     m_grfKeyState = dwKeyState;
282     m_dwDefaultEffect = DROPEFFECT_MOVE;
283 
284     STGMEDIUM medium;
285     if (SUCCEEDED(pDataObject->GetData(&fmt2, &medium)))
286     {
287         WCHAR wstrFirstFile[MAX_PATH];
288         if (DragQueryFileW((HDROP)medium.hGlobal, 0, wstrFirstFile, _countof(wstrFirstFile)))
289         {
290             /* Check if the drive letter is different */
291             if (wstrFirstFile[0] != m_sPathTarget[0])
292             {
293                 m_dwDefaultEffect = DROPEFFECT_COPY;
294             }
295         }
296         ReleaseStgMedium(&medium);
297     }
298 
299     _QueryDrop(dwKeyState, pdwEffect);
300     return S_OK;
301 }
302 
303 HRESULT WINAPI CFSDropTarget::DragOver(DWORD dwKeyState, POINTL pt,
304                                        DWORD *pdwEffect)
305 {
306     TRACE("(%p)\n", this);
307 
308     if (!pdwEffect)
309         return E_INVALIDARG;
310 
311     m_grfKeyState = dwKeyState;
312 
313     _QueryDrop(dwKeyState, pdwEffect);
314 
315     return S_OK;
316 }
317 
318 HRESULT WINAPI CFSDropTarget::DragLeave()
319 {
320     TRACE("(%p)\n", this);
321 
322     m_fAcceptFmt = FALSE;
323 
324     return S_OK;
325 }
326 
327 HRESULT WINAPI CFSDropTarget::Drop(IDataObject *pDataObject,
328                                    DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
329 {
330     TRACE("(%p) object dropped, effect %u\n", this, *pdwEffect);
331 
332     if (!pdwEffect)
333         return E_INVALIDARG;
334 
335     IUnknown_GetWindow(m_site, &m_hwndSite);
336 
337     DWORD dwAvailableEffects = *pdwEffect;
338 
339     _QueryDrop(dwKeyState, pdwEffect);
340 
341     TRACE("pdwEffect: 0x%x, m_dwDefaultEffect: 0x%x, dwAvailableEffects: 0x%x\n", *pdwEffect, m_dwDefaultEffect, dwAvailableEffects);
342 
343     if (m_grfKeyState & MK_RBUTTON)
344     {
345         HRESULT hr = _GetEffectFromMenu(pDataObject, pt, pdwEffect, dwAvailableEffects);
346         if (FAILED_UNEXPECTEDLY(hr) || hr == S_FALSE)
347             return hr;
348     }
349 
350     if (*pdwEffect == DROPEFFECT_MOVE && m_site)
351     {
352         CComPtr<IShellFolderView> psfv;
353         HRESULT hr = IUnknown_QueryService(m_site, SID_IFolderView, IID_PPV_ARG(IShellFolderView, &psfv));
354         if (SUCCEEDED(hr) && psfv->IsDropOnSource(this) == S_OK)
355         {
356             _RepositionItems(psfv, pDataObject, pt);
357             return S_OK;
358         }
359     }
360 
361     BOOL fIsOpAsync = FALSE;
362     CComPtr<IAsyncOperation> pAsyncOperation;
363 
364     if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation))))
365     {
366         if (SUCCEEDED(pAsyncOperation->GetAsyncMode(&fIsOpAsync)) && fIsOpAsync)
367         {
368             _DoDropData *data = static_cast<_DoDropData*>(HeapAlloc(GetProcessHeap(), 0, sizeof(_DoDropData)));
369             data->This = this;
370             // Need to maintain this class in case the window is closed or the class exists temporarily (when dropping onto a folder).
371             pDataObject->AddRef();
372             pAsyncOperation->StartOperation(NULL);
373             CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObject, &data->pStream);
374             this->AddRef();
375             data->dwKeyState = dwKeyState;
376             data->pt = pt;
377             // Need to dereference as pdweffect gets freed.
378             data->pdwEffect = *pdwEffect;
379             SHCreateThread(CFSDropTarget::_DoDropThreadProc, data, NULL, NULL);
380             return S_OK;
381         }
382     }
383     return this->_DoDrop(pDataObject, dwKeyState, pt, pdwEffect);
384 }
385 
386 HRESULT
387 WINAPI
388 CFSDropTarget::SetSite(IUnknown *pUnkSite)
389 {
390     m_site = pUnkSite;
391     return S_OK;
392 }
393 
394 HRESULT
395 WINAPI
396 CFSDropTarget::GetSite(REFIID riid, void **ppvSite)
397 {
398     if (!m_site)
399         return E_FAIL;
400 
401     return m_site->QueryInterface(riid, ppvSite);
402 }
403 
404 HRESULT CFSDropTarget::_DoDrop(IDataObject *pDataObject,
405                                DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
406 {
407     TRACE("(%p) performing drop, effect %u\n", this, *pdwEffect);
408     FORMATETC fmt;
409     FORMATETC fmt2;
410     STGMEDIUM medium;
411 
412     InitFormatEtc (fmt, m_cfShellIDList, TYMED_HGLOBAL);
413     InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
414 
415     HRESULT hr;
416     bool bCopy = TRUE;
417     bool bLinking = FALSE;
418 
419     /* Figure out what drop operation we're doing */
420     if (pdwEffect)
421     {
422         TRACE("Current drop effect flag %i\n", *pdwEffect);
423         if ((*pdwEffect & DROPEFFECT_MOVE) == DROPEFFECT_MOVE)
424             bCopy = FALSE;
425         if ((*pdwEffect & DROPEFFECT_LINK) == DROPEFFECT_LINK)
426             bLinking = TRUE;
427     }
428 
429     if (SUCCEEDED(pDataObject->QueryGetData(&fmt)))
430     {
431         hr = pDataObject->GetData(&fmt, &medium);
432         TRACE("CFSTR_SHELLIDLIST.\n");
433 
434         /* lock the handle */
435         LPIDA lpcida = (LPIDA)GlobalLock(medium.hGlobal);
436         if (!lpcida)
437         {
438             ReleaseStgMedium(&medium);
439             return E_FAIL;
440         }
441 
442         /* convert the data into pidl */
443         LPITEMIDLIST pidl;
444         LPITEMIDLIST *apidl = _ILCopyCidaToaPidl(&pidl, lpcida);
445         if (!apidl)
446         {
447             ReleaseStgMedium(&medium);
448             return E_FAIL;
449         }
450 
451         CComPtr<IShellFolder> psfDesktop;
452         CComPtr<IShellFolder> psfFrom = NULL;
453 
454         /* Grab the desktop shell folder */
455         hr = SHGetDesktopFolder(&psfDesktop);
456         if (FAILED(hr))
457         {
458             ERR("SHGetDesktopFolder failed\n");
459             SHFree(pidl);
460             _ILFreeaPidl(apidl, lpcida->cidl);
461             ReleaseStgMedium(&medium);
462             return E_FAIL;
463         }
464 
465         /* Find source folder, this is where the clipboard data was copied from */
466         if (_ILIsDesktop(pidl))
467         {
468             /* use desktop shell folder */
469             psfFrom = psfDesktop;
470         }
471         else
472         {
473             hr = psfDesktop->BindToObject(pidl, NULL, IID_PPV_ARG(IShellFolder, &psfFrom));
474             if (FAILED(hr))
475             {
476                 ERR("no IShellFolder\n");
477                 SHFree(pidl);
478                 _ILFreeaPidl(apidl, lpcida->cidl);
479                 ReleaseStgMedium(&medium);
480                 return E_FAIL;
481             }
482         }
483 
484         if (bLinking)
485         {
486             WCHAR wszTargetPath[MAX_PATH];
487             WCHAR wszPath[MAX_PATH];
488             WCHAR wszTarget[MAX_PATH];
489 
490             wcscpy(wszTargetPath, m_sPathTarget);
491 
492             TRACE("target path = %s", debugstr_w(wszTargetPath));
493 
494             /* We need to create a link for each pidl in the copied items, so step through the pidls from the clipboard */
495             for (UINT i = 0; i < lpcida->cidl; i++)
496             {
497                 //Find out which file we're copying
498                 STRRET strFile;
499                 hr = psfFrom->GetDisplayNameOf(apidl[i], SHGDN_FORPARSING, &strFile);
500                 if (FAILED(hr))
501                 {
502                     ERR("Error source obtaining path");
503                     break;
504                 }
505 
506                 hr = StrRetToBufW(&strFile, apidl[i], wszPath, _countof(wszPath));
507                 if (FAILED(hr))
508                 {
509                     ERR("Error putting source path into buffer");
510                     break;
511                 }
512                 TRACE("source path = %s", debugstr_w(wszPath));
513 
514                 // Creating a buffer to hold the combined path
515                 WCHAR buffer_1[MAX_PATH] = L"";
516                 WCHAR *lpStr1;
517                 lpStr1 = buffer_1;
518 
519                 LPWSTR pwszFileName = PathFindFileNameW(wszPath);
520                 LPWSTR pwszExt = PathFindExtensionW(wszPath);
521                 LPWSTR placementPath = PathCombineW(lpStr1, m_sPathTarget, pwszFileName);
522                 CComPtr<IPersistFile> ppf;
523 
524                 //Check to see if it's already a link.
525                 if (!wcsicmp(pwszExt, L".lnk"))
526                 {
527                     //It's a link so, we create a new one which copies the old.
528                     if(!_GetUniqueFileName(placementPath, pwszExt, wszTarget, TRUE))
529                     {
530                         ERR("Error getting unique file name");
531                         hr = E_FAIL;
532                         break;
533                     }
534                     hr = IShellLink_ConstructFromPath(wszPath, IID_PPV_ARG(IPersistFile, &ppf));
535                     if (FAILED(hr)) {
536                         ERR("Error constructing link from file");
537                         break;
538                     }
539 
540                     hr = ppf->Save(wszTarget, FALSE);
541 					if (FAILED(hr))
542 						break;
543 					SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL);
544                 }
545                 else
546                 {
547                     //It's not a link, so build a new link using the creator class and fill it in.
548                     //Create a file name for the link
549                     if (!_GetUniqueFileName(placementPath, L".lnk", wszTarget, TRUE))
550                     {
551                         ERR("Error creating unique file name");
552                         hr = E_FAIL;
553                         break;
554                     }
555 
556                     CComPtr<IShellLinkW> pLink;
557                     hr = CShellLink::_CreatorClass::CreateInstance(NULL, IID_PPV_ARG(IShellLinkW, &pLink));
558                     if (FAILED(hr)) {
559                         ERR("Error instantiating IShellLinkW");
560                         break;
561                     }
562 
563                     WCHAR szDirPath[MAX_PATH], *pwszFile;
564                     GetFullPathName(wszPath, MAX_PATH, szDirPath, &pwszFile);
565                     if (pwszFile) pwszFile[0] = 0;
566 
567                     hr = pLink->SetPath(wszPath);
568                     if(FAILED(hr))
569                         break;
570 
571                     hr = pLink->SetWorkingDirectory(szDirPath);
572                     if(FAILED(hr))
573                         break;
574 
575                     hr = pLink->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
576                     if(FAILED(hr))
577                         break;
578 
579                     hr = ppf->Save(wszTarget, TRUE);
580 					if (FAILED(hr))
581 						break;
582 					SHChangeNotify(SHCNE_CREATE, SHCNF_PATHW, wszTarget, NULL);
583                 }
584             }
585         }
586         else
587         {
588             hr = _CopyItems(psfFrom, lpcida->cidl, (LPCITEMIDLIST*)apidl, bCopy);
589         }
590 
591         SHFree(pidl);
592         _ILFreeaPidl(apidl, lpcida->cidl);
593         ReleaseStgMedium(&medium);
594     }
595     else if (SUCCEEDED(pDataObject->QueryGetData(&fmt2)))
596     {
597         FORMATETC fmt2;
598         InitFormatEtc (fmt2, CF_HDROP, TYMED_HGLOBAL);
599         if (SUCCEEDED(pDataObject->GetData(&fmt2, &medium)) /* && SUCCEEDED(pDataObject->GetData(&fmt2, &medium))*/)
600         {
601             WCHAR wszTargetPath[MAX_PATH + 1];
602             LPWSTR pszSrcList;
603 
604             wcscpy(wszTargetPath, m_sPathTarget);
605             //Double NULL terminate.
606             wszTargetPath[wcslen(wszTargetPath) + 1] = '\0';
607 
608             LPDROPFILES lpdf = (LPDROPFILES) GlobalLock(medium.hGlobal);
609             if (!lpdf)
610             {
611                 ERR("Error locking global\n");
612                 return E_FAIL;
613             }
614             pszSrcList = (LPWSTR) (((byte*) lpdf) + lpdf->pFiles);
615             ERR("Source file (just the first) = %s, target path = %s, bCopy: %d\n", debugstr_w(pszSrcList), debugstr_w(wszTargetPath), bCopy);
616 
617             SHFILEOPSTRUCTW op;
618             ZeroMemory(&op, sizeof(op));
619             op.pFrom = pszSrcList;
620             op.pTo = wszTargetPath;
621             op.hwnd = m_hwndSite;
622             op.wFunc = bCopy ? FO_COPY : FO_MOVE;
623             op.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMMKDIR;
624             hr = SHFileOperationW(&op);
625             return hr;
626         }
627         ERR("Error calling GetData\n");
628         hr = E_FAIL;
629     }
630     else
631     {
632         ERR("No viable drop format.\n");
633         hr = E_FAIL;
634     }
635     return hr;
636 }
637 
638 DWORD WINAPI CFSDropTarget::_DoDropThreadProc(LPVOID lpParameter)
639 {
640     CoInitialize(NULL);
641     _DoDropData *data = static_cast<_DoDropData*>(lpParameter);
642     CComPtr<IDataObject> pDataObject;
643     HRESULT hr = CoGetInterfaceAndReleaseStream (data->pStream, IID_PPV_ARG(IDataObject, &pDataObject));
644 
645     if (SUCCEEDED(hr))
646     {
647         CComPtr<IAsyncOperation> pAsyncOperation;
648         hr = data->This->_DoDrop(pDataObject, data->dwKeyState, data->pt, &data->pdwEffect);
649         if (SUCCEEDED(pDataObject->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pAsyncOperation))))
650         {
651             pAsyncOperation->EndOperation(hr, NULL, data->pdwEffect);
652         }
653     }
654     //Release the CFSFolder and data object holds in the copying thread.
655     data->This->Release();
656     //Release the parameter from the heap.
657     HeapFree(GetProcessHeap(), 0, data);
658     CoUninitialize();
659     return 0;
660 }
661 
662 HRESULT CFSDropTarget_CreateInstance(LPWSTR sPathTarget, REFIID riid, LPVOID * ppvOut)
663 {
664     return ShellObjectCreatorInit<CFSDropTarget>(sPathTarget, riid, ppvOut);
665 }