[SHELL32][SHELLEXT] Fix use of SHFileOperation results and improve log on failure...
[reactos.git] / dll / shellext / mydocs / CMyDocsDropHandler.cpp
1 /*
2 * PROJECT: mydocs
3 * LICENSE: LGPL-2.1+ (https://spdx.org/licenses/LGPL-2.1+)
4 * PURPOSE: MyDocs implementation
5 * COPYRIGHT: Copyright 2020 Katayama Hirofumi MZ (katayama.hirofumi.mz@gmail.com)
6 */
7
8 #include "precomp.hpp"
9
10 WINE_DEFAULT_DEBUG_CHANNEL(mydocs);
11
12 static CLIPFORMAT g_cfHIDA = 0;
13
14 CMyDocsDropHandler::CMyDocsDropHandler()
15 {
16 InterlockedIncrement(&g_ModuleRefCnt);
17 }
18
19 CMyDocsDropHandler::~CMyDocsDropHandler()
20 {
21 InterlockedDecrement(&g_ModuleRefCnt);
22 }
23
24 // IDropTarget
25 STDMETHODIMP
26 CMyDocsDropHandler::DragEnter(IDataObject *pDataObject, DWORD dwKeyState,
27 POINTL pt, DWORD *pdwEffect)
28 {
29 TRACE("(%p)\n", this);
30
31 *pdwEffect &= DROPEFFECT_COPY; // Copy only
32
33 return S_OK;
34 }
35
36 STDMETHODIMP
37 CMyDocsDropHandler::DragOver(DWORD dwKeyState, POINTL pt, DWORD *pdwEffect)
38 {
39 TRACE("(%p)\n", this);
40
41 *pdwEffect &= DROPEFFECT_COPY; // Copy only
42
43 return S_OK;
44 }
45
46 STDMETHODIMP CMyDocsDropHandler::DragLeave()
47 {
48 TRACE("(%p)\n", this);
49 return S_OK;
50 }
51
52 STDMETHODIMP
53 CMyDocsDropHandler::Drop(IDataObject *pDataObject, DWORD dwKeyState,
54 POINTL pt, DWORD *pdwEffect)
55 {
56 TRACE("(%p)\n", this);
57
58 if (!pDataObject)
59 {
60 ERR("pDataObject is NULL\n");
61 *pdwEffect = 0;
62 DragLeave();
63 return E_POINTER;
64 }
65
66 CComPtr<IShellFolder> pDesktop;
67 HRESULT hr = SHGetDesktopFolder(&pDesktop);
68 if (FAILED_UNEXPECTEDLY(hr))
69 return hr;
70
71 // get the clipboard format
72 if (g_cfHIDA == 0)
73 g_cfHIDA = ::RegisterClipboardFormatW(CFSTR_SHELLIDLIST);
74
75 // Retrieve an HIDA (handle of IDA)
76 STGMEDIUM medium;
77 FORMATETC fmt = { g_cfHIDA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
78 hr = pDataObject->GetData(&fmt, &medium);
79 if (FAILED_UNEXPECTEDLY(hr))
80 {
81 *pdwEffect = 0;
82 DragLeave();
83 return E_FAIL;
84 }
85
86 // lock HIDA
87 LPIDA pida = reinterpret_cast<LPIDA>(GlobalLock(medium.hGlobal));
88 UINT iItem, cItems = pida->cidl;
89
90 // get the path of "My Documents"
91 WCHAR szzDir[MAX_PATH + 1];
92 SHGetSpecialFolderPathW(NULL, szzDir, CSIDL_PERSONAL, FALSE);
93 szzDir[lstrlenW(szzDir) + 1] = 0; // ends with double NULs
94
95 // for all source items
96 CStringW strSrcList;
97 WCHAR szSrc[MAX_PATH];
98 PCIDLIST_ABSOLUTE pidlParent = HIDA_GetPIDLFolder(pida);
99 for (iItem = 0; iItem < cItems; ++iItem)
100 {
101 // query source pidl
102 PCITEMID_CHILD pidlChild = HIDA_GetPIDLItem(pida, iItem);
103 CComHeapPtr<ITEMIDLIST> pidl(ILCombine(pidlParent, pidlChild));
104
105 // can get path?
106 szSrc[0] = 0;
107 if (!SHGetPathFromIDListW(pidl, szSrc))
108 {
109 // try to retrieve path from desktop
110 STRRET strret;
111 hr = pDesktop->GetDisplayNameOf(pidl, SHGDN_INFOLDER, &strret);
112 if (FAILED_UNEXPECTEDLY(hr))
113 break;
114 hr = StrRetToBufW(&strret, pidl, szSrc, _countof(szSrc));
115 if (FAILED_UNEXPECTEDLY(hr))
116 break;
117 if (!PathFileExistsW(szSrc))
118 break;
119 }
120
121 if (iItem > 0)
122 strSrcList += L'|'; // separator is '|'
123 strSrcList += szSrc;
124 }
125
126 // unlock HIDA
127 GlobalUnlock(medium.hGlobal);
128
129 if (iItem != cItems)
130 {
131 // source not found
132 CStringW strText;
133 strText.Format(IDS_NOSRCFILEFOUND, szSrc[0] ? szSrc : L"(null)");
134 MessageBoxW(NULL, strText, NULL, MB_ICONERROR);
135
136 *pdwEffect = 0;
137 DragLeave();
138 return E_FAIL;
139 }
140
141 strSrcList += L"||"; // double separators
142
143 // lock the buffer
144 LPWSTR pszzSrcList = strSrcList.GetBuffer();
145
146 // convert every separator to a NUL
147 INT cch = strSrcList.GetLength();
148 for (INT i = 0; i < cch; ++i)
149 {
150 if (pszzSrcList[i] == L'|')
151 pszzSrcList[i] = L'\0';
152 }
153
154 // copy them
155 SHFILEOPSTRUCTW fileop = { NULL };
156 fileop.wFunc = FO_COPY;
157 fileop.pFrom = pszzSrcList;
158 fileop.pTo = szzDir;
159 fileop.fFlags = FOF_ALLOWUNDO | FOF_FILESONLY | FOF_MULTIDESTFILES | FOF_NOCONFIRMMKDIR;
160 int res = SHFileOperationW(&fileop);
161 if (res)
162 {
163 ERR("SHFileOperationW failed with 0x%x\n", res);
164 hr = E_FAIL;
165 }
166
167 // unlock buffer
168 strSrcList.ReleaseBuffer();
169
170 DragLeave();
171 return hr;
172 }
173
174 // IPersistFile
175 STDMETHODIMP CMyDocsDropHandler::GetCurFile(LPOLESTR *ppszFileName)
176 {
177 FIXME("(%p)\n", this);
178 return E_NOTIMPL;
179 }
180
181 STDMETHODIMP CMyDocsDropHandler::IsDirty()
182 {
183 FIXME("(%p)\n", this);
184 return E_NOTIMPL;
185 }
186
187 STDMETHODIMP CMyDocsDropHandler::Load(LPCOLESTR pszFileName, DWORD dwMode)
188 {
189 return S_OK;
190 }
191
192 STDMETHODIMP CMyDocsDropHandler::Save(LPCOLESTR pszFileName, BOOL fRemember)
193 {
194 FIXME("(%p)\n", this);
195 return E_NOTIMPL;
196 }
197
198 STDMETHODIMP CMyDocsDropHandler::SaveCompleted(LPCOLESTR pszFileName)
199 {
200 FIXME("(%p)\n", this);
201 return E_NOTIMPL;
202 }
203
204 // IPersist
205 STDMETHODIMP CMyDocsDropHandler::GetClassID(CLSID * lpClassId)
206 {
207 TRACE("(%p)\n", this);
208
209 if (!lpClassId)
210 {
211 ERR("lpClassId is NULL\n");
212 return E_POINTER;
213 }
214
215 *lpClassId = CLSID_MyDocsDropHandler;
216
217 return S_OK;
218 }