📄 ftpcm.cpp
字号:
switch (FtpConfirmReplaceDialog(hwnd, &wfdSrc, &wfdDest, nObjs, pff))
{
case IDC_REPLACE_YESTOALL:
*pOps = opsYesToAll;
// FALLTHROUGH
case IDC_REPLACE_YES:
hr = S_OK;
break;
case IDC_REPLACE_NOTOALL:
*pOps = opsNoToAll;
// FALLTHROUGH
case IDC_REPLACE_NO:
hr = S_FALSE;
break;
default:
ASSERT(0); // Huh?
// FALLTHROUGH
case IDC_REPLACE_CANCEL:
*pOps = opsCancel;
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
break;
}
FindClose(hfindDest);
}
}
}
}
return hr;
}
// The following struct is used when recursively downloading
// files/dirs from the FTP server after a "Download" verb.
typedef struct tagDOWNLOADSTRUCT
{
LPCWSTR pwzDestRootPath; // Dir on FileSys of the Download Destination
LPCITEMIDLIST pidlRoot; // Base URL of the Download Source
DWORD dwInternetFlags; // Binary, ASCII, AutoDetect?
HWND hwndParent; // hwnd for Confirm UI
OPS ops; // Do we cancel?
CFtpFolder * pff;
CFtpDir * pfd;
// Progress
PROGRESSINFO progInfo;
} DOWNLOADSTRUCT;
/*****************************************************************************\
FUNCTION: _CalcDestName
DESCRIPTION:
This recursive function starts at pwzDestDir as the dest FS path and
pidlRoot as the src ftp path. We need to construct pwzDestPath which
is the current path. This will be done by adding the relative path
(pidlFull - pidlRoot) to pwzDestDir. pidlFull can point to either a file
or a directory.
PARAMETERS: (Example. "C:\dir1\dir2\dir3\file.txt")
pwzDestParentPath: "C:\dir1\dir2\dir3"
pwzDestDir: "C:\dir1\dir2\dir3\file.txt"
pwzDestFileName: "file.txt"
Example. "C:\dir1\dir2\dir3\"
pwzDestParentPath: "C:\dir1\dir2"
pwzDestDir: "C:\dir1\dir2\dir3"
pwzDestFileName: "dir3"
\*****************************************************************************/
HRESULT _CalcDestName(LPCWSTR pwzDestDir, LPCITEMIDLIST pidlRoot, LPCITEMIDLIST pidlFull, LPWSTR pwzDestParentPath, DWORD cchDestParentPathSize,
LPWSTR pwzDestPath, DWORD cchDestPathSize, LPWSTR pwzDestFileName, DWORD cchDestFileNameSize)
{
HRESULT hr = S_OK;
WCHAR wzFtpPathTemp[MAX_PATH];
WCHAR wzFSPathTemp[MAX_PATH];
LPITEMIDLIST pidlRootIterate = (LPITEMIDLIST) pidlRoot; // I promise to iterate only
LPITEMIDLIST pidlFullIterate = (LPITEMIDLIST) pidlFull; // I promise to iterate only
// This one is easy.
StrCpyNW(pwzDestFileName, FtpPidl_GetLastFileDisplayName(pidlFull), cchDestFileNameSize); // The dest filename is easy.
// Let's find the relative path between pidlRoot and pidlFull.
while (!ILIsEmpty(pidlRootIterate) && !ILIsEmpty(pidlFullIterate) && FtpItemID_IsEqual(pidlRootIterate, pidlFullIterate))
{
pidlFullIterate = _ILNext(pidlFullIterate);
pidlRootIterate = _ILNext(pidlRootIterate);
}
ASSERT(ILIsEmpty(pidlRootIterate) && !ILIsEmpty(pidlFullIterate)); // Asure pidlFull is a superset of pidlRoot
LPITEMIDLIST pidlParent = ILClone(pidlFullIterate);
if (pidlParent)
{
ILRemoveLastID(pidlParent); // Remove the item that will be created (file or dir)
GetDisplayPathFromPidl(pidlParent, wzFtpPathTemp, ARRAYSIZE(wzFtpPathTemp), TRUE); // Full path w/o last item.
StrCpyNW(pwzDestParentPath, pwzDestDir, cchDestParentPathSize); // Put the base dest.
UrlPathToFilePath(wzFtpPathTemp, wzFSPathTemp, ARRAYSIZE(wzFSPathTemp));
if (!PathAppendW(pwzDestParentPath, wzFSPathTemp))
hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW); // Path too long, probably.
ILFree(pidlParent);
}
if (SUCCEEDED(hr))
{
GetDisplayPathFromPidl(pidlFullIterate, wzFtpPathTemp, ARRAYSIZE(wzFSPathTemp), FALSE); // Full Path including item.
StrCpyNW(pwzDestPath, pwzDestDir, cchDestParentPathSize); // Put the base dest.
UrlPathToFilePath(wzFtpPathTemp, wzFSPathTemp, ARRAYSIZE(wzFSPathTemp));
if (!PathAppendW(pwzDestPath, wzFSPathTemp))
hr = HRESULT_FROM_WIN32(ERROR_BUFFER_OVERFLOW); // Path too long, probably.
}
return hr;
}
// This defines the size of a directory measured by the amount of time it would take compared to a file.
#define VIRTUAL_DIR_SIZE 1000 // about 1k.
/*****************************************************************************\
FUNCTION: DownloadItemStackPig
DESCRIPTION:
This function will download the specified item and it's contents if it
is a directory.
\*****************************************************************************/
HRESULT DownloadItemStackPig(HINTERNET hint, LPCITEMIDLIST pidlFull, BOOL * pfValidhinst, DOWNLOADSTRUCT * pDownLoad, CFtpDir ** ppfd)
{
HRESULT hr;
WCHAR wzDestParentPath[MAX_PATH]; // If item is "C:\dir1\dir2copy\", the this is "C:\dir1"
WCHAR wzDestPath[MAX_PATH]; // This is "C:\dir1\dir2copy\"
WCHAR wzDestFileName[MAX_PATH]; // This is "dir2copy"
hr = _CalcDestName(pDownLoad->pwzDestRootPath, pDownLoad->pidlRoot, pidlFull, wzDestParentPath, ARRAYSIZE(wzDestParentPath), wzDestPath, ARRAYSIZE(wzDestPath), wzDestFileName, ARRAYSIZE(wzDestFileName));
if (SUCCEEDED(hr))
{
if (pDownLoad->progInfo.ppd)
EVAL(SUCCEEDED(UpdateDownloadProgress(&(pDownLoad->progInfo), pidlFull, wzDestParentPath, wzDestFileName)));
if (pDownLoad->progInfo.ppd && pDownLoad->progInfo.ppd->HasUserCancelled())
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
else
{
// Is this a dir/folder that we need to recurse into?
if (FILE_ATTRIBUTE_DIRECTORY & FtpPidl_GetAttributes(pidlFull))
{
// Yes, so let's go...
if (EVAL((PathFileExistsW(wzDestPath) && PathIsDirectoryW(wzDestPath)) ||
CreateDirectoryW(wzDestPath, NULL)))
{
EVAL(SetFileAttributes(wzDestPath, FtpPidl_GetAttributes(pidlFull)));
hr = pDownLoad->pfd->GetFtpSite()->GetFtpDir(pidlFull, ppfd);
if (!EVAL(SUCCEEDED(hr)))
TraceMsg(TF_ERROR, "DownloadItemStackPig() GetFtpDir failed hr=%#08lx", hr);
}
else
{
hr = E_FAIL;
TraceMsg(TF_ERROR, "DownloadItemStackPig() CreateDirectory or PathFileExists failed hr=%#08lx", hr);
}
}
else
{
BOOL fDeleteRequired;
ULARGE_INTEGER uliFileSize;
pDownLoad->progInfo.dwCompletedInCurFile = 0;
pDownLoad->progInfo.dwLastDisplayed = 0;
hr = ConfirmDownloadReplace(wzDestPath, pidlFull, &(pDownLoad->ops), GetProgressHWnd(pDownLoad->progInfo.ppd, pDownLoad->hwndParent), pDownLoad->pff, pDownLoad->pfd, 1, &fDeleteRequired);
if (S_OK == hr)
{
if (fDeleteRequired)
{
if (!DeleteFileW(wzDestPath))
hr = HRESULT_FROM_WIN32(GetLastError());
}
// Don't copy the file if it's a SoftLink because of the possible
// recursion case.
if (EVAL(SUCCEEDED(hr)) && (0 != FtpPidl_GetAttributes(pidlFull)))
{
// Contemplate adding a callback function in order to feed the status bar.
hr = FtpGetFileExPidlWrap(hint, TRUE, pidlFull, wzDestPath, TRUE, FtpPidl_GetAttributes(pidlFull), pDownLoad->dwInternetFlags, (DWORD_PTR)&(pDownLoad->progInfo));
if (FAILED(hr))
{
if (HRESULT_FROM_WIN32(ERROR_CANCELLED) != hr)
{
// We need to display the error now while the extended error info is still valid.
// This is because as we walk out of the resursive call, we will be calling
// FtpSetCurrentDirectory() which will wipe clean the extended error msg.
DisplayWininetError(pDownLoad->hwndParent, TRUE, HRESULT_CODE(hr), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_DOWNLOADING, IDS_FTPERR_WININET, MB_OK, pDownLoad->progInfo.ppd);
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Don't display any more error dialogs.
}
}
else
{
// The docs imply that (FILE_SHARE_READ | FILE_SHARE_WRITE) means that other callers need both, but
// I want them to be able to use either.
HANDLE hFile = CreateFileW(wzDestPath, GENERIC_WRITE, (FILE_SHARE_READ | FILE_SHARE_WRITE), NULL, OPEN_EXISTING, FtpPidl_GetAttributes(pidlFull), NULL);
// FtpGetFile() won't set the time/date correctly, so we will.
if (EVAL(INVALID_HANDLE_VALUE != hFile))
{
FILETIME ftLastWriteTime = FtpPidl_GetFileTime(ILFindLastID(pidlFull));
// Since the file time on the disk is stored in a time zone independent way (UTC)
// we have a problem because FTP WIN32_FIND_DATA is in the local time zone. So we
// need to convert the FTP local time to UTC when we set the file.
// Note that we are using an optimization that uses the fact that FTP always
// has the same time for LastAccessTime, LastWriteTime, and CreationTime.
// ASSERT(pwfd->ftCreationTime.dwLowDateTime = pwfd->ftLastAccessTime.dwLowDateTime = pwfd->ftLastWriteTime.dwLowDateTime);
// ASSERT(pwfd->ftCreationTime.dwHighDateTime = pwfd->ftLastAccessTime.dwHighDateTime = pwfd->ftLastWriteTime.dwHighDateTime);
// priv.h has notes on how time works.
SetFileTime(hFile, &ftLastWriteTime, &ftLastWriteTime, &ftLastWriteTime);
CloseHandle(hFile);
}
SHChangeNotify(SHCNE_CREATE, SHCNF_PATH, wzDestPath, NULL);
}
}
}
uliFileSize.QuadPart = FtpPidl_GetFileSize(pidlFull);
pDownLoad->progInfo.uliBytesCompleted.QuadPart += uliFileSize.QuadPart;
}
}
}
if (pfValidhinst)
*pfValidhinst = (pDownLoad->progInfo.hint ? TRUE : FALSE);
return hr;
}
/*****************************************************************************\
FUNCTION: DownloadItemCB
DESCRIPTION:
This function will download the specified item and it's contents if it
is a directory.
\*****************************************************************************/
HRESULT DownloadItemCB(LPVOID pvFuncCB, HINTERNET hint, LPCITEMIDLIST pidlFull, BOOL * pfValidhinst, LPVOID pvData)
{
DOWNLOADSTRUCT * pDownLoad = (DOWNLOADSTRUCT *) pvData;
LPFNPROCESSITEMCB pfnProcessItemCB = (LPFNPROCESSITEMCB) pvFuncCB;
CFtpDir * pfdNew = NULL;
HRESULT hr = DownloadItemStackPig(hint, pidlFull, pfValidhinst, pDownLoad, &pfdNew);
if (SUCCEEDED(hr) && pfdNew) // pfdNew Maybe NULL if cancelled
{
CFtpDir * pfdOriginal = pDownLoad->pfd;
pDownLoad->pfd = pfdNew;
hr = EnumFolder(pfnProcessItemCB, hint, pidlFull, pDownLoad->pff->GetCWireEncoding(), pfValidhinst, pvData);
pDownLoad->pfd = pfdOriginal;
pfdNew->Release();
}
return hr;
}
// BUGBUG/TODO: First, make this work on pidls that Bind to IStorages.
// Second, nuke the CDownloadDialog code.
HRESULT ShowDownloadDialog(HWND hwnd, LPTSTR pszPath, DWORD cchSize)
{
TCHAR szMessage[MAX_URL_STRING];
HRESULT hr;
LPITEMIDLIST pidlDefault = NULL;
LPITEMIDLIST pidlFolder = NULL;
HKEY hkey = NULL;
IStream * pstrm = NULL;
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, SZ_REGKEY_MICROSOFTSOFTWARE, 0, (KEY_READ | KEY_WRITE), &hkey))
{
pstrm = SHOpenRegStream(hkey, SZ_REGKEY_FTPCLASS, SZ_REGVALUE_DOWNLOAD_DIR, STGM_READWRITE);
if (pstrm)
ILLoadFromStream(pstrm, &pidlDefault); // Will return (NULL == pidlDefault) if the reg value is empty.
}
if (!pidlDefault && (SHELL_VERSION_W95NT4 == GetShellVersion())) // If reg key is empty.
pidlDefault = SHCloneSpecialIDList(NULL, CSIDL_PERSONAL, TRUE);
EVAL(LoadString(HINST_THISDLL, IDS_DLG_DOWNLOAD_TITLE, szMessage, ARRAYSIZE(szMessage)));
hr = BrowseForDir(hwnd, szMessage, pidlDefault, &pidlFolder);
if (pstrm)
{
// Do we want to save the new pidl?
if (S_OK == hr)
{
LARGE_INTEGER li = {0};
ULARGE_INTEGER uli = {0};
// rewind the stream to the beginning so that when we
// add a new pidl it does not get appended to the first one
pstrm->Seek(li, STREAM_SEEK_SET, &uli);
ILSaveToStream(pstrm, pidlFolder);
}
pstrm->Release();
}
if (S_OK == hr)
{
ASSERT(cchSize >= MAX_PATH); // This is an assumption SHGetPathFromIDList makes.
hr = (SHGetPathFromIDList(pidlFolder, pszPath) ? S_OK : E_FAIL);
}
if (hkey)
RegCloseKey(hkey);
if (pidlDefault)
ILFree(pidlDefault);
if (pidlFolder)
ILFree(pidlFolder);
return hr;
}
/*****************************************************************************\
FUNCTION: _InvokeDownloadVerb
DESCRIPTION:
The user just selected file(s) and/or folder(s) and selected the
"download" verb. We need t
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -