2 #include "DropHandler.h"
7 #include "Environment.h"
9 #include "WaitStatusCursor.h"
13 struct HandleDeleter {
14 typedef HANDLE pointer;
15 void operator()(HANDLE h) { if (h && h != INVALID_HANDLE_VALUE) ::CloseHandle(h); }
18 typedef std::unique_ptr<HANDLE, HandleDeleter> unique_handle;
19 typedef _com_ptr_t<_com_IIID<IFileOperation, &__uuidof(IFileOperation)>> IFileOperationPtr;
20 typedef _com_ptr_t<_com_IIID<IShellItem, &__uuidof(IShellItem)>> IShellItemPtr;
22 bool CopyFileOrFolder(const String& src, const String& dst)
24 std::vector<TCHAR> srcpath(src.length() + 2, 0);
25 std::vector<TCHAR> dstpath(dst.length() + 2, 0);
26 memcpy(&srcpath[0], src.c_str(), src.length() * sizeof(TCHAR));
27 memcpy(&dstpath[0], dst.c_str(), dst.length() * sizeof(TCHAR));
28 SHFILEOPSTRUCT fileop = { 0, FO_COPY, &srcpath[0], &dstpath[0], FOF_NOCONFIRMATION, 0, 0, 0 };
29 return SHFileOperation(&fileop) == 0;
33 // OnDropFiles code from CDropEdit
34 // Copyright 1997 Chris Losinger
36 // shortcut expansion code modified from :
37 // CShortcut, 1996 Rob Warner
40 std::vector<String> GetDroppedFiles(HDROP dropInfo)
42 std::vector<String> files;
43 // Get the number of pathnames that have been dropped
44 UINT wNumFilesDropped = DragQueryFile(dropInfo, 0xFFFFFFFF, NULL, 0);
47 // get all file names. but we'll only need the first one.
48 for (WORD x = 0; x < wNumFilesDropped; x++)
50 // Get the number of bytes required by the file's full pathname
51 UINT wPathnameSize = DragQueryFile(dropInfo, x, NULL, 0);
53 // Allocate memory to contain full pathname & zero byte
55 std::unique_ptr<TCHAR[]> npszFile(new TCHAR[wPathnameSize]);
57 // Copy the pathname into the buffer
58 DragQueryFile(dropInfo, x, npszFile.get(), wPathnameSize);
60 files.push_back(npszFile.get());
65 std::vector<String> FilterFiles(const std::vector<String>& files_src)
67 std::vector<String> files(files_src);
68 TCHAR szTempPath[MAX_PATH];
69 TCHAR szTempPathShort[MAX_PATH];
70 GetTempPath(sizeof(szTempPath) / sizeof(szTempPath[0]), szTempPath);
71 GetShortPathName(szTempPath, szTempPathShort, sizeof(szTempPathShort) / sizeof(szTempPathShort[0]));
73 for (UINT i = 0; i < files.size(); i++)
75 if (paths_IsShortcut(files[i]))
77 // if this was a shortcut, we need to expand it to the target path
78 String expandedFile = ExpandShortcut(files[i]);
80 // if that worked, we should have a real file name
81 if (!expandedFile.empty())
82 files[i] = expandedFile;
84 else if (paths_IsDecendant(files[i], szTempPath) || paths_IsDecendant(files[i], szTempPathShort))
86 String tmpdir = env_GetTempChildPath();
87 CopyFileOrFolder(files[i], tmpdir);
88 files[i] = paths_ConcatPath(tmpdir, paths_FindFileName(files[i]));
94 HRESULT IStream_WriteToFile(IStream *pStream, const String& filename)
96 unique_handle hFile(CreateFile(filename.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
97 if (hFile.get() == INVALID_HANDLE_VALUE)
104 HRESULT hr = pStream->Read(buf, sizeof(buf), &size);
109 WriteFile(hFile.get(), buf, size, &dwWritten, NULL);
114 HRESULT HGLOBAL_WriteToFile(HGLOBAL hGlobal, const String& filename)
116 unique_handle hFile(CreateFile(filename.c_str(), GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
117 if (hFile.get() == INVALID_HANDLE_VALUE)
119 char *p = static_cast<char *>(GlobalLock(hGlobal));
122 SIZE_T size = GlobalSize(hGlobal);
126 if (WriteFile(hFile.get(), p, (size > INT_MAX) ? INT_MAX : static_cast<DWORD>(size), &dwWritten, NULL) == FALSE)
128 GlobalUnlock(hGlobal);
134 GlobalUnlock(hGlobal);
138 HRESULT SetFileWriteTime(const String& filename, const FILETIME& writetime)
140 unique_handle hFile(CreateFile(filename.c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL));
141 if (hFile.get() == INVALID_HANDLE_VALUE)
143 return SetFileTime(hFile.get(), NULL, NULL, &writetime) ? S_OK : E_FAIL;
146 HRESULT GetFileItemsFromIDataObject_CF_HDROP(IDataObject *pDataObj, std::vector<String>& files)
148 FORMATETC fmtetc_cf_hdrop = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
149 STGMEDIUM medium = { 0 };
151 if ((hr = pDataObj->GetData(&fmtetc_cf_hdrop, &medium)) == S_OK)
153 HDROP hDrop = (HDROP)GlobalLock(medium.hGlobal);
156 files = FilterFiles(GetDroppedFiles(hDrop));
157 GlobalUnlock(medium.hGlobal);
159 ReleaseStgMedium(&medium);
164 #define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
165 #define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
167 HRESULT GetFileItemsFromIDataObject_ShellIDList(IDataObject *pDataObj, std::vector<String>& root_files)
170 FORMATETC fmtetc_filedescriptor = { RegisterClipboardFormat(CFSTR_SHELLIDLIST), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
171 STGMEDIUM medium = { 0 };
173 if ((hr = pDataObj->GetData(&fmtetc_filedescriptor, &medium)) == S_OK)
175 CIDA *pcida = (CIDA *)GlobalLock(medium.hGlobal);
178 LPCITEMIDLIST pidlParent = HIDA_GetPIDLFolder(pcida);
179 for (unsigned i = 0; i < pcida->cidl; ++i)
181 IShellItemPtr pShellItem;
182 if (SUCCEEDED(hr = SHCreateShellItem(pidlParent, NULL, HIDA_GetPIDLItem(pcida, i), &pShellItem)))
185 if (SUCCEEDED(hr = pShellItem->GetAttributes(SFGAO_FOLDER, &sfgaof)) && (sfgaof & SFGAO_FOLDER))
188 wchar_t *pPath = NULL;
189 if (SUCCEEDED(hr = pShellItem->GetDisplayName(SIGDN_DESKTOPABSOLUTEPARSING, &pPath)))
191 root_files.push_back(ucr::toTString(pPath));
192 CoTaskMemFree(pPath);
198 IFileOperationPtr pFileOperation;
199 if (SUCCEEDED(hr = pFileOperation.CreateInstance(CLSID_FileOperation, NULL, CLSCTX_ALL)))
202 tmpdir = env_GetTempChildPath();
203 pFileOperation->SetOperationFlags(0);
204 PIDLIST_ABSOLUTE pidlDest;
205 if (SUCCEEDED(hr = SHParseDisplayName(ucr::toUTF16(tmpdir).c_str(), NULL, &pidlDest, 0, NULL)))
207 IShellItemPtr pShellItemDest;
208 SHCreateShellItem(NULL, NULL, pidlDest, &pShellItemDest);
209 pFileOperation->CopyItem(pShellItem, pShellItemDest, NULL, NULL);
210 if (SUCCEEDED(hr = pFileOperation->PerformOperations()))
213 if (SUCCEEDED(hr = pShellItem->GetDisplayName(SIGDN_PARENTRELATIVEPARSING, &pName)))
215 root_files.push_back(paths_ConcatPath(tmpdir, ucr::toTString(pName)));
216 CoTaskMemFree(pName);
224 GlobalUnlock(medium.hGlobal);
226 ReleaseStgMedium(&medium);
231 HRESULT ExtractFileItemFromIDataObject_FileContents(IDataObject *pDataObj, int lindex, const String& filepath)
233 FORMATETC fmtetc_filecontents = { RegisterClipboardFormat(CFSTR_FILECONTENTS), NULL, DVASPECT_CONTENT, lindex, TYMED_HGLOBAL | TYMED_ISTREAM };
234 STGMEDIUM medium = { 0 };
236 if ((hr = pDataObj->GetData(&fmtetc_filecontents, &medium)) == S_OK)
239 if (medium.tymed == TYMED_HGLOBAL)
240 hr = HGLOBAL_WriteToFile(medium.hGlobal, filepath);
241 else if (medium.tymed == TYMED_ISTREAM)
242 hr = IStream_WriteToFile(medium.pstm, filepath);
243 ReleaseStgMedium(&medium);
248 HRESULT GetFileItemsFromIDataObject_FileDescriptor(IDataObject *pDataObj, std::vector<String>& root_files)
250 String tmpdir = env_GetTempChildPath();
251 FORMATETC fmtetc_filedescriptor = { RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
252 STGMEDIUM medium = { 0 };
254 if ((hr = pDataObj->GetData(&fmtetc_filedescriptor, &medium)) == S_OK)
256 FILEGROUPDESCRIPTOR *file_group_descriptor = (FILEGROUPDESCRIPTOR *)GlobalLock(medium.hGlobal);
257 if (file_group_descriptor)
259 for (unsigned i = 0; i < file_group_descriptor->cItems; ++i)
261 String filename = file_group_descriptor->fgd[i].cFileName;
262 String filepath = paths_ConcatPath(tmpdir, filename);
263 if (file_group_descriptor->fgd[i].dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
264 paths_CreateIfNeeded(filepath);
267 ExtractFileItemFromIDataObject_FileContents(pDataObj, i, filepath);
268 if (file_group_descriptor->fgd[i].dwFlags & FD_WRITESTIME)
269 SetFileWriteTime(filepath, file_group_descriptor->fgd[i].ftLastWriteTime);
271 if (filename.find('\\') == String::npos)
272 root_files.push_back(filepath);
274 GlobalUnlock(medium.hGlobal);
276 ReleaseStgMedium(&medium);
283 DropHandler::DropHandler(std::function<void(const std::vector<String>&)> callback)
284 : m_cRef(0), m_callback(callback)
288 DropHandler::~DropHandler()
292 HRESULT STDMETHODCALLTYPE DropHandler::QueryInterface(REFIID riid, void **ppvObject)
294 if (!IsEqualIID(riid, IID_IUnknown) && !IsEqualIID(riid, IID_IDropTarget))
297 return E_NOINTERFACE;
299 *ppvObject = static_cast<IDropTarget *>(this);
304 ULONG STDMETHODCALLTYPE DropHandler::AddRef(void)
306 return InterlockedIncrement(&m_cRef);
309 ULONG STDMETHODCALLTYPE DropHandler::Release(void)
311 if (InterlockedDecrement(&m_cRef) == 0) {
318 HRESULT STDMETHODCALLTYPE DropHandler::DragEnter(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
320 FORMATETC fmtetc_cf_hdrop = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
321 FORMATETC fmtetc_shellidlist = { RegisterClipboardFormat(CFSTR_SHELLIDLIST), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
322 FORMATETC fmtetc_filedescriptor = { RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
323 if (pDataObj->QueryGetData(&fmtetc_cf_hdrop) == S_OK ||
324 pDataObj->QueryGetData(&fmtetc_shellidlist) == S_OK ||
325 pDataObj->QueryGetData(&fmtetc_filedescriptor) == S_OK)
327 *pdwEffect = DROPEFFECT_COPY;
330 *pdwEffect = DROPEFFECT_NONE;
331 return DRAGDROP_S_CANCEL;
334 HRESULT STDMETHODCALLTYPE DropHandler::DragOver(DWORD, POINTL, DWORD *)
339 HRESULT STDMETHODCALLTYPE DropHandler::DragLeave(void)
344 HRESULT DropHandler::Drop(IDataObject* pDataObj, DWORD grfKeyState, POINTL pt, DWORD* pdwEffect)
347 WaitStatusCursor waitstatus(_T("Copying files..."));
348 std::vector<String> files;
349 FORMATETC fmtetc_cf_hdrop = { CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
350 FORMATETC fmtetc_shellidlist = { RegisterClipboardFormat(CFSTR_SHELLIDLIST), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
351 FORMATETC fmtetc_filedescriptor = { RegisterClipboardFormat(CFSTR_FILEDESCRIPTOR), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL };
352 if (pDataObj->QueryGetData(&fmtetc_cf_hdrop) == S_OK &&
353 GetFileItemsFromIDataObject_CF_HDROP(pDataObj, files) == S_OK && files.size() > 0)
355 else if (pDataObj->QueryGetData(&fmtetc_shellidlist) == S_OK &&
356 GetFileItemsFromIDataObject_ShellIDList(pDataObj, files) == S_OK && files.size() > 0)
358 else if (pDataObj->QueryGetData(&fmtetc_filedescriptor) == S_OK &&
359 GetFileItemsFromIDataObject_FileDescriptor(pDataObj, files) == S_OK && files.size() > 0)
361 if (files.size() > 3)