Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

759 lines
27 KiB

  1. /*****************************************************************************\
  2. FILE: copyfgd.cpp
  3. DESCRIPTION:
  4. Copy a FileGroupDescriptor.
  5. \*****************************************************************************/
  6. #include "shellprv.h"
  7. #include <ynlist.h>
  8. #include "ids.h"
  9. #include "pidl.h"
  10. #include "fstreex.h"
  11. #include "copy.h"
  12. #include <shldisp.h>
  13. #include <shlwapi.h>
  14. #include <wininet.h> // InternetGetLastResponseInfo
  15. #include "ynlist.h"
  16. #include "datautil.h"
  17. class CCopyThread;
  18. HRESULT CreateInstance_CopyThread(HWND hwnd, LPCTSTR pszPath, IDataObject *pdtobj, DWORD *pdwEffect, BOOL fIsBkDropTarget, CCopyThread ** ppct);
  19. extern "C" {
  20. #include "undo.h"
  21. #include "defview.h"
  22. };
  23. BOOL GetWininetError(DWORD dwError, BOOL fCopy, LPTSTR pszErrorMsg, DWORD cchSize)
  24. {
  25. TCHAR szErrorMsg[MAX_PATH];
  26. BOOL fIsWininetError = ((dwError >= INTERNET_ERROR_BASE) && (dwError <= INTERNET_ERROR_LAST));
  27. // Default message if FormatMessage doesn't recognize hres
  28. szErrorMsg[0] = 0;
  29. LoadString(HINST_THISDLL, (fCopy ? IDS_COPYERROR : IDS_MOVEERROR), szErrorMsg, ARRAYSIZE(szErrorMsg));
  30. if (fIsWininetError)
  31. {
  32. static HINSTANCE s_hinst = NULL;
  33. if (!s_hinst)
  34. s_hinst = GetModuleHandle(TEXT("WININET")); // It's okay if we leak it.
  35. // Can wininet give us extended error messages?
  36. // We ignore them because it's too late to call InternetGetLastResponseInfo.
  37. if (ERROR_INTERNET_EXTENDED_ERROR != dwError)
  38. {
  39. TCHAR szDetails[MAX_PATH*2];
  40. FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, (LPCVOID)s_hinst, dwError, 0, szDetails, ARRAYSIZE(szDetails), NULL);
  41. StrCatBuff(szErrorMsg, TEXT("%s"), ARRAYSIZE(szErrorMsg));
  42. wnsprintf(pszErrorMsg, cchSize, szErrorMsg, szDetails);
  43. }
  44. else
  45. StrCpyN(pszErrorMsg, szErrorMsg, cchSize);
  46. }
  47. else
  48. {
  49. StrCpyN(pszErrorMsg, szErrorMsg, cchSize);
  50. }
  51. return TRUE;
  52. }
  53. // thunk A/W funciton to access A/W FILEGROUPDESCRIPTOR
  54. // this relies on the fact that the first part of the A/W structures are
  55. // identical. only the string buffer part is different. so all accesses to the
  56. // cFileName field need to go through this function.
  57. //
  58. FILEDESCRIPTOR *GetFileDescriptor(FILEGROUPDESCRIPTOR *pfgd, BOOL fUnicode, int nIndex, LPTSTR pszName)
  59. {
  60. if (fUnicode)
  61. {
  62. // Yes, so grab the data because it matches.
  63. FILEGROUPDESCRIPTORW * pfgdW = (FILEGROUPDESCRIPTORW *)pfgd; // cast to what this really is
  64. // If the filename starts with a leading / we're going to be in trouble, since the rest
  65. // of the code assumes its going to be a \. Web folders does the leading /, so just lop it
  66. // off right here.
  67. WCHAR *pwz = pfgdW->fgd[nIndex].cFileName;
  68. if (pfgdW->fgd[nIndex].cFileName[0] == '/')
  69. {
  70. memmove(pwz, pwz+1, sizeof(pfgdW->fgd[nIndex].cFileName) - sizeof(WCHAR));
  71. }
  72. // Now flip all the /'s to \'s. No dbcs issues, we're unicode!
  73. for (; *pwz; ++pwz)
  74. {
  75. if (*pwz == '/')
  76. {
  77. *pwz = '\\';
  78. }
  79. }
  80. if (pszName)
  81. SHUnicodeToTChar(pfgdW->fgd[nIndex].cFileName, pszName, MAX_PATH);
  82. return (FILEDESCRIPTOR *)&pfgdW->fgd[nIndex]; // cast assume the non string parts are the same!
  83. }
  84. else
  85. {
  86. FILEGROUPDESCRIPTORA *pfgdA = (FILEGROUPDESCRIPTORA *)pfgd; // cast to what this really is
  87. if (pfgdA->fgd[nIndex].cFileName[0] == '/' &&
  88. CharNextA(pfgdA->fgd[nIndex].cFileName) == pfgdA->fgd[nIndex].cFileName+1)
  89. {
  90. memmove (pfgdA->fgd[nIndex].cFileName, pfgdA->fgd[nIndex].cFileName+1, sizeof(pfgdA->fgd[nIndex].cFileName)-sizeof(char));
  91. }
  92. if (pszName)
  93. SHAnsiToTChar(pfgdA->fgd[nIndex].cFileName, pszName, MAX_PATH);
  94. return (FILEDESCRIPTOR *)&pfgdA->fgd[nIndex]; // cast assume the non string parts are the same!
  95. }
  96. }
  97. void CreateProgressStatusStr(LPCTSTR pszDirTo, LPWSTR pwzProgressStr, DWORD cchSize)
  98. {
  99. // IDS_COPYTO also works in move operations. (It doesn't use the work "Copy")
  100. LPTSTR pszMsg = ShellConstructMessageString(HINST_THISDLL, MAKEINTRESOURCE(IDS_COPYTO), pszDirTo);
  101. if (pszMsg)
  102. {
  103. SHTCharToUnicode(pszMsg, pwzProgressStr, cchSize);
  104. LocalFree(pszMsg);
  105. }
  106. else
  107. pwzProgressStr[0] = 0;
  108. }
  109. void CalcBytesInFileGrpDescriptor(FILEGROUPDESCRIPTOR *pfgd, BOOL fUnicode, ULARGE_INTEGER * puliTotal)
  110. {
  111. puliTotal->QuadPart = 0; // Init.
  112. for (UINT i = 0; i < pfgd->cItems; i++)
  113. {
  114. // WARNING: This may point to a FILEDESCRIPTOR *A or W, but that's ok as long as we ignore the filename.
  115. ULARGE_INTEGER uliFileSize;
  116. FILEDESCRIPTOR *pfd = GetFileDescriptor(pfgd, fUnicode, i, NULL);
  117. if (pfd->dwFlags & FD_FILESIZE)
  118. {
  119. uliFileSize.HighPart = pfd->nFileSizeHigh;
  120. uliFileSize.LowPart = pfd->nFileSizeLow;
  121. puliTotal->QuadPart += uliFileSize.QuadPart;
  122. }
  123. }
  124. }
  125. BOOL IsNameInDescriptor(FILEGROUPDESCRIPTOR *pfgd, BOOL fUnicode, LPCTSTR pszName, UINT iMax)
  126. {
  127. for (UINT i = 0; i < iMax; i++)
  128. {
  129. TCHAR szName[MAX_PATH];
  130. // WARNING: This may point to a FILEDESCRIPTOR *A or W, but that's ok as long as we ignore the filename.
  131. FILEDESCRIPTOR *pfd = GetFileDescriptor(pfgd, fUnicode, i, szName);
  132. if (lstrcmpi(szName, pszName) == 0)
  133. return TRUE;
  134. }
  135. return FALSE;
  136. }
  137. BOOL ShowProgressUI(FILEGROUPDESCRIPTOR *pfgd)
  138. {
  139. return (0 < pfgd->cItems) && (FD_PROGRESSUI & pfgd->fgd->dwFlags);
  140. }
  141. class CCopyThread
  142. : public IUnknown
  143. {
  144. public:
  145. // *** IUnknown ***
  146. STDMETHODIMP_(ULONG) AddRef(void);
  147. STDMETHODIMP_(ULONG) Release(void);
  148. STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  149. HRESULT DoCopy(void) {return _DoCopy(_pdtobj);};
  150. HRESULT DoAsynchCopy(void);
  151. HRESULT GetEffect(DWORD *pdwEffect) {*pdwEffect = _dwEffect; return S_OK;}; // For synchronous case only.
  152. friend HRESULT CreateInstance_CopyThread(HWND hwnd, LPCTSTR pszPath, IDataObject *pdtobj, DWORD *pdwEffect, BOOL fIsBkDropTarget, CCopyThread ** ppct);
  153. protected:
  154. CCopyThread(HWND hwnd, LPCTSTR pszPath, IDataObject *pdtobj, DWORD *pdwEffect, BOOL fIsBkDropTarget);
  155. ~CCopyThread();
  156. private:
  157. HRESULT _CopyThreadProc(void);
  158. static DWORD CALLBACK CopyThreadProc(void *pvThis) { return ((CCopyThread *) pvThis)->_CopyThreadProc(); };
  159. HRESULT _DoCopy(IDataObject * pdo);
  160. LONG _cRef;
  161. HWND _hwnd;
  162. LPCTSTR _pszPath;
  163. IDataObject * _pdtobj; // Unmarshalled
  164. IStream * _pstmDataObjMarshal; // Carrying the IDataObject across threads
  165. DWORD _dwEffect;
  166. BOOL _fWindowIsTarget;
  167. };
  168. CCopyThread::CCopyThread(HWND hwnd, LPCTSTR pszPath, IDataObject *pdtobj, DWORD *pdwEffect, BOOL fIsBkDropTarget) : _cRef(1)
  169. {
  170. DllAddRef();
  171. // Assert this class was zero inited.
  172. ASSERT(!_pdtobj);
  173. ASSERT(!_pstmDataObjMarshal);
  174. ASSERT(!_dwEffect);
  175. ASSERT(!_hwnd);
  176. ASSERT(!_pszPath);
  177. _hwnd = hwnd;
  178. // If we are dropping onto the background of the window, we can assume that the window we
  179. // are passed is the target window of the copy.
  180. _fWindowIsTarget = fIsBkDropTarget;
  181. Str_SetPtr((LPTSTR *) &_pszPath, pszPath);
  182. IUnknown_Set((IUnknown **)&_pdtobj, (IUnknown *)pdtobj);
  183. // The caller doesn't get the return value in pdwEffect because it happens on a background thread.
  184. // take these out of this because we don't want a cancel operation to not make it back to a caller
  185. // and have them delete the files anyway. We also need to make sure moves are done in such a way that the
  186. // destination (us) moves the files and not the caller. This is the caller will return from ::Drop() before
  187. // the files finish copying (moving).
  188. _dwEffect = *pdwEffect;
  189. }
  190. CCopyThread::~CCopyThread()
  191. {
  192. IUnknown_Set((IUnknown **)&_pdtobj, NULL);
  193. IUnknown_Set((IUnknown **)&_pstmDataObjMarshal, NULL);
  194. Str_SetPtr((LPTSTR *) &_pszPath, NULL);
  195. DllRelease();
  196. }
  197. HRESULT CCopyThread::DoAsynchCopy(void)
  198. {
  199. HRESULT hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, _pdtobj, &_pstmDataObjMarshal);
  200. if (SUCCEEDED(hr))
  201. {
  202. IUnknown_Set((IUnknown **)&_pdtobj, NULL);
  203. AddRef(); // pass to thread
  204. if (SHCreateThread(CCopyThread::CopyThreadProc, this, CTF_COINIT, NULL))
  205. {
  206. hr = S_OK;
  207. }
  208. else
  209. {
  210. hr = E_OUTOFMEMORY;
  211. Release(); // thread did not take, we need to release
  212. }
  213. }
  214. return hr;
  215. }
  216. HRESULT CCopyThread::_CopyThreadProc(void)
  217. {
  218. IDataObject * pdo;
  219. HRESULT hr = CoGetInterfaceAndReleaseStream(_pstmDataObjMarshal, IID_PPV_ARG(IDataObject, &pdo));
  220. _pstmDataObjMarshal = NULL; // CoGetInterfaceAndReleaseStream() released the ref.
  221. if (S_OK == hr)
  222. {
  223. hr = _DoCopy(pdo);
  224. IAsyncOperation * pao;
  225. if (SUCCEEDED(pdo->QueryInterface(IID_PPV_ARG(IAsyncOperation, &pao))))
  226. {
  227. pao->EndOperation(hr, NULL, _dwEffect);
  228. pao->Release();
  229. }
  230. pdo->Release();
  231. }
  232. Release(); // Releae the background thread's ref.
  233. return hr;
  234. }
  235. HRESULT ConvertCallerFDToTCharFD(const FILEDESCRIPTOR * pfdSource, BOOL fUnicode, FILEDESCRIPTOR * pfdDest)
  236. {
  237. #ifdef UNICODE
  238. if (fUnicode)
  239. {
  240. *pfdDest = *pfdSource;
  241. }
  242. else
  243. {
  244. // pfdSource is really ansi.
  245. const FILEDESCRIPTORA * pfdSourceA = (const FILEDESCRIPTORA *) pfdSource;
  246. // We need to convert, so copy the small part.
  247. *(FILEDESCRIPTORA *)pfdDest = *pfdSourceA;
  248. SHAnsiToUnicode(pfdSourceA->cFileName, pfdDest->cFileName, ARRAYSIZE(pfdDest->cFileName));
  249. }
  250. #else // UNICODE
  251. if (fUnicode)
  252. {
  253. // pfdSource is really unicode.
  254. const FILEDESCRIPTORW * pfdSourceW = (const FILEDESCRIPTORW *) pfdSource;
  255. // We need to convert, so copy the small part.
  256. *(FILEDESCRIPTORA *)pfdDest = *(const FILEDESCRIPTORA *)pfdSource;
  257. SHUnicodeToAnsi(pfdSourceW->cFileName, pfdDest->cFileName, ARRAYSIZE(pfdDest->cFileName));
  258. }
  259. else
  260. {
  261. *pfdDest = *pfdSource;
  262. }
  263. #endif // UNICODE
  264. return S_OK;
  265. }
  266. BOOL IsWininetHRESULT(IN HRESULT hr)
  267. {
  268. // One way to tell if the error is from wininet is to check for the FACILITY_INTERNET
  269. // facility.
  270. BOOL fIsWininet = (FACILITY_INTERNET == HRESULT_FACILITY(hr));
  271. DWORD dwError = HRESULT_CODE(hr);
  272. // However, sometimes that facility won't be set but it will still be a wininet error.
  273. if (!fIsWininet &&
  274. (FACILITY_WIN32 == HRESULT_FACILITY(hr)) &&
  275. ((dwError >= INTERNET_ERROR_BASE) && (dwError <= INTERNET_ERROR_LAST)))
  276. {
  277. // Win #147295. The root of the problem is that an API that returns a dwError can only
  278. // return error numbers in one FACILITY. However, wininet APIs will return error values
  279. // in the FACILITY_INTERNET facility (ERROR_INTERNET_* which is 12000 to INTERNET_ERROR_LAST)
  280. // AND from the FACILITY_WIN32 facility (like ERROR_NO_MORE_FILES). Therefore the caller
  281. // can't just blindly set the FACILITY_INTERNET facility when converting the dwError to an
  282. // HRESULT.
  283. //
  284. // If wininet was smart, they would just use the WIN32 facility since they reserved the 12000
  285. // range.
  286. fIsWininet = TRUE;
  287. }
  288. return fIsWininet;
  289. }
  290. // Puts up a message box if necessary.
  291. // we don't want to see any error with FACILITY_ITF, since we
  292. // can't make sense of those errors anyway.
  293. void MessageBoxIfNecessary(HRESULT hr, DWORD dwEffect, HWND hwndDlgParent)
  294. {
  295. TCHAR szTitle[MAX_PATH];
  296. LoadString(HINST_THISDLL, ((DROPEFFECT_COPY == dwEffect) ? IDS_FILEERRORCOPY : IDS_FILEERRORCOPY), szTitle, ARRAYSIZE(szTitle));
  297. if (IsWininetHRESULT(hr))
  298. {
  299. TCHAR szErrorMsg[MAX_PATH];
  300. if (GetWininetError(HRESULT_CODE(hr), (DROPEFFECT_COPY == dwEffect),
  301. szErrorMsg, ARRAYSIZE(szErrorMsg)))
  302. {
  303. MessageBox(hwndDlgParent, szErrorMsg, szTitle, (MB_ICONERROR | MB_OK | MB_SYSTEMMODAL));
  304. }
  305. }
  306. else if (HRESULT_FACILITY(hr) != FACILITY_ITF)
  307. {
  308. // dont display an error if caused by a user cancel.
  309. if (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr)
  310. {
  311. TCHAR szErrorMsg[MAX_PATH];
  312. if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, HRESULT_CODE(hr),
  313. 0L, szErrorMsg, ARRAYSIZE(szErrorMsg), NULL))
  314. {
  315. MessageBox(hwndDlgParent, szErrorMsg, szTitle, (MB_ICONERROR | MB_OK | MB_SYSTEMMODAL));
  316. }
  317. }
  318. }
  319. // All other errors don't make any sense to the user, so don't put
  320. // up any UI.
  321. }
  322. HRESULT CCopyThread::_DoCopy(IDataObject * pdo)
  323. {
  324. HRESULT hr = S_OK;
  325. FORMATETC fmteA = {g_cfFileGroupDescriptorA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  326. FORMATETC fmteW = {g_cfFileGroupDescriptorW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  327. FORMATETC fmteOffset = {g_cfOFFSETS, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  328. STGMEDIUM mediumFGD = {0}, mediumOffset = {0};
  329. BOOL fUnicode = FALSE;
  330. PROGRESSINFO progInfo = {0};
  331. // We should have only one bit set.
  332. ASSERT(_dwEffect==DROPEFFECT_COPY || _dwEffect==DROPEFFECT_LINK || _dwEffect==DROPEFFECT_MOVE);
  333. // Display the progress now because pdo->GetData may be slow, especially if we have to call it
  334. // twice.IDA_FILEMOVE
  335. progInfo.ppd = CProgressDialog_CreateInstance(((DROPEFFECT_COPY == _dwEffect) ? IDS_ACTIONTITLECOPY : IDS_ACTIONTITLEMOVE), ((DROPEFFECT_COPY == _dwEffect) ? IDA_FILECOPY : IDA_FILEMOVE), g_hinst);
  336. if (progInfo.ppd)
  337. {
  338. WCHAR wzCalcTime[MAX_PATH];
  339. progInfo.uliBytesCompleted.QuadPart = progInfo.uliBytesTotal.QuadPart = 0;
  340. progInfo.ppd->StartProgressDialog(_hwnd, NULL, PROGDLG_AUTOTIME, NULL);
  341. LoadStringW(HINST_THISDLL, ((DROPEFFECT_COPY == _dwEffect) ? IDS_CALCCOPYTIME : IDS_CALCMOVETIME), wzCalcTime, ARRAYSIZE(wzCalcTime));
  342. progInfo.ppd->SetLine(2, wzCalcTime, FALSE, NULL);
  343. }
  344. // First try to massage the IDataObject into a virtual storage that we can use as the
  345. // source of a storage engine copy
  346. bool bPerformManually = true;
  347. if (bPerformManually)
  348. {
  349. // Try for UNICODE group descriptor first. If that succeeds, we won't bother trying to
  350. // ASCII since UNICODE is the "preferred" format. For ANSI builds, we only try for ANSI
  351. hr = pdo->GetData(&fmteW, &mediumFGD);
  352. if (SUCCEEDED(hr))
  353. fUnicode = TRUE;
  354. else
  355. hr = pdo->GetData(&fmteA, &mediumFGD);
  356. if (SUCCEEDED(hr))
  357. {
  358. UINT i, iConflict = 1;
  359. UINT iTopLevelItem = 0;
  360. YNLIST ynl;
  361. DROPHISTORY dh = {0};
  362. // WARNING: pfgd is really an A or W struct. to deal with this all code needs to use
  363. // the GetFileDescriptor() function
  364. FILEGROUPDESCRIPTOR *pfgd = (FILEGROUPDESCRIPTOR *)GlobalLock(mediumFGD.hGlobal);
  365. DECLAREWAITCURSOR;
  366. SetWaitCursor();
  367. BOOL fCantUseDialogHWND = FALSE;
  368. if (progInfo.ppd)
  369. {
  370. CalcBytesInFileGrpDescriptor(pfgd, fUnicode, &progInfo.uliBytesTotal);
  371. // We displayed progress above because pdo->GetData() and CalcBytesInFileGrpDescriptor are slow, but most likely it
  372. // was just eating into the delay time before the progress appears. If the caller
  373. // didn't want UI, we will close it down now.
  374. if (!ShowProgressUI(pfgd))
  375. {
  376. progInfo.ppd->StopProgressDialog();
  377. fCantUseDialogHWND = TRUE;
  378. }
  379. else
  380. progInfo.ppd->Timer(PDTIMER_RESET, NULL);
  381. }
  382. CreateYesNoList(&ynl);
  383. // Try & get the offsets too.
  384. HRESULT hrOffset = pdo->GetData(&fmteOffset, &mediumOffset);
  385. if (SUCCEEDED(hrOffset))
  386. {
  387. dh.pptOffset = (POINT *)GlobalLock(mediumOffset.hGlobal);
  388. dh.pptOffset++; // First item is the anchor
  389. }
  390. for (i = 0; i < pfgd->cItems; i++)
  391. {
  392. BOOL fTopLevel;
  393. TCHAR szFullPath[MAX_PATH], szFileName[MAX_PATH];
  394. FILEDESCRIPTOR *pfd = GetFileDescriptor(pfgd, fUnicode, i, szFileName);
  395. StrCpyN(szFullPath, _pszPath, ARRAYSIZE(szFullPath));
  396. // if the source gave us duplicate file names we make them unique here
  397. // foo (1).txt, foo (2).txt, etc
  398. // name conflicts with targets still get the replace file confirm UI
  399. if (IsNameInDescriptor(pfgd, fUnicode, szFileName, i))
  400. {
  401. TCHAR szBuf[MAX_PATH], *pszExt = PathFindExtension(szFileName);
  402. wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT(" (%d)%s"), iConflict++, pszExt); // " (xxx).msg"
  403. // make sure it will fit
  404. if (((int)ARRAYSIZE(szFileName) - lstrlen(szFileName)) > (lstrlen(szBuf) - lstrlen(pszExt)))
  405. lstrcpy(pszExt, szBuf);
  406. }
  407. // do PathCleanupSpec on the filespec part of the filename because names
  408. // can be relative paths "Folder\foo.txt", "Folder\Folder2\foo.txt"
  409. PathCleanupSpec(szFullPath, PathFindFileName(szFileName));
  410. // the filename in the descriptor should not be a fully qualified path
  411. if (PathIsRelative(szFileName))
  412. {
  413. PathAppend(szFullPath, szFileName);
  414. fTopLevel = (StrChr(szFileName, TEXT('\\')) == NULL &&
  415. StrChr(szFileName, TEXT('/')) == NULL);
  416. }
  417. else
  418. {
  419. TraceMsg(TF_WARNING, "CopyFGD: FGD contains full path - ignoring path");
  420. PathAppend(szFullPath, PathFindFileName(szFileName));
  421. fTopLevel = TRUE;
  422. }
  423. if (IsInNoList(&ynl, szFullPath))
  424. {
  425. continue;
  426. }
  427. HWND hwndDlgParent;
  428. if (fCantUseDialogHWND || FAILED(IUnknown_GetWindow(progInfo.ppd, &hwndDlgParent)))
  429. {
  430. hwndDlgParent = _hwnd;
  431. }
  432. BOOL fDirectory = (pfd->dwFlags & FD_ATTRIBUTES) && (pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  433. // NOTE: SHPathPrepareForWrite() was moved here to insure that we check for replace operations
  434. // against a real directory (not an empty a: drive, for example). However, the result is not checked
  435. // until later - make sure that we don't overwrite 'hr' between here and there.
  436. hr = SHPathPrepareForWrite(hwndDlgParent, NULL, szFullPath, SHPPFW_DEFAULT | SHPPFW_IGNOREFILENAME);
  437. FILEDESCRIPTOR fd = {0};
  438. ConvertCallerFDToTCharFD(pfd, fUnicode, &fd);
  439. switch (ValidateCreateFileFromClip(hwndDlgParent, &fd, szFullPath, &ynl))
  440. {
  441. case IDYES:
  442. break;
  443. case IDNO:
  444. // mil bug 127038
  445. // when somebody says "no don't overwrite", we don't want the performed
  446. // drop effect to get set to DROPEFFECT_MOVE, so we set it to DROPEFFECT_NONE
  447. // here.
  448. _dwEffect = 0;
  449. continue;
  450. case IDCANCEL:
  451. // NOTE: This doesn't do anything because the caller never gets this back
  452. // in the asynch case.
  453. _dwEffect = 0;
  454. i = (int)pfgd->cItems - 1;
  455. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  456. continue;
  457. }
  458. if (progInfo.ppd)
  459. {
  460. WCHAR wzFileName[MAX_PATH];
  461. WCHAR wzTemplateStr[MAX_PATH];
  462. WCHAR wzProgressStr[MAX_PATH];
  463. if (DROPEFFECT_COPY == _dwEffect)
  464. LoadStringW(HINST_THISDLL, IDS_COPYING, wzTemplateStr, ARRAYSIZE(wzTemplateStr));
  465. else
  466. LoadStringW(HINST_THISDLL, IDS_MOVING, wzTemplateStr, ARRAYSIZE(wzTemplateStr));
  467. // Display "Copying 'filename'" or "Moving 'filename'" on line 1
  468. SHTCharToUnicode(szFileName, wzFileName, ARRAYSIZE(wzFileName));
  469. wnsprintfW(wzProgressStr, ARRAYSIZE(wzProgressStr), wzTemplateStr, wzFileName);
  470. progInfo.ppd->SetLine(1, wzProgressStr, FALSE, NULL);
  471. // Display the dir on line 2
  472. CreateProgressStatusStr(_pszPath, wzProgressStr, ARRAYSIZE(wzProgressStr));
  473. progInfo.ppd->SetLine(2, wzProgressStr, FALSE, NULL);
  474. }
  475. if (fDirectory)
  476. {
  477. // Call SHPathPrepareForWrite() again without SHPPFW_IGNOREFILENAME so that it
  478. // will create the directory if it doesn't already exist.
  479. hr = SHPathPrepareForWrite(hwndDlgParent, NULL, szFullPath, SHPPFW_DEFAULT);
  480. if (FAILED(hr))
  481. {
  482. // NOTE: This doesn't do anything because the caller never gets this back
  483. // in the asynch case.
  484. _dwEffect = 0;
  485. break;
  486. }
  487. }
  488. else
  489. {
  490. // We want to prepare the path both before and after errors in order to catch different cases.
  491. // NOTE: We should be checking the result of SHPathPrepareForWrite() here
  492. if (SUCCEEDED(hr))
  493. {
  494. hr = DataObj_SaveToFile(pdo, g_cfFileContents, i, szFullPath, pfd, &progInfo);
  495. }
  496. if (FAILED(hr))
  497. {
  498. MessageBoxIfNecessary(hr, _dwEffect, hwndDlgParent);
  499. // NOTE: This doesn't do anything because the caller never gets this back
  500. // in the asynch case.
  501. _dwEffect = 0;
  502. break;
  503. }
  504. }
  505. // Only position item if it was created successfully
  506. // and it is not tucked in a subdir.
  507. // The last condition is because there is some confusion about whether _hwnd is the
  508. // target window of the copy or not. If it is, we should tell the window
  509. // to position the item we just dropped.
  510. if (SUCCEEDED(hr) && fTopLevel && _fWindowIsTarget)
  511. {
  512. dh.iItem = iTopLevelItem;
  513. if (SUCCEEDED(hrOffset) && dh.pptOffset)
  514. {
  515. // need to range check the points in case # of items in the list does not match the #
  516. // of points. this happens when defview does not know the right number of items
  517. // in the data object.
  518. SIZE_T cbSize = GlobalSize(mediumOffset.hGlobal);
  519. if (((dh.iItem + 1) * sizeof(dh.pptOffset[0])) > cbSize)
  520. {
  521. dh.pptOffset = NULL;
  522. ReleaseStgMediumHGLOBAL(NULL, &mediumOffset);
  523. hrOffset = E_FAIL; // for test below
  524. }
  525. }
  526. PositionFileFromDrop(_hwnd, szFullPath, &dh);
  527. iTopLevelItem++;
  528. }
  529. if (progInfo.ppd)
  530. {
  531. ULARGE_INTEGER uliFileSize;
  532. uliFileSize.HighPart = pfd->nFileSizeHigh;
  533. uliFileSize.LowPart = pfd->nFileSizeLow;
  534. progInfo.uliBytesCompleted.QuadPart += uliFileSize.QuadPart;
  535. progInfo.ppd->SetProgress64(progInfo.uliBytesCompleted.QuadPart, progInfo.uliBytesTotal.QuadPart);
  536. if (progInfo.ppd->HasUserCancelled())
  537. break; // Cancel the copy.
  538. }
  539. }
  540. DestroyYesNoList(&ynl);
  541. if (SUCCEEDED(hrOffset))
  542. ReleaseStgMediumHGLOBAL(NULL, &mediumOffset);
  543. ResetWaitCursor();
  544. ReleaseStgMediumHGLOBAL(pfgd, &mediumFGD);
  545. }
  546. }
  547. if (SUCCEEDED(hr))
  548. {
  549. // Inform the caller of what we did. We don't do optimized moves
  550. // so the caller is responsible for the delete half of the move and
  551. // this is now we notify them of that.
  552. DataObj_SetDWORD(pdo, g_cfPerformedDropEffect, _dwEffect);
  553. DataObj_SetDWORD(pdo, g_cfLogicalPerformedDropEffect, _dwEffect);
  554. }
  555. if (progInfo.ppd)
  556. {
  557. progInfo.ppd->StopProgressDialog();
  558. progInfo.ppd->Release();
  559. }
  560. return hr;
  561. }
  562. //===========================
  563. // *** IUnknown Interface ***
  564. HRESULT CCopyThread::QueryInterface(REFIID riid, void **ppvObj)
  565. {
  566. static const QITAB qit[] = {
  567. QITABENT(CCopyThread, IUnknown),
  568. { 0 },
  569. };
  570. return QISearch(this, qit, riid, ppvObj);
  571. }
  572. ULONG CCopyThread::AddRef(void)
  573. {
  574. return InterlockedIncrement(&_cRef);
  575. }
  576. ULONG CCopyThread::Release(void)
  577. {
  578. if (InterlockedDecrement(&_cRef))
  579. return _cRef;
  580. delete this;
  581. return 0;
  582. }
  583. HRESULT CreateInstance_CopyThread(HWND hwnd, LPCTSTR pszPath, IDataObject *pdtobj, DWORD *pdwEffect, BOOL fIsBkDropTarget, CCopyThread ** ppct)
  584. {
  585. *ppct = new CCopyThread(hwnd, pszPath, pdtobj, pdwEffect, fIsBkDropTarget);
  586. return (*ppct ? S_OK : E_FAIL);
  587. }
  588. HRESULT CFSFolder_CreateFileFromClip(HWND hwnd, LPCTSTR pszPath, IDataObject *pdtobj, POINTL pt, DWORD *pdwEffect, BOOL fIsBkDropTarget)
  589. {
  590. CCopyThread * pct;
  591. HRESULT hr = CreateInstance_CopyThread(hwnd, pszPath, pdtobj, pdwEffect, fIsBkDropTarget, &pct);
  592. if (SUCCEEDED(hr))
  593. {
  594. hr = pct->DoCopy();
  595. pct->Release();
  596. }
  597. return hr;
  598. }
  599. /*****************************************************************************\
  600. We know that the IDataObject (pdo) supports the CF_FILEGROUPDESCRIPTOR
  601. clipboard format, so copy that data to the file system directory pszPath.
  602. The caller will want to know if this completed or if it was cancelled or
  603. errored out. This result will come in the DROPEFFECT out param (pdwEffect).
  604. Zero (0) will indicate either error or cancel and this code will take care
  605. of displaying error messages.
  606. \*****************************************************************************/
  607. HRESULT CFSFolder_AsyncCreateFileFromClip(HWND hwnd, LPCTSTR pszPath, IDataObject *pdo,
  608. POINTL pt, DWORD *pdwEffect, BOOL fIsBkDropTarget)
  609. {
  610. CCopyThread * pct;
  611. HRESULT hr = CreateInstance_CopyThread(hwnd, pszPath, pdo, pdwEffect, fIsBkDropTarget, &pct);
  612. if (SUCCEEDED(hr))
  613. {
  614. if (DataObj_CanGoAsync(pdo))
  615. hr = pct->DoAsynchCopy();
  616. else
  617. hr = pct->DoCopy();
  618. pct->Release();
  619. }
  620. if (FAILED(hr))
  621. *pdwEffect = DROPEFFECT_NONE;
  622. return hr;
  623. }