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