util.cpp
来自「很好用的ftp源码」· C++ 代码 · 共 1,997 行 · 第 1/5 页
CPP
1,997 行
}
/*****************************************************************************\
FUNCTION: FtpChangeNotify
Convert the relative pidls into absolute pidls, then hand onwards
to SHChangeNotify. If we can't do the notification, tough.
Issuing a change notify also invalidates the name-cache, because
we know that something happened to the directory.
If we wanted to be clever, we could edit the name-cache on the
fly, but that would entail allocating a new name-cache, initializing
it with the edited directory contents, then setting it as the new
cache. (We can't edit the name-cache in place because somebody
might still be holding a reference to it.) And all this work needs
to be done under the critical section, so that nobody else tries
to do the same thing simultaneously. What's more, the only thing
that this helps is the case where the user opens two views on
the same folder from within the same process, which not a very
common scenario. Summary: It's just not worth it.
Note that this must be done at the CFtpFolder level and not at the
CFtpDir level, because CFtpDir doesn't know where we are rooted.
(We might have several instances, each rooted at different places.)
_UNDOCUMENTED_: The pidl1 and pidl2 parameters to SHChangeNotify
are not documented. It is also not mentioned (although it becomes
obvious once you realize it) that the pidls passed to SHChangeNotify
must be absolute.
\*****************************************************************************/
void FtpChangeNotify(HWND hwnd, LONG wEventId, CFtpFolder * pff, CFtpDir * pfd, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2, BOOL fTopLevel)
{
ASSERT(pfd && IsValidPIDL(pidl1));
ASSERT(!pidl2 || IsValidPIDL(pidl2));
// Update our local cache because SHChangeNotify will come back in later and
// want to create a pidl from a DisplayName and will then use that pidls
// time/date. This is done because the shell is trying to create a 'full'
// pidl.
switch (wEventId)
{
case SHCNE_CREATE:
case SHCNE_MKDIR:
TraceMsg(TF_CHANGENOTIFY, ((wEventId == SHCNE_CREATE) ? "FtpChangeNotify(SHCNE_CREATE), Name=%ls" : "FtpChangeNotify(SHCNE_MKDIR), Name=%s"), FtpPidl_GetFileDisplayName(pidl1));
EVAL(SUCCEEDED(pfd->AddItem(pidl1)));
break;
case SHCNE_RMDIR:
case SHCNE_DELETE:
TraceMsg(TF_CHANGENOTIFY, "FtpChangeNotify(SHCNE_DELETE), Name=%ls", FtpPidl_GetLastFileDisplayName(pidl1));
pfd->DeletePidl(pidl1); // This may fail if we never populated that cache.
break;
case SHCNE_RENAMEFOLDER:
{
CFtpDir * pfdSubFolder = pfd->GetSubFtpDir(NULL, pidl1, TRUE);
if (EVAL(pfdSubFolder))
{
LPITEMIDLIST pidlDest = pfd->GetSubPidl(NULL, pidl2, TRUE);
if (EVAL(pidlDest))
{
EVAL(SUCCEEDED(pfdSubFolder->ChangeFolderName(pidlDest)));
ILFree(pidlDest);
}
pfdSubFolder->Release();
}
}
// break; Fall Thru so we change the pidl also.
case SHCNE_RENAMEITEM:
case SHCNE_ATTRIBUTES:
TraceMsg(TF_CHANGENOTIFY, "FtpChangeNotify(SHCNE_RENAMEITEM), Name1=%ls, Name2=%ls", FtpPidl_GetLastFileDisplayName(pidl1), FtpPidl_GetLastFileDisplayName(pidl2));
EVAL(SUCCEEDED(pfd->ReplacePidl(pidl1, pidl2)));
break;
}
pidl1 = pfd->GetSubPidl(pff, pidl1, TRUE);
if (EVAL(pidl1))
{
if ((pidl2 == NULL) || (EVAL(pidl2 = pfd->GetSubPidl(pff, pidl2, TRUE))) != 0)
{
// LRESULT SHShellFolderView_Message(HWND hwnd, UINT uMsg, LPARAM lParam)
// Are we on something (browser only) that can't read
// IDelegateFolder pidls (our Pidls)?
if (IsLegacyChangeNotifyNeeded(wEventId)) // BUGBUG
{
// Yes, so SHChangeNotify won't work. Use a work around.
if (fTopLevel) // Only top level changes are appropriate.
LegacyChangeNotify(hwnd, wEventId, pidl1, pidl2);
}
else
SHChangeNotify(wEventId, (SHCNF_IDLIST | SHCNF_FLUSH), pidl1, pidl2);
ILFree((LPITEMIDLIST)pidl2);
}
ILFree((LPITEMIDLIST)pidl1);
}
}
/**************************************************************\
FUNCTION: EscapeString
DESCRIPTION:
\**************************************************************/
HRESULT EscapeString(LPCTSTR pszStrToEscape, LPTSTR pszEscapedStr, DWORD cchSize)
{
LPCTSTR pszCopy = NULL;
if (!pszStrToEscape)
{
Str_SetPtr((LPTSTR *) &pszCopy, pszEscapedStr); // NULL pszStrToEscape means do pszEscapedStr in place.
pszStrToEscape = pszCopy;
}
pszEscapedStr[0] = 0;
if (pszStrToEscape[0])
UrlEscape(pszStrToEscape, pszEscapedStr, &cchSize, URL_ESCAPE_SEGMENT_ONLY);
Str_SetPtr((LPTSTR *) &pszCopy, NULL); // NULL pszStrToEscape means do pszEscapedStr in place.
return S_OK;
}
/**************************************************************\
FUNCTION: UnEscapeString
DESCRIPTION:
\**************************************************************/
HRESULT UnEscapeString(LPCTSTR pszStrToUnEscape, LPTSTR pszUnEscapedStr, DWORD cchSize)
{
LPCTSTR pszCopy = NULL;
if (!pszStrToUnEscape)
{
Str_SetPtr((LPTSTR *) &pszCopy, pszUnEscapedStr); // NULL pszStrToEscape means do pszEscapedStr in place.
pszStrToUnEscape = pszCopy;
}
pszUnEscapedStr[0] = 0;
UrlUnescape((LPTSTR)pszStrToUnEscape, pszUnEscapedStr, &cchSize, URL_ESCAPE_SEGMENT_ONLY);
Str_SetPtr((LPTSTR *) &pszCopy, NULL); // NULL pszStrToEscape means do pszEscapedStr in place.
return S_OK;
}
/**************************************************************\
Since wininet errors are often very generic, this function
will generate error message of this format:
"An error occurred while attempted to do x and it could not
be completed.
Details:
<Wininet error that may be specific or generic>"
\**************************************************************/
int DisplayWininetErrorEx(HWND hwnd, BOOL fAssertOnNULLHWND, DWORD dwError, UINT idTitleStr, UINT idBaseErrorStr, UINT idDetailsStr, UINT nMsgBoxType, IProgressDialog * ppd, LPCWSTR pwzDetails)
{
TCHAR szErrMessage[MAX_PATH*3];
TCHAR szTitle[MAX_PATH];
BOOL fIsWininetError = ((dwError >= INTERNET_ERROR_BASE) && (dwError <= INTERNET_ERROR_LAST));
HMODULE hmod = (fIsWininetError ? GetModuleHandle(TEXT("WININET")) : NULL);
UINT uiType = (IDS_FTPERR_GETDIRLISTING == idBaseErrorStr) ? MB_ICONINFORMATION : MB_ICONERROR;
if (ppd)
{
// If we have a progress dialog, we want to close it down
// because we will display an error message and the progress
// dialog in the background looks really dumb.
ppd->StopProgressDialog();
}
// Default message if FormatMessage doesn't recognize hres
LoadString(HINST_THISDLL, idBaseErrorStr, szErrMessage, ARRAYSIZE(szErrMessage));
LoadString(HINST_THISDLL, idTitleStr, szTitle, ARRAYSIZE(szTitle));
// Yes we did, so display the error.
WCHAR szDetails[MAX_URL_STRING*2];
TCHAR szPromptTemplate[MAX_PATH];
TCHAR szBuffer[MAX_PATH*4];
LoadString(HINST_THISDLL, idDetailsStr, szPromptTemplate, ARRAYSIZE(szPromptTemplate));
// Can wininet give us extended error messages?
// UNIX servers cancel the connection if the disk or quote is full
// but the return a value that explains that to the user.
if ((ERROR_INTERNET_EXTENDED_ERROR == dwError) ||
(ERROR_INTERNET_CONNECTION_ABORTED == dwError))
{
if (!pwzDetails)
{
// BUGBUG/TODO: Strip the FTP Spec #s from the err msg.
// StripResponseHeaders(pszMOTD);
if (FAILED(InternetGetLastResponseInfoDisplayWrap(TRUE, &dwError, szDetails, ARRAYSIZE(szDetails))))
szDetails[0] = 0;
pwzDetails = (LPCWSTR) szDetails;
}
}
else
{
if (fIsWininetError)
FormatMessage(FORMAT_MESSAGE_FROM_HMODULE, (LPCVOID)hmod, dwError, 0, szDetails, ARRAYSIZE(szDetails), NULL);
else
FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, (LPCVOID)hmod, dwError, 0, szDetails, ARRAYSIZE(szDetails), NULL);
pwzDetails = (LPCWSTR) szDetails;
}
wnsprintf(szBuffer, ARRAYSIZE(szBuffer), szPromptTemplate, pwzDetails);
StrCatBuff(szErrMessage, szBuffer, ARRAYSIZE(szErrMessage));
return MessageBox(hwnd, szErrMessage, szTitle, (uiType | nMsgBoxType));
}
int DisplayWininetError(HWND hwnd, BOOL fAssertOnNULLHWND, DWORD dwError, UINT idTitleStr, UINT idBaseErrorStr, UINT idDetailsStr, UINT nMsgBoxType, IProgressDialog * ppd)
{
if (hwnd) // Only display if HWND exists.
return DisplayWininetErrorEx(hwnd, fAssertOnNULLHWND, dwError, idTitleStr, idBaseErrorStr, idDetailsStr, nMsgBoxType, ppd, NULL);
else
{
if (fAssertOnNULLHWND)
{
// ASSERT(hwnd);
}
TraceMsg(TF_ALWAYS, "DisplayWininetError() no HWND so no Error.");
}
return IDCANCEL;
}
#define CCH_SIZE_ERROR_MESSAGE 6*1024
HRESULT FtpSafeCreateDirectory(HWND hwnd, HINTERNET hint, CMultiLanguageCache * pmlc, CFtpFolder * pff, CFtpDir * pfd, IProgressDialog * ppd, LPCWSTR pwzFtpPath, BOOL fRoot)
{
FTP_FIND_DATA wfd;
HRESULT hr = S_OK;
WIRECHAR wFtpPath[MAX_PATH];
CWireEncoding * pwe = pfd->GetFtpSite()->GetCWireEncoding();
if (SUCCEEDED(pwe->UnicodeToWireBytes(NULL, pwzFtpPath, (pfd->IsUTF8Supported() ? WIREENC_USE_UTF8 : WIREENC_NONE), wFtpPath, ARRAYSIZE(wFtpPath))))
{
hr = FtpCreateDirectoryWrap(hint, TRUE, wFtpPath);
// PERF NOTE: It is faster to just try to create the directory and then ignore
// error return values that indicate that they failed to create because it
// already exists. The problem I worry about is that there is some FTP server
// impl somewhere that will return the same error as failed to create because
// of access violation and we don't or can't return an error value.
if (FAILED(hr)
// BUGBUG: IE #30208: Currently broken in wininet.
// I want to test the attribute flags but for some reason the FILE_ATTRIBUTE_DIRECTORY bit
// is also set for files!!!! (!@(*#!!!)
// || !(FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes)
)
{
// Maybe if failed because it already exists, which is fine by me.
// First save off the error msg in case we need it for the err dlg later.
CHAR szErrorMsg[CCH_SIZE_ERROR_MESSAGE];
WCHAR wzErrorMsg[CCH_SIZE_ERROR_MESSAGE];
DWORD cchSize = ARRAYSIZE(szErrorMsg);
InternetGetLastResponseInfoWrap(TRUE, NULL, szErrorMsg, &cchSize);
HRESULT hrOrig = hr;
pwe->WireBytesToUnicode(NULL, szErrorMsg, WIREENC_NONE, wzErrorMsg, ARRAYSIZE(wzErrorMsg));
// Does it already exist?
hr = FtpDoesFileExist(hint, TRUE, wFtpPath, &wfd, (INTERNET_NO_CALLBACK | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RESYNCHRONIZE | INTERNET_FLAG_RELOAD));
// It's okay if we failed to create the directory because a -DIRECTORY- already exists
// because we'll just use that directory. However, it a file with that name exists,
// then we need the err msg.
if ((S_OK != hr) || !(FILE_ATTRIBUTE_DIRECTORY & wfd.dwFileAttributes))
{
// No, so it was a real error, now display the error message with the original
// server response.
DisplayWininetErrorEx(hwnd, TRUE, HRESULT_CODE(hrOrig), IDS_FTPERR_TITLE_ERROR, IDS_FTPERR_DIRCOPY, IDS_FTPERR_WININET, MB_OK, ppd, wzErrorMsg);
hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
}
}
// Was it created successfully?
if (SUCCEEDED(hr))
{
// Yes, so fire the change notify.
LPITEMIDLIST pidlNewDir;
FILETIME ftUTC;
FTP_FIND_DATA wfd;
GetSystemTimeAsFileTime(&ftUTC); // UTC
FileTimeToLocalFileTime(&ftUTC, &wfd.ftCreationTime); // Need Local Time because FTP is stupid and won't work in the cross time zones case.
// For some reason, FtpFindFirstFile needs an '*' behind the name.
StrCpyNA(wfd.cFileName, wFtpPath, ARRAYSIZE(wfd.cFileName));
wfd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
wfd.ftLastWriteTime = wfd.ftCreationTime;
wfd.ftLastAccessTime = wfd.ftCreationTime;
wfd.nFileSizeLow = 0;
wfd.nFileSizeHigh = 0;
wfd.dwReserved0 = 0;
wfd.dwReserved1 = 0;
wfd.cAlternateFileName[0] = 0;
hr = FtpItemID_CreateReal(&wfd, pwzFtpPath, &pidlNewDir);
if (SUCCEEDED(hr)) // May happen on weird character set problems.
{
// Notify the folder of the new item so the Shell Folder updates.
// PERF: Note that we should give SHChangeNotify() the information (time/date)
// from the local file system which may be different than on the server.
// But I don't think it's worth the perf to hit the server for the info.
FtpChangeNotify(hwnd, SHCNE_MKDIR, pff, pfd, pidlNewDir, NULL, fRoot);
ILFree(pidlNewDir);
}
}
}
return hr;
}
HWND GetProgressHWnd(IProgressDialog * ppd, HWND hwndDefault)
{
if (ppd)
{
HWND hwndProgress = NULL;
IUnknown_GetWindow(ppd, &hwndProgress);
if (hwndProgress)
hwndDefault = hwndProgress;
}
return hwndDefault;
}
// Returns FALSE if out of memory
int SHMessageBox(HWND hwnd, LPCTSTR pszMessage, UINT uMessageID, UINT uTitleID, UINT uType)
{
int nResult = IDCANCEL;
TCHAR szMessage[MAX_PATH];
TCHAR szTitle[MAX_PATH];
if (LoadString(HINST_THISDLL, uTitleID, szTitle, ARRAYSIZE(szTitle)) &&
(pszMessage ||
(uMessageID && LoadString(HINST_THISDLL, uMessageID, szMessage, ARRAYSIZE(szMessage)))))
{
nResult = MessageBox(hwnd, pszMessage ? pszMessage : szMessage, szTitle, uType);
}
return nResult;
}
DWORD GetOSVer(void)
{
OSVERSIONINFOA osVerInfoA;
osVerInfoA.dwOSVersionInfoSize = sizeof(osVerInfoA);
if (!GetVersionExA(&osVerInfoA))
return VER_PLATFORM_WIN32_WINDOWS; // Default to this.
return osVerInfoA.dwPlatformId;
}
LPITEMIDLIST SHILCreateFromPathWrapper(LPCTSTR pszPath)
{
LPITEMIDLIST pidl;
if (VER_PLATFORM_WIN32_NT == GetOSVer())
{
WCHAR wzPath[MAX_PATH];
SHTCharToUnicode(pszPath, wzPath, ARRAYSIZE(wzPath));
SHILCreateFromPath((LPCTSTR)wzPath, &pidl, NULL);
}
else
{
CHAR szPath[MAX_PATH];
SHTCharToAnsi(pszPath, szPath, ARRAYSIZE(szPath));
SHILCreateFromPath((LPCTSTR)szPath, &pidl, NULL);
}
return pidl;
}
LPCITEMIDLIST ILGetLastID(LPCITEMIDLIST pidlIn)
{
LPITEMIDLIST pidl = (LPITEMIDLIST) pidlIn;
while (!ILIsEmpty(_ILNext(pidl)))
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?