copyfgd.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 25k
Category:

Windows Kernel

Development Platform:

Visual C++

  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 "datautil.h"
  16. class CCopyThread;
  17. HRESULT CreateInstance_CopyThread(HWND hwnd, LPCTSTR pszPath, IDataObject *pdtobj, DWORD *pdwEffect, BOOL fIsBkDropTarget, CCopyThread ** ppct);
  18. extern "C" {
  19.     #include "undo.h"
  20.     #include "defview.h"
  21. };
  22. BOOL GetWininetError(DWORD dwError, BOOL fCopy, LPTSTR pszErrorMsg, DWORD cchSize)
  23. {
  24.     TCHAR szErrorMsg[MAX_PATH];
  25.     BOOL fIsWininetError = ((dwError >= INTERNET_ERROR_BASE) && (dwError <= INTERNET_ERROR_LAST));
  26.     // Default message if FormatMessage doesn't recognize hres
  27.     szErrorMsg[0] = 0;
  28.     LoadString(HINST_THISDLL, (fCopy ? IDS_COPYERROR : IDS_MOVEERROR), szErrorMsg, ARRAYSIZE(szErrorMsg));
  29.     if (fIsWininetError)
  30.     {
  31.         static HINSTANCE s_hinst = NULL;
  32.         if (!s_hinst)
  33.             s_hinst = GetModuleHandle(TEXT("WININET")); // It's okay if we leak it.
  34.         // Can wininet give us extended error messages?
  35.         // We ignore them because it's too late to call InternetGetLastResponseInfo.
  36.         if (ERROR_INTERNET_EXTENDED_ERROR != dwError)
  37.         {
  38.             TCHAR szDetails[MAX_PATH*2];
  39.             FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, (LPCVOID)s_hinst, dwError, 0, szDetails, ARRAYSIZE(szDetails), NULL);
  40.             StrCatBuff(szErrorMsg, TEXT("%s"), ARRAYSIZE(szErrorMsg));
  41.             wnsprintf(pszErrorMsg, cchSize, szErrorMsg, szDetails);
  42.         }
  43.         else
  44.             StrCpyN(pszErrorMsg, szErrorMsg, cchSize);
  45.     }
  46.     else
  47.     {
  48.         StrCpyN(pszErrorMsg, szErrorMsg, cchSize);
  49.     }
  50.     return TRUE;
  51. }
  52. // thunk A/W funciton to access A/W FILEGROUPDESCRIPTOR
  53. // this relies on the fact that the first part of the A/W structures are
  54. // identical. only the string buffer part is different. so all accesses to the
  55. // cFileName field need to go through this function.
  56. //
  57. FILEDESCRIPTOR *GetFileDescriptor(FILEGROUPDESCRIPTOR *pfgd, BOOL fUnicode, int nIndex, LPTSTR pszName)
  58. {
  59.     if (fUnicode)
  60.     {
  61.         // Yes, so grab the data because it matches.
  62.         FILEGROUPDESCRIPTORW * pfgdW = (FILEGROUPDESCRIPTORW *)pfgd;    // cast to what this really is
  63.         // If the filename starts with a leading / we're going to be in trouble, since the rest
  64.         // of the code assumes its going to be a .  Web folders does the leading /, so just lop it
  65.         // off right here.
  66.         WCHAR *pwz;
  67.         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.         uliFileSize.HighPart = pfd->nFileSizeHigh;
  118.         uliFileSize.LowPart = pfd->nFileSizeLow;
  119.         puliTotal->QuadPart += uliFileSize.QuadPart;
  120.     }
  121. }
  122. BOOL IsNameInDescriptor(FILEGROUPDESCRIPTOR *pfgd, BOOL fUnicode, LPCTSTR pszName, UINT iMax)
  123. {
  124.     for (UINT i = 0; i < iMax; i++)
  125.     {
  126.         TCHAR szName[MAX_PATH];
  127.         // WARNING: This may point to a FILEDESCRIPTOR *A or W, but that's ok as long as we ignore the filename.
  128.         FILEDESCRIPTOR *pfd = GetFileDescriptor(pfgd, fUnicode, i, szName);
  129.         if (lstrcmpi(szName, pszName) == 0)
  130.             return TRUE;
  131.     }
  132.     return FALSE;
  133. }
  134. BOOL ShowProgressUI(FILEGROUPDESCRIPTOR *pfgd)
  135. {
  136.     return (0 < pfgd->cItems) && (FD_PROGRESSUI & pfgd->fgd->dwFlags);
  137. }
  138. class CCopyThread
  139.                 : public IUnknown
  140. {
  141. public:
  142.     // *** IUnknown ***
  143.     virtual STDMETHODIMP_(ULONG) AddRef(void);
  144.     virtual STDMETHODIMP_(ULONG) Release(void);
  145.     virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj);
  146.     HRESULT DoCopy(void) {return _DoCopy(_pdtobj);};
  147.     HRESULT DoAsynchCopy(void);
  148.     HRESULT GetEffect(DWORD *pdwEffect) {*pdwEffect = _dwEffect; return S_OK;}; // For synchronous case only.
  149.     friend HRESULT CreateInstance_CopyThread(HWND hwnd, LPCTSTR pszPath, IDataObject *pdtobj, DWORD *pdwEffect, BOOL fIsBkDropTarget, CCopyThread ** ppct);
  150. protected:
  151.     CCopyThread(HWND hwnd, LPCTSTR pszPath, IDataObject *pdtobj, DWORD *pdwEffect, BOOL fIsBkDropTarget);
  152.     ~CCopyThread();
  153. private:
  154.     HRESULT _CopyThreadProc(void);
  155.     static DWORD CALLBACK CopyThreadProc(LPVOID pvThis) { return ((CCopyThread *) pvThis)->_CopyThreadProc(); };
  156.     HRESULT _DoCopy(IDataObject * pdo);
  157.     LONG                _cRef;
  158.     HWND                _hwnd;
  159.     LPCTSTR             _pszPath;
  160.     IDataObject *       _pdtobj;        // Unmarshalled
  161.     IStream *           _pstmDataObjMarshal;  // Carrying the IDataObject across threads
  162.     DWORD               _dwEffect;
  163.     BOOL                _fWindowIsTarget;
  164. };
  165. CCopyThread::CCopyThread(HWND hwnd, LPCTSTR pszPath, IDataObject *pdtobj, DWORD *pdwEffect, BOOL fIsBkDropTarget) : _cRef(1)
  166. {
  167.     DllAddRef();
  168.     // Assert this class was zero inited.
  169.     ASSERT(!_pdtobj);
  170.     ASSERT(!_pstmDataObjMarshal);
  171.     ASSERT(!_dwEffect);
  172.     ASSERT(!_hwnd);
  173.     ASSERT(!_pszPath);
  174.     _hwnd = hwnd;
  175.     // If we are dropping onto the background of the window, we can assume that the window we
  176.     // are passed is the target window of the copy.
  177.     _fWindowIsTarget = fIsBkDropTarget;
  178.     Str_SetPtr((LPTSTR *) &_pszPath, pszPath);
  179.     IUnknown_Set((IUnknown **)&_pdtobj, (IUnknown *)pdtobj);
  180.     // The caller doesn't get the return value in pdwEffect because it happens on a background thread.
  181.     // These the hell out of this because we don't want a cancel operation to not make it back to a caller
  182.     // and have them delete the files anyway.  We also need to make sure moves are done in such a way that the
  183.     // destination (us) moves the files and not the caller.  This is the caller will return from ::Drop() before
  184.     // the files finish copying (moving).
  185.     _dwEffect = *pdwEffect;
  186. }
  187. CCopyThread::~CCopyThread()
  188. {
  189.     IUnknown_Set((IUnknown **)&_pdtobj, NULL);
  190.     IUnknown_Set((IUnknown **)&_pstmDataObjMarshal, NULL);
  191.     Str_SetPtr((LPTSTR *) &_pszPath, NULL);
  192.     DllRelease();
  193. }
  194. HRESULT CCopyThread::DoAsynchCopy(void)
  195. {
  196.     HRESULT hr = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, _pdtobj, &_pstmDataObjMarshal);
  197.     if (SUCCEEDED(hr))
  198.     {
  199.         IUnknown_Set((IUnknown **)&_pdtobj, NULL);
  200.         AddRef();   // pass to thread
  201.         if (SHCreateThread(CCopyThread::CopyThreadProc, this, CTF_COINIT, NULL))
  202.         {
  203.             hr = S_OK;
  204.         }
  205.         else
  206.         {
  207.             hr = E_OUTOFMEMORY;
  208.             Release();  // thread did not take, we need to release
  209.         }
  210.     }
  211.     return hr;
  212. }
  213. HRESULT CCopyThread::_CopyThreadProc(void)
  214. {
  215.     IDataObject * pdo;
  216.     HRESULT hr = CoGetInterfaceAndReleaseStream(_pstmDataObjMarshal, IID_IDataObject, (void **)&pdo);
  217.     _pstmDataObjMarshal = NULL; // CoGetInterfaceAndReleaseStream() released the ref.
  218.     if (EVAL(S_OK == hr))
  219.     {
  220.         hr = _DoCopy(pdo);
  221.         IAsyncOperation * pao;
  222.         if (EVAL(SUCCEEDED(pdo->QueryInterface(IID_IAsyncOperation, (void **) &pao))))
  223.         {
  224.             EVAL(SUCCEEDED(pao->EndOperation(hr, NULL, _dwEffect)));
  225.             pao->Release();
  226.         }
  227.         pdo->Release();
  228.     }
  229.     Release();      // Releae the background thread's ref.
  230.     return hr;
  231. }
  232. HRESULT CCopyThread::_DoCopy(IDataObject * pdo)
  233. {
  234.     HRESULT hres = S_OK;
  235.     HRESULT hresOffset;
  236.     FORMATETC fmteA = {g_cfFileGroupDescriptorA, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  237.     FORMATETC fmteW = {g_cfFileGroupDescriptorW, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  238.     FORMATETC fmteOffset = {g_cfOFFSETS, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  239.     STGMEDIUM mediumFGD = {0}, mediumOffset = {0};
  240.     BOOL fUnicode = FALSE;
  241.     PROGRESSINFO progInfo = {0};
  242.     // We should have only one bit set.
  243.     ASSERT(_dwEffect==DROPEFFECT_COPY || _dwEffect==DROPEFFECT_LINK || _dwEffect==DROPEFFECT_MOVE);
  244.     // Display the progress now because pdo->GetData may be slow, especially if we have to call it
  245.     // twice.IDA_FILEMOVE
  246.     progInfo.ppd = CProgressDialog_CreateInstance(((DROPEFFECT_COPY == _dwEffect) ? IDS_ACTIONTITLECOPY : IDS_ACTIONTITLEMOVE), ((DROPEFFECT_COPY == _dwEffect) ? IDA_FILECOPY : IDA_FILEMOVE), g_hinst);
  247.     if (EVAL(progInfo.ppd))
  248.     {
  249.         WCHAR wzCalcTime[MAX_PATH];
  250.         progInfo.uliBytesCompleted.QuadPart = progInfo.uliBytesTotal.QuadPart = 0;
  251.         EVAL(SUCCEEDED(progInfo.ppd->StartProgressDialog(_hwnd, NULL, PROGDLG_AUTOTIME, NULL)));
  252.         
  253.         EVAL(LoadStringW(HINST_THISDLL, ((DROPEFFECT_COPY == _dwEffect) ? IDS_CALCCOPYTIME : IDS_CALCMOVETIME), wzCalcTime, ARRAYSIZE(wzCalcTime)));
  254.         EVAL(SUCCEEDED(progInfo.ppd->SetLine(2, wzCalcTime, FALSE, NULL)));
  255.     }
  256.     // Try for UNICODE group descriptor first.  If that succeeds, we won't bother trying to
  257.     // ASCII since UNICODE is the "preferred" format.  For ANSI builds, we only try for ANSI
  258.     hres = pdo->GetData(&fmteW, &mediumFGD);
  259.     if (SUCCEEDED(hres))
  260.         fUnicode = TRUE;
  261.     else
  262.         hres = pdo->GetData(&fmteA, &mediumFGD);
  263.     if (SUCCEEDED(hres))
  264.     {
  265.         UINT i, iConflict = 1;
  266.         UINT iTopLevelItem = 0;
  267.         YNLIST ynl;
  268.         DROPHISTORY dh = {0};
  269.         // WARNING: pfgd is really an A or W struct. to deal with this all code needs to use
  270.         // the GetFileDescriptor() function
  271.         FILEGROUPDESCRIPTOR *pfgd = (FILEGROUPDESCRIPTOR *)GlobalLock(mediumFGD.hGlobal);  
  272.         DECLAREWAITCURSOR;
  273.         SetWaitCursor();
  274.         if (progInfo.ppd)
  275.         {
  276.             CalcBytesInFileGrpDescriptor(pfgd, fUnicode, &progInfo.uliBytesTotal);
  277.             // We displayed progress above because pdo->GetData() and CalcBytesInFileGrpDescriptor are slow, but most likely it
  278.             // was just eating into the delay time before the progress appears.  If the caller
  279.             // didn't want UI, we will close it down now.
  280.             if (!ShowProgressUI(pfgd))
  281.                 EVAL(SUCCEEDED(progInfo.ppd->StopProgressDialog()));
  282.             else
  283.                 EVAL(SUCCEEDED(progInfo.ppd->Timer(PDTIMER_RESET, NULL)));
  284.         }
  285.         CreateYesNoList(&ynl);
  286.         // Try & get the offsets too.
  287.         hresOffset = pdo->GetData(&fmteOffset, &mediumOffset);
  288.         if (SUCCEEDED(hresOffset))
  289.         {
  290.             dh.pptOffset = (POINT *)GlobalLock(mediumOffset.hGlobal);
  291.             dh.pptOffset++;  // First item is the anchor
  292.         }
  293.         for (i = 0; i < pfgd->cItems; i++)
  294.         {
  295.             BOOL fTopLevel;
  296.             TCHAR szFullPath[MAX_PATH], szFileName[MAX_PATH];
  297.             FILEDESCRIPTOR *pfd = GetFileDescriptor(pfgd, fUnicode, i, szFileName);
  298.             StrCpyN(szFullPath, _pszPath, ARRAYSIZE(szFullPath));
  299.             // if the source gave us duplicate file names we make them unique here
  300.             // foo (1).txt, foo (2).txt, etc
  301.             // name conflicts with targets still get the replace file confirm UI
  302.             if (IsNameInDescriptor(pfgd, fUnicode, szFileName, i))
  303.             {
  304.                 TCHAR szBuf[MAX_PATH], *pszExt = PathFindExtension(szFileName);
  305.                 wnsprintf(szBuf, ARRAYSIZE(szBuf), TEXT(" (%d)%s"), iConflict++, pszExt);  // " (xxx).msg"
  306.                 // make sure it will fit
  307.                 if (((int)ARRAYSIZE(szFileName) - lstrlen(szFileName)) > (lstrlen(szBuf) - lstrlen(pszExt))) 
  308.                     lstrcpy(pszExt, szBuf);
  309.             }
  310.             // do PathCleanupSpec on the filespec part of the filename because names
  311.             // can be relative paths "Folderfoo.txt", "FolderFolder2foo.txt"
  312.             PathCleanupSpec(szFullPath, PathFindFileName(szFileName));
  313.             // the filename in the descriptor should not be a fully qualified path
  314.             if (PathIsRelative(szFileName))
  315.             {
  316.                 PathAppend(szFullPath, szFileName);
  317.                 fTopLevel = (StrChr(szFileName, TEXT('\')) == NULL &&
  318.                                 StrChr(szFileName, TEXT('/')) == NULL);
  319.             }
  320.             else
  321.             {
  322.                 TraceMsg(TF_WARNING, "CopyFGD: FGD contains full path - ignoring path");
  323.                 PathAppend(szFullPath, PathFindFileName(szFileName));
  324.                 fTopLevel = TRUE;
  325.             }
  326.             if (IsInNoList(&ynl, szFullPath))
  327.             {
  328.                 continue;
  329.             }
  330.             HWND hwndDlgParent;
  331.             if (FAILED(IUnknown_GetWindow(progInfo.ppd, &hwndDlgParent)))
  332.             {
  333.                 hwndDlgParent = _hwnd;
  334.             }
  335.             BOOL fDirectory = (pfd->dwFlags & FD_ATTRIBUTES) && (pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
  336.             // NOTE: SHPathPrepareForWrite() was moved here to insure that we check for replace operations
  337.             // against a real directory (not an empty a: drive, for example).  However, the result is not checked
  338.             // until later - make sure that we don't overwrite 'hres' between here and there.
  339.             hres = SHPathPrepareForWrite(hwndDlgParent, NULL, szFullPath, SHPPFW_DEFAULT | SHPPFW_IGNOREFILENAME);
  340.             switch (ValidateCreateFileFromClip(hwndDlgParent, pfd, szFullPath, &ynl))
  341.             {
  342.             case IDYES:
  343.                 break;
  344.             case IDNO:
  345.                 continue;
  346.             case IDCANCEL:
  347.                 // NOTE: This doesn't do anything because the caller never gets this back
  348.                 //       in the asynch case.
  349.                 _dwEffect = 0;
  350.                 i = (int)pfgd->cItems - 1;
  351.                 hres = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  352.                 continue;
  353.             }
  354.             if (progInfo.ppd)
  355.             {
  356.                 WCHAR wzFileName[MAX_PATH];
  357.                 WCHAR wzTemplateStr[MAX_PATH];
  358.                 WCHAR wzProgressStr[MAX_PATH];
  359.                 if (DROPEFFECT_COPY == _dwEffect)
  360.                     EVAL(LoadStringW(HINST_THISDLL, IDS_COPYING, wzTemplateStr, ARRAYSIZE(wzTemplateStr)));
  361.                 else
  362.                     EVAL(LoadStringW(HINST_THISDLL, IDS_MOVING, wzTemplateStr, ARRAYSIZE(wzTemplateStr)));
  363.                 // Display "Copying 'filename'" or "Moving 'filename'" on line 1
  364.                 SHTCharToUnicode(szFileName, wzFileName, ARRAYSIZE(wzFileName));
  365.                 wnsprintfW(wzProgressStr, ARRAYSIZE(wzProgressStr), wzTemplateStr, wzFileName);
  366.                 progInfo.ppd->SetLine(1, wzProgressStr, FALSE, NULL);
  367.                 // Display the dir on line 2
  368.                 CreateProgressStatusStr(_pszPath, wzProgressStr, ARRAYSIZE(wzProgressStr));
  369.                 progInfo.ppd->SetLine(2, wzProgressStr, FALSE, NULL);
  370.             }
  371.             if (fDirectory)
  372.             {
  373.                 // Call SHPathPrepareForWrite() again without SHPPFW_IGNOREFILENAME so that it
  374.                 // will create the directory if it doesn't already exist.
  375.                 hres = SHPathPrepareForWrite(hwndDlgParent, NULL, szFullPath, SHPPFW_DEFAULT);
  376.                 
  377.                 if (FAILED(hres))
  378.                 {
  379.                     // NOTE: This doesn't do anything because the caller never gets this back
  380.                     //       in the asynch case.
  381.                     _dwEffect = 0;
  382.                     break;
  383.                 }
  384.             }
  385.             else
  386.             {
  387.                 // We want to prepare the path both before and after errors in order to catch different cases.
  388.                 
  389.                 // NOTE: We should be checking the result of SHPathPrepareForWrite() here
  390.                 if (SUCCEEDED(hres))
  391.                 {
  392.                     hres = DataObj_SaveToFile(pdo, g_cfFileContents, i, szFullPath,
  393.                             ((pfd->dwFlags & FD_FILESIZE) ? pfd->nFileSizeLow : 0), &progInfo);
  394.                 }
  395.                 if (FAILED(hres))
  396.                 {
  397.                     // Display an error if it wasn't caused by a user cancel.
  398.                     if (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hres)
  399.                     {
  400.                         TCHAR szTitle[MAX_PATH];
  401.                         if (EVAL(LoadString(HINST_THISDLL, ((DROPEFFECT_COPY == _dwEffect) ? IDS_FILEERRORCOPY : IDS_FILEERRORCOPY), szTitle, ARRAYSIZE(szTitle))))
  402.                         {
  403.                             TCHAR szErrorMsg[MAX_PATH];
  404.                             if (FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, HRESULT_CODE(hres), 0L, szErrorMsg, ARRAYSIZE(szErrorMsg), NULL) ||
  405.                                 GetWininetError(HRESULT_CODE(hres), (DROPEFFECT_COPY == _dwEffect), szErrorMsg, ARRAYSIZE(szErrorMsg)))
  406.                             {
  407.                                 MessageBox(hwndDlgParent, szErrorMsg, szTitle, (MB_ICONERROR | MB_OK | MB_SYSTEMMODAL));
  408.                             }
  409.                         }
  410.                     }
  411.                     // NOTE: This doesn't do anything because the caller never gets this back
  412.                     //       in the asynch case.
  413.                     _dwEffect = 0;
  414.                     break;
  415.                 }
  416.                 if (pfd->dwFlags & (FD_CREATETIME | FD_ACCESSTIME | FD_WRITESTIME))
  417.                 {
  418.                     // BUGBUG: We already fired the SHChangeNotify() in DataObj_SaveToFile() so it won't have the new date. BAD BAD BAD
  419.                     HANDLE hFile = CreateFile(szFullPath, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
  420.                     if (hFile != INVALID_HANDLE_VALUE)
  421.                     {
  422.                         SetFileTime(hFile,
  423.                                     pfd->dwFlags & FD_CREATETIME ? &pfd->ftCreationTime : NULL,
  424.                                     pfd->dwFlags & FD_ACCESSTIME ? &pfd->ftLastAccessTime : NULL,
  425.                                     pfd->dwFlags & FD_WRITESTIME ? &pfd->ftLastWriteTime : NULL);
  426.                         CloseHandle(hFile);
  427.                     }
  428.                 }
  429.             }
  430.             // Only position item if it was created successfully
  431.             // and it is not tucked in a subdir.
  432.             // The last condition is because there is some confusion about whether _hwnd is the
  433.             // target window of the copy or not. If it is, we should tell the window
  434.             // to position the item we just dropped.
  435.             if (SUCCEEDED(hres) && fTopLevel && _fWindowIsTarget)
  436.             {
  437.                 dh.iItem = iTopLevelItem;
  438.                 FS_PositionFileFromDrop(_hwnd, szFullPath, &dh);
  439.                 iTopLevelItem ++;
  440.             }
  441.             if ( SUCCEEDED( hres ) && (pfd->dwFlags & FD_ATTRIBUTES))
  442.             {
  443.                 // set the rest of the attributes if passed...
  444.                 DWORD dwAttrs= (pfd->dwFileAttributes & ~FILE_ATTRIBUTE_DIRECTORY);
  445.                 if ( dwAttrs )
  446.                     SetFileAttributes( szFullPath, dwAttrs );
  447.             }
  448.             if (progInfo.ppd)
  449.             {
  450.                 ULARGE_INTEGER uliFileSize;
  451.                 uliFileSize.HighPart = pfd->nFileSizeHigh;
  452.                 uliFileSize.LowPart = pfd->nFileSizeLow;
  453.                 progInfo.uliBytesCompleted.QuadPart += uliFileSize.QuadPart;
  454.                 progInfo.ppd->SetProgress64(progInfo.uliBytesCompleted.QuadPart, progInfo.uliBytesTotal.QuadPart);
  455.                 if (progInfo.ppd->HasUserCancelled())
  456.                     break;   // Cancel the copy.
  457.             }
  458.         }
  459.         DestroyYesNoList(&ynl);
  460.         if (SUCCEEDED(hresOffset))
  461.             ReleaseStgMediumHGLOBAL(NULL, &mediumOffset);
  462.         if (SUCCEEDED(hres))
  463.         {
  464.             // Inform the caller of what we did.  We don't do optimized moves
  465.             // so the caller is responsible for the delete half of the move and
  466.             // this is now we notify them of that.
  467.             DataObj_SetDWORD(pdo, g_cfPerformedDropEffect, _dwEffect);
  468.             DataObj_SetDWORD(pdo, g_cfLogicalPerformedDropEffect, _dwEffect);
  469.         }
  470.         ResetWaitCursor();
  471.         ReleaseStgMediumHGLOBAL(pfgd, &mediumFGD);
  472.     }
  473.     if (progInfo.ppd)
  474.     {
  475.         progInfo.ppd->StopProgressDialog();
  476.         progInfo.ppd->Release();
  477.     }
  478.     return hres;
  479. }
  480. //===========================
  481. // *** IUnknown Interface ***
  482. HRESULT CCopyThread::QueryInterface(REFIID riid, void **ppvObj)
  483. {
  484.     static const QITAB qit[] = {
  485.         QITABENT(CCopyThread, IUnknown),
  486.         { 0 },
  487.     };
  488.     return QISearch(this, qit, riid, ppvObj);
  489. }
  490. ULONG CCopyThread::AddRef(void)
  491. {
  492.     return InterlockedIncrement(&_cRef);
  493. }
  494. ULONG CCopyThread::Release(void)
  495. {
  496.     if (InterlockedDecrement(&_cRef))
  497.         return _cRef;
  498.     delete this;
  499.     return 0;
  500. }
  501. HRESULT CreateInstance_CopyThread(HWND hwnd, LPCTSTR pszPath, IDataObject *pdtobj, DWORD *pdwEffect, BOOL fIsBkDropTarget, CCopyThread ** ppct)
  502. {
  503.     *ppct = new CCopyThread(hwnd, pszPath, pdtobj, pdwEffect, fIsBkDropTarget);
  504.     return (*ppct ? S_OK : E_FAIL);
  505. }
  506. HRESULT FS_CreateFileFromClip(HWND hwnd, LPCTSTR pszPath, IDataObject *pdtobj, POINTL pt, DWORD *pdwEffect, BOOL fIsBkDropTarget)
  507. {
  508.     HRESULT hr = E_FAIL;
  509.     CCopyThread * pct;
  510.     hr = CreateInstance_CopyThread(hwnd, pszPath, pdtobj, pdwEffect, fIsBkDropTarget, &pct);
  511.     if (SUCCEEDED(hr))
  512.     {
  513.         hr = pct->DoCopy();
  514.         pct->Release();
  515.     }
  516.     return hr;
  517. }
  518. /*****************************************************************************
  519.     DESCRIPTION:
  520.         We know that the IDataObject (pdo) supports the CF_FILEGROUPDESCRIPTOR
  521.     clipboard format, so copy that data to the file system directory pszPath.
  522.     The caller will want to know if this completed or if it was cancelled or
  523.     errored out.  This result will come in the DROPEFFECT out param (pdwEffect).
  524.     Zero (0) will indicate either error or cancel and this code will take care
  525.     of displaying error messages.
  526. *****************************************************************************/
  527. HRESULT FS_AsyncCreateFileFromClip(HWND hwnd, LPCTSTR pszPath, IDataObject * pdo, POINTL pt, DWORD *pdwEffect, BOOL fIsBkDropTarget)
  528. {
  529.     CCopyThread * pct;
  530.     HRESULT hr = CreateInstance_CopyThread(hwnd, pszPath, pdo, pdwEffect, fIsBkDropTarget, &pct);
  531.     if (SUCCEEDED(hr))
  532.     {
  533.         if (DataObj_CanGoAsync(pdo))
  534.             hr = pct->DoAsynchCopy();
  535.         else
  536.             hr = pct->DoCopy();
  537.         pct->Release();
  538.     }
  539.     if (FAILED(hr))
  540.         *pdwEffect = DROPEFFECT_NONE;
  541.     return hr;
  542. }