Leaked source code of windows server 2003
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.

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