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.
2781 lines
85 KiB
2781 lines
85 KiB
/*****************************************************************************\
|
|
FILE: util.cpp
|
|
|
|
DESCRIPTION:
|
|
Shared stuff that operates on all classes.
|
|
\*****************************************************************************/
|
|
|
|
#include "priv.h"
|
|
#include "util.h"
|
|
#include "ftpurl.h"
|
|
#include "view.h"
|
|
#include "statusbr.h"
|
|
#include <commctrl.h>
|
|
#include <shdocvw.h>
|
|
#define CPP_FUNCTIONS
|
|
#include <crtfree.h>
|
|
|
|
HINSTANCE g_hinst; /* My instance handle */
|
|
CHAR g_szShell32[MAX_PATH]; /* Full path to shell32.dll (must be ANSI) */
|
|
|
|
#ifdef DEBUG
|
|
DWORD g_TLSliStopWatchStartHi = 0;
|
|
DWORD g_TLSliStopWatchStartLo = 0;
|
|
LARGE_INTEGER g_liStopWatchFreq = {0};
|
|
#endif // DEBUG
|
|
|
|
// Shell32.dll v3 (original Win95/WinNT) has so many bugs when it receives
|
|
// an IDataObject with FILEGROUPDESCRIPTOR that it doesn't make sense to allow
|
|
// users to drag from FTP with FILEGROUPDESCRIPTOR on these early shell machines.
|
|
// This #define turns this on off.
|
|
//#define BROWSERONLY_DRAGGING 1
|
|
|
|
const VARIANT c_vaEmpty = {0};
|
|
#define PVAREMPTY ((VARIANT*)&c_vaEmpty)
|
|
|
|
//////////////////////////// IE 5 vs IE 4 /////////////////////////////////
|
|
// These are functions that IE5 exposes (normally in shlwapi), but
|
|
// if we want to be compatible with IE4, we need to have our own copy.s
|
|
// If we turn on USE_IE5_UTILS, we won't work with IE4's DLLs (like shlwapi).
|
|
//
|
|
#ifndef USE_IE5_UTILS
|
|
void UnicWrapper_IUnknown_Set(IUnknown ** ppunk, IUnknown * punk)
|
|
{
|
|
ENTERCRITICAL;
|
|
|
|
if (*ppunk)
|
|
(*ppunk)->Release();
|
|
|
|
*ppunk = punk;
|
|
if (punk)
|
|
punk->AddRef();
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
void UnicWrapper_IUnknown_AtomicRelease(void ** ppunk)
|
|
{
|
|
if (ppunk && *ppunk) {
|
|
IUnknown* punk = *(IUnknown**)ppunk;
|
|
*ppunk = NULL;
|
|
punk->Release();
|
|
}
|
|
}
|
|
|
|
|
|
DWORD UnicWrapper_SHWaitForSendMessageThread(HANDLE hThread, DWORD dwTimeout)
|
|
{
|
|
MSG msg;
|
|
DWORD dwRet;
|
|
DWORD dwEnd = GetTickCount() + dwTimeout;
|
|
|
|
// We will attempt to wait up to dwTimeout for the thread to
|
|
// terminate
|
|
do
|
|
{
|
|
dwRet = MsgWaitForMultipleObjects(1, &hThread, FALSE,
|
|
dwTimeout, QS_SENDMESSAGE);
|
|
if (dwRet == WAIT_OBJECT_0 ||
|
|
dwRet == WAIT_FAILED)
|
|
{
|
|
// The thread must have exited, so we are happy
|
|
break;
|
|
}
|
|
|
|
if (dwRet == WAIT_TIMEOUT)
|
|
{
|
|
// The thread is taking too long to finish, so just
|
|
// return and let the caller kill it
|
|
break;
|
|
}
|
|
|
|
// There must be a pending SendMessage from either the
|
|
// thread we are killing or some other thread/process besides
|
|
// this one. Do a PeekMessage to process the pending
|
|
// SendMessage and try waiting again
|
|
PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
|
|
|
|
if (dwTimeout != INFINITE)
|
|
dwTimeout = dwEnd - GetTickCount();
|
|
}
|
|
while((dwTimeout == INFINITE) || ((long)dwTimeout > 0));
|
|
|
|
return(dwRet);
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: UnicWrapper_AutoCompleteFileSysInEditbox
|
|
|
|
DESCRIPTION:
|
|
This function will have AutoComplete take over
|
|
an editbox to help autocomplete DOS paths.
|
|
\****************************************************/
|
|
HRESULT UnicWrapper_AutoCompleteFileSysInEditbox(HWND hwndEdit)
|
|
{
|
|
HRESULT hr;
|
|
IUnknown * punkACLISF;
|
|
|
|
hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&punkACLISF);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
IAutoComplete * pac;
|
|
|
|
// Create the AutoComplete Object
|
|
hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, IID_IAutoComplete, (void **)&pac);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = pac->Init(hwndEdit, punkACLISF, NULL, NULL);
|
|
pac->Release();
|
|
}
|
|
|
|
punkACLISF->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
#endif // USE_IE5_UTILS
|
|
//////////////////////////// IE 5 vs IE 4 /////////////////////////////////
|
|
|
|
|
|
void IUnknown_Set(IMalloc ** ppm, IMalloc * pm)
|
|
{
|
|
ENTERCRITICAL;
|
|
|
|
if (*ppm)
|
|
(*ppm)->Release();
|
|
|
|
*ppm = pm;
|
|
|
|
if (pm)
|
|
pm->AddRef();
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
// TODO: This is a remnent of using C++ in stead of real COM
|
|
void IUnknown_Set(CFtpFolder ** ppff, CFtpFolder * pff)
|
|
{
|
|
ENTERCRITICAL;
|
|
|
|
if (*ppff)
|
|
(*ppff)->Release();
|
|
|
|
*ppff = pff;
|
|
|
|
if (pff)
|
|
pff->AddRef();
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
void IUnknown_Set(CFtpDir ** ppfd, CFtpDir * pfd)
|
|
{
|
|
ENTERCRITICAL;
|
|
|
|
if (*ppfd)
|
|
(*ppfd)->Release();
|
|
|
|
*ppfd = pfd;
|
|
|
|
if (pfd)
|
|
pfd->AddRef();
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
void IUnknown_Set(CFtpSite ** ppfs, CFtpSite * pfs)
|
|
{
|
|
ENTERCRITICAL;
|
|
|
|
if (*ppfs)
|
|
(*ppfs)->Release();
|
|
|
|
*ppfs = pfs;
|
|
|
|
if (pfs)
|
|
pfs->AddRef();
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
void IUnknown_Set(CFtpList ** ppfl, CFtpList * pfl)
|
|
{
|
|
ENTERCRITICAL;
|
|
|
|
if (*ppfl)
|
|
(*ppfl)->Release();
|
|
|
|
*ppfl = pfl;
|
|
|
|
if (pfl)
|
|
pfl->AddRef();
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
void IUnknown_Set(CFtpPidlList ** ppflpidl, CFtpPidlList * pflpidl)
|
|
{
|
|
ENTERCRITICAL;
|
|
|
|
if (*ppflpidl)
|
|
(*ppflpidl)->Release();
|
|
|
|
*ppflpidl = pflpidl;
|
|
|
|
if (pflpidl)
|
|
pflpidl->AddRef();
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
void IUnknown_Set(CFtpEfe ** ppfefe, CFtpEfe * pfefe)
|
|
{
|
|
ENTERCRITICAL;
|
|
|
|
if (*ppfefe)
|
|
(*ppfefe)->Release();
|
|
|
|
*ppfefe = pfefe;
|
|
|
|
if (pfefe)
|
|
pfefe->AddRef();
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
void IUnknown_Set(CFtpGlob ** ppfg, CFtpGlob * pfg)
|
|
{
|
|
ENTERCRITICAL;
|
|
|
|
if (*ppfg)
|
|
(*ppfg)->Release();
|
|
|
|
*ppfg = pfg;
|
|
|
|
if (pfg)
|
|
pfg->AddRef();
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
|
|
void IUnknown_Set(CFtpMenu ** ppfcm, CFtpMenu * pfcm)
|
|
{
|
|
ENTERCRITICAL;
|
|
|
|
if (*ppfcm)
|
|
(*ppfcm)->Release();
|
|
|
|
*ppfcm = pfcm;
|
|
|
|
if (pfcm)
|
|
pfcm->AddRef();
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
|
|
void IUnknown_Set(CFtpStm ** ppfstm, CFtpStm * pfstm)
|
|
{
|
|
ENTERCRITICAL;
|
|
|
|
if (*ppfstm)
|
|
(*ppfstm)->Release();
|
|
|
|
*ppfstm = pfstm;
|
|
|
|
if (pfstm)
|
|
pfstm->AddRef();
|
|
|
|
LEAVECRITICAL;
|
|
}
|
|
|
|
|
|
#undef ILCombine
|
|
// Fix Shell32 bug
|
|
LPITEMIDLIST ILCombineWrapper(LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
if (!pidl1)
|
|
return ILClone(pidl2);
|
|
|
|
if (!pidl2)
|
|
return ILClone(pidl1);
|
|
|
|
return ILCombine(pidl1, pidl2);
|
|
}
|
|
|
|
|
|
#undef ILClone
|
|
// Fix Shell32 bug
|
|
LPITEMIDLIST ILCloneWrapper(LPCITEMIDLIST pidl)
|
|
{
|
|
if (!pidl)
|
|
return NULL;
|
|
|
|
return ILClone(pidl);
|
|
}
|
|
|
|
|
|
#undef ILFree
|
|
// Fix Shell32 bug
|
|
void ILFreeWrapper(LPITEMIDLIST pidl)
|
|
{
|
|
if (pidl)
|
|
ILFree(pidl);
|
|
}
|
|
|
|
|
|
// Don't ship with this on.
|
|
//#define DEBUG_LEGACY
|
|
|
|
BOOL IsLegacyChangeNotifyNeeded(LONG wEventId)
|
|
{
|
|
#ifdef DEBUG_LEGACY
|
|
return TRUE;
|
|
#endif // DEBUG_LEGACY
|
|
|
|
// The only version that doesn't support IDelegateFolder pidls is
|
|
// shell32 v3 (w/o IE4 Shell Intergrated)
|
|
BOOL fResult = (SHELL_VERSION_W95NT4 == GetShellVersion());
|
|
|
|
return fResult;
|
|
}
|
|
|
|
|
|
|
|
/*****************************************************************************\
|
|
FUNCTION: LegacyChangeNotify
|
|
|
|
DESCRIPTION:
|
|
Browser only can't read IDelegateFolder pidls (our Pidls), so we need
|
|
to use this function instead of SHChangeNotify that will use hacks to
|
|
get DefView's ListView to update by using
|
|
SHShellFolderView_Message(HWND hwnd, UINT uMsg, LPARAM lParam).
|
|
|
|
These are the messages to use.
|
|
SFVM_ADDOBJECT (SHCNE_CREATE & SHCNE_MKDIR),
|
|
SFVM_UPDATEOBJECT (SHCNE_RENAMEFOLDER, SHCNE_RENAMEITEM, SHCNE_ATTRIBUTES), or SFVM_REFRESHOBJECT(),
|
|
SFVM_REMOVEOBJECT (SHCNE_RMDIR & SHCNE_DELETE).
|
|
\*****************************************************************************/
|
|
HRESULT LegacyChangeNotify(HWND hwnd, LONG wEventId, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
|
|
{
|
|
if (EVAL(hwnd)) // We can't talk to the window w/o this.
|
|
{
|
|
switch(wEventId)
|
|
{
|
|
case SHCNE_CREATE:
|
|
case SHCNE_MKDIR:
|
|
{
|
|
// NOTE: If the item alread exists, it will create a new duplicate name.
|
|
// We need to skip this if it exists.
|
|
LPCITEMIDLIST pidlRelative = ILGetLastID(pidl1);
|
|
// SFVM_ADDOBJECT frees the pidl we give them.
|
|
EVAL(SHShellFolderView_Message(hwnd, SFVM_ADDOBJECT, (LPARAM) ILClone(pidlRelative)));
|
|
break;
|
|
}
|
|
case SHCNE_RMDIR:
|
|
case SHCNE_DELETE:
|
|
{
|
|
LPCITEMIDLIST pidlRelative = ILGetLastID(pidl1);
|
|
EVAL(SHShellFolderView_Message(hwnd, SFVM_REMOVEOBJECT, (LPARAM) pidlRelative));
|
|
break;
|
|
}
|
|
case SHCNE_RENAMEFOLDER:
|
|
case SHCNE_RENAMEITEM:
|
|
case SHCNE_ATTRIBUTES:
|
|
{
|
|
LPCITEMIDLIST pidlArray[2];
|
|
|
|
pidlArray[0] = ILGetLastID(pidl1);
|
|
pidlArray[1] = ILClone(ILGetLastID(pidl2));
|
|
EVAL(SHShellFolderView_Message(hwnd, SFVM_UPDATEOBJECT, (LPARAM) pidlArray));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
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))
|
|
{
|
|
// 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 && 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)
|
|
{
|
|
// We could remove the FTP cmd numbers from before the err strings except advanced users
|
|
// can use them to know more information about the state of the server when this happened.
|
|
// 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)
|
|
// NOTE: IE #30208: Currently broken in wininet. The dorks in wininet never fixed this because
|
|
// they say it's no repro. It's no repro because I did this work around!$#!@@#%!!!
|
|
//
|
|
// 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 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;
|
|
}
|
|
|
|
|
|
BOOL IsOSNT(void)
|
|
{
|
|
OSVERSIONINFOA osVerInfoA;
|
|
|
|
osVerInfoA.dwOSVersionInfoSize = sizeof(osVerInfoA);
|
|
if (!GetVersionExA(&osVerInfoA))
|
|
return VER_PLATFORM_WIN32_WINDOWS; // Default to this.
|
|
|
|
return (VER_PLATFORM_WIN32_NT == osVerInfoA.dwPlatformId);
|
|
}
|
|
|
|
|
|
DWORD GetOSVer(void)
|
|
{
|
|
OSVERSIONINFOA osVerInfoA;
|
|
|
|
osVerInfoA.dwOSVersionInfoSize = sizeof(osVerInfoA);
|
|
if (!GetVersionExA(&osVerInfoA))
|
|
return VER_PLATFORM_WIN32_WINDOWS; // Default to this.
|
|
|
|
return osVerInfoA.dwMajorVersion;
|
|
}
|
|
|
|
|
|
LPITEMIDLIST SHILCreateFromPathWrapper(LPCTSTR pszPath)
|
|
{
|
|
LPITEMIDLIST pidl;
|
|
|
|
if (IsOSNT())
|
|
{
|
|
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)))
|
|
pidl = _ILNext(pidl);
|
|
|
|
return pidl;
|
|
}
|
|
|
|
|
|
LPCITEMIDLIST ILGetLastNonFragID(LPCITEMIDLIST pidlIn)
|
|
{
|
|
LPITEMIDLIST pidl = (LPITEMIDLIST) pidlIn;
|
|
|
|
while (!ILIsEmpty(_ILNext(pidl)) && !FtpItemID_IsFragment(_ILNext(pidl)))
|
|
pidl = _ILNext(pidl);
|
|
|
|
return pidl;
|
|
}
|
|
|
|
|
|
|
|
SAFEARRAY * MakeSafeArrayFromData(LPCBYTE pData,DWORD cbData)
|
|
{
|
|
SAFEARRAY * psa;
|
|
|
|
if (!pData || 0 == cbData)
|
|
return NULL; // nothing to do
|
|
|
|
// create a one-dimensional safe array
|
|
psa = SafeArrayCreateVector(VT_UI1,0,cbData);
|
|
ASSERT(psa);
|
|
|
|
if (psa) {
|
|
// copy data into the area in safe array reserved for data
|
|
// Note we party directly on the pointer instead of using locking/
|
|
// unlocking functions. Since we just created this and no one
|
|
// else could possibly know about it or be using it, this is OK.
|
|
|
|
ASSERT(psa->pvData);
|
|
memcpy(psa->pvData,pData,cbData);
|
|
}
|
|
|
|
return psa;
|
|
}
|
|
|
|
|
|
//
|
|
// PARAMETER:
|
|
// pvar - Allocated by caller and filled in by this function.
|
|
// pidl - Allocated by caller and caller needs to free.
|
|
//
|
|
// This function will take the PIDL parameter and COPY it
|
|
// into the Variant data structure. This allows the pidl
|
|
// to be freed and the pvar to be used later, however, it
|
|
// is necessary to call VariantClear(pvar) to free memory
|
|
// that this function allocates.
|
|
|
|
BOOL InitVariantFromIDList(VARIANT* pvar, LPCITEMIDLIST pidl)
|
|
{
|
|
UINT cb = ILGetSize(pidl);
|
|
SAFEARRAY* psa = MakeSafeArrayFromData((LPCBYTE)pidl, cb);
|
|
if (psa) {
|
|
ASSERT(psa->cDims == 1);
|
|
// ASSERT(psa->cbElements == cb);
|
|
ASSERT(ILGetSize((LPCITEMIDLIST)psa->pvData)==cb);
|
|
VariantInit(pvar);
|
|
pvar->vt = VT_ARRAY|VT_UI1;
|
|
pvar->parray = psa;
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
|
|
BSTR BStrFromStr(LPCTSTR pszStr)
|
|
{
|
|
BSTR bStr = NULL;
|
|
|
|
#ifdef UNICODE
|
|
bStr = SysAllocString(pszStr);
|
|
|
|
#else // UNICODE
|
|
DWORD cchSize = (lstrlen(pszStr) + 2);
|
|
bStr = SysAllocStringLen(NULL, cchSize);
|
|
if (bStr)
|
|
SHAnsiToUnicode(pszStr, bStr, cchSize);
|
|
|
|
#endif // UNICODE
|
|
|
|
return bStr;
|
|
}
|
|
|
|
|
|
HRESULT IUnknown_IWebBrowserNavigate2(IUnknown * punk, LPCITEMIDLIST pidl, BOOL fHistoryEntry)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
IWebBrowser2 * pwb2;
|
|
|
|
// punk will be NULL on Browser Only installs because the old
|
|
// shell32 doesn't do ::SetSite().
|
|
IUnknown_QueryService(punk, SID_SWebBrowserApp, IID_IWebBrowser2, (LPVOID *) &pwb2);
|
|
if (pwb2)
|
|
{
|
|
VARIANT varThePidl;
|
|
|
|
if (InitVariantFromIDList(&varThePidl, pidl))
|
|
{
|
|
VARIANT varFlags;
|
|
VARIANT * pvarFlags = PVAREMPTY;
|
|
|
|
if (!fHistoryEntry)
|
|
{
|
|
varFlags.vt = VT_I4;
|
|
varFlags.lVal = navNoHistory;
|
|
pvarFlags = &varFlags;
|
|
}
|
|
|
|
hr = pwb2->Navigate2(&varThePidl, pvarFlags, PVAREMPTY, PVAREMPTY, PVAREMPTY);
|
|
VariantClear(&varThePidl);
|
|
}
|
|
pwb2->Release();
|
|
}
|
|
else
|
|
{
|
|
IShellBrowser * psb;
|
|
|
|
// Maybe we are in comdlg32.
|
|
hr = IUnknown_QueryService(punk, SID_SCommDlgBrowser, IID_IShellBrowser, (LPVOID *) &psb);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CFtpView * pfv = GetCFtpViewFromDefViewSite(punk);
|
|
|
|
AssertMsg((NULL != pfv), TEXT("IUnknown_IWebBrowserNavigate2() defview gave us our IShellFolderViewCB so it needs to support this interface."));
|
|
if (pfv)
|
|
{
|
|
// Are we on the forground thread?
|
|
if (pfv->IsForegroundThread())
|
|
{
|
|
// Yes, so this will be easy. This is the case
|
|
// where "Login As..." was chosen from the background context menu item.
|
|
hr = psb->BrowseObject(pidl, 0);
|
|
}
|
|
else
|
|
{
|
|
// No, so this is the case where we failed to login with the original
|
|
// UserName/Password and we will try again with the corrected Username/Password.
|
|
|
|
// Okay, we are talking to the ComDlg code but we don't want to use
|
|
// IShellBrowse::BrowseObject() because we are on a background thread. (NT #297732)
|
|
// Therefore, we want to have the IShellFolderViewCB (CFtpView) cause
|
|
// the redirect on the forground thread. Let's inform
|
|
// CFtpView now to do this.
|
|
hr = pfv->SetRedirectPidl(pidl);
|
|
}
|
|
|
|
pfv->Release();
|
|
}
|
|
|
|
AssertMsg(SUCCEEDED(hr), TEXT("IUnknown_IWebBrowserNavigate2() defview needs to support QS(SID_ShellFolderViewCB) on all platforms that hit this point"));
|
|
psb->Release();
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT IUnknown_PidlNavigate(IUnknown * punk, LPCITEMIDLIST pidl, BOOL fHistoryEntry)
|
|
{
|
|
HRESULT hrOle = SHCoInitialize();
|
|
HRESULT hr = IUnknown_IWebBrowserNavigate2(punk, pidl, fHistoryEntry);
|
|
|
|
// Try a pre-NT5 work around.
|
|
// punk will be NULL on Browser Only installs because the old
|
|
// shell32 doesn't do ::SetSite().
|
|
if (FAILED(hr))
|
|
{
|
|
IWebBrowserApp * pauto = NULL;
|
|
|
|
hr = SHGetIDispatchForFolder(pidl, &pauto);
|
|
if (pauto)
|
|
{
|
|
hr = IUnknown_IWebBrowserNavigate2(pauto, pidl, fHistoryEntry);
|
|
ASSERT(SUCCEEDED(hr));
|
|
pauto->Release();
|
|
}
|
|
}
|
|
|
|
ASSERT(SUCCEEDED(hrOle));
|
|
SHCoUninitialize(hrOle);
|
|
return hr;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
|
|
HIDACREATEINFO
|
|
|
|
Structure that collects all information needed when building
|
|
an ID List Array.
|
|
|
|
\*****************************************************************************/
|
|
|
|
typedef struct tagHIDACREATEINFO
|
|
{
|
|
HIDA hida; /* The HIDA being built */
|
|
UINT ipidl; /* Who we are */
|
|
UINT ib; /* Where we are */
|
|
UINT cb; /* Where we're going */
|
|
UINT cpidl; /* How many we're doing */
|
|
LPCITEMIDLIST pidlFolder; /* The parent all these LPITEMIDLISTs live in */
|
|
CFtpPidlList * pflHfpl; /* The pidl list holding all the kids */
|
|
} HIDACREATEINFO, * LPHIDACREATEINFO;
|
|
|
|
#define pidaPhci(phci) ((LPIDA)(phci)->hida) /* no need to lock */
|
|
|
|
|
|
/*****************************************************************************\
|
|
Misc_SfgaoFromFileAttributes
|
|
|
|
AIGH!
|
|
|
|
UNIX and Win32 semantics on file permissions are different.
|
|
|
|
On UNIX, the ability to rename or delete a file depends on
|
|
your permissions on the parent folder.
|
|
|
|
On Win32, the ability to rename or delete a file depends on
|
|
your permissions on the file itself.
|
|
|
|
Note that there is no such thing as "deny-read" attributes
|
|
on Win32... I wonder how WinINet handles that...
|
|
|
|
I'm going to hope that WinINet does the proper handling of this,
|
|
so I'll just proceed with Win32 semantics... I'm probably assuming too much...
|
|
\*****************************************************************************/
|
|
DWORD Misc_SfgaoFromFileAttributes(DWORD dwFAFLFlags)
|
|
{
|
|
DWORD sfgao = SFGAO_CANLINK; // You can always link
|
|
|
|
sfgao |= SFGAO_HASPROPSHEET; // You can always view properties
|
|
|
|
sfgao |= SFGAO_CANCOPY; // Deny-read? No such thing! (Yet)
|
|
|
|
if (dwFAFLFlags & FILE_ATTRIBUTE_READONLY)
|
|
{ /* Can't delete it, sorry */
|
|
#ifdef _SOMEDAY_ASK_FRANCISH_WHAT_THIS_IS
|
|
if (SHELL_VERSION_NT5 == GetShellVersion())
|
|
sfgao |= SFGAO_READONLY;
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
sfgao |= (SFGAO_CANRENAME | SFGAO_CANDELETE);
|
|
#ifdef FEATURE_CUT_MOVE
|
|
sfgao |= SFGAO_CANMOVE;
|
|
#endif // FEATURE_CUT_MOVE
|
|
}
|
|
|
|
if (dwFAFLFlags & FILE_ATTRIBUTE_DIRECTORY)
|
|
{
|
|
//Since FTP connections are expensive, assume SFGAO_HASSUBFOLDER
|
|
sfgao |= SFGAO_DROPTARGET | SFGAO_FOLDER | SFGAO_HASSUBFOLDER | SFGAO_STORAGEANCESTOR;
|
|
}
|
|
else
|
|
{
|
|
// We always return the
|
|
// SFGAO_BROWSABLE because we always want to do the navigation
|
|
// using our IShellFolder::CreateViewObject(). In the case of
|
|
// files, the CreateViewObject() that we create is for URLMON
|
|
// which will do the download. This is especially true for
|
|
// Folder Shortcuts.
|
|
sfgao |= SFGAO_BROWSABLE | SFGAO_STREAM;
|
|
}
|
|
|
|
return sfgao;
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
FUNCTION: Misc_StringFromFileTime
|
|
|
|
DESCRIPTION:
|
|
Get the date followed by the time. flType can be DATE_SHORTDATE
|
|
(for defview's details list) or DATE_LONGDATE for the property sheet.
|
|
|
|
PARAMETERS:
|
|
pft: This needs to be stored in UTC (NOT Time Zone dependent form!!!!)
|
|
\*****************************************************************************/
|
|
HRESULT Misc_StringFromFileTime(LPTSTR pszDateTime, DWORD cchSize, LPFILETIME pftUTC, DWORD flType)
|
|
{
|
|
if (EVAL(pftUTC && pftUTC->dwHighDateTime))
|
|
{
|
|
// SHFormatDateTime() takes the date in UTC format.
|
|
SHFormatDateTime(pftUTC, &flType, pszDateTime, cchSize);
|
|
}
|
|
else
|
|
pszDateTime[0] = 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
LPITEMIDLIST GetPidlFromFtpFolderAndPidlList(CFtpFolder * pff, CFtpPidlList * pflHfpl)
|
|
{
|
|
LPCITEMIDLIST pidlBase = pff->GetPrivatePidlReference();
|
|
LPCITEMIDLIST pidlRelative = ((0 == pflHfpl->GetCount()) ? c_pidlNil : pflHfpl->GetPidl(0));
|
|
|
|
return ILCombine(pidlBase, pidlRelative);
|
|
}
|
|
|
|
|
|
IProgressDialog * CProgressDialog_CreateInstance(UINT idTitle, UINT idAnimation)
|
|
{
|
|
IProgressDialog * ppd = NULL;
|
|
|
|
if (SUCCEEDED(CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_IProgressDialog, (void **)&ppd)))
|
|
{
|
|
WCHAR wzTitle[MAX_PATH];
|
|
|
|
if (EVAL(LoadStringW(HINST_THISDLL, idTitle, wzTitle, ARRAYSIZE(wzTitle))))
|
|
EVAL(SUCCEEDED(ppd->SetTitle(wzTitle)));
|
|
|
|
EVAL(SUCCEEDED(ppd->SetAnimation(HINST_THISDLL, idAnimation)));
|
|
}
|
|
|
|
return ppd;
|
|
}
|
|
|
|
|
|
BOOL_PTR CALLBACK ProxyDlgWarningWndProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
|
|
{
|
|
if (uMsg == WM_INITDIALOG)
|
|
{
|
|
LPCTSTR pszUrl = (LPCTSTR)lParam;
|
|
TCHAR szMessage[MAX_PATH*3];
|
|
TCHAR szTemplate[MAX_PATH*3];
|
|
|
|
ASSERT(pszUrl);
|
|
|
|
EVAL(LoadString(HINST_THISDLL, IDS_FTP_PROXY_WARNING, szTemplate, ARRAYSIZE(szTemplate)));
|
|
wnsprintf(szMessage, ARRAYSIZE(szMessage), szTemplate, pszUrl);
|
|
EVAL(SetWindowText(GetDlgItem(hDlg, IDC_PROXY_MESSAGE), szMessage));
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
FUNCTION: DisplayBlockingProxyDialog
|
|
|
|
DESCRIPTION:
|
|
Inform user that their CERN style proxy is blocking real FTP access so
|
|
they can do something about it.
|
|
|
|
Inform the user so they can:
|
|
A) Change proxies,
|
|
B) Annoy their administrator to install real proxies,
|
|
C) Install Remote WinSock themselves,
|
|
D) or settle for their sorry situation in life and use the
|
|
limited CERN proxy support and dream about the abilitity
|
|
to rename, delete, and upload.
|
|
|
|
This will be a no-op if the user clicks "Don't display this
|
|
message again" check box.
|
|
\*****************************************************************************/
|
|
HRESULT DisplayBlockingProxyDialog(LPCITEMIDLIST pidl, HWND hwnd)
|
|
{
|
|
// Did the IBindCtx provide information to allow us to do UI?
|
|
if (hwnd)
|
|
{
|
|
TCHAR szUrl[MAX_PATH];
|
|
HWND hwndParent = hwnd;
|
|
|
|
// NT #321103: If the user used Start->Run to open the dialog, then
|
|
// it may not receive focus because our parent browser has not yet
|
|
// appeared (it doesn't have the WS_VISIBLE style yet and it has the
|
|
// WS_DISABLED style. So we need to force our dialog to become active.
|
|
if (hwndParent && !IsWindowVisible(hwndParent))
|
|
{
|
|
hwndParent = NULL;
|
|
}
|
|
|
|
UrlCreateFromPidl(pidl, SHGDN_FORPARSING, szUrl, ARRAYSIZE(szUrl), 0, TRUE);
|
|
|
|
// Make it modal while the dialog is being displayed.
|
|
// IUnknown_EnableModless(punkSite, FALSE);
|
|
SHMessageBoxCheckEx(hwndParent, HINST_THISDLL, MAKEINTRESOURCE(IDD_PROXYDIALOG), ProxyDlgWarningWndProc, (LPVOID) szUrl, IDOK, SZ_REGVALUE_WARN_ABOUT_PROXY);
|
|
// IUnknown_EnableModless(punkSite, TRUE);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CreateFromToStr(LPWSTR pwzStrOut, DWORD cchSize, ...)
|
|
{
|
|
CHAR szStatusText[MAX_PATH];
|
|
CHAR szTemplate[MAX_PATH];
|
|
va_list vaParamList;
|
|
|
|
va_start(vaParamList, cchSize);
|
|
// Generate the string "From <SrcFtpUrlDir> to <DestFileDir>" status string
|
|
EVAL(LoadStringA(HINST_THISDLL, IDS_DL_SRC_DEST, szTemplate, ARRAYSIZE(szTemplate)));
|
|
if (EVAL(FormatMessageA(FORMAT_MESSAGE_FROM_STRING, szTemplate, 0, 0, szStatusText, ARRAYSIZE(szStatusText), &vaParamList)))
|
|
SHAnsiToUnicode(szStatusText, pwzStrOut, cchSize);
|
|
|
|
va_end(vaParamList);
|
|
return S_OK;
|
|
}
|
|
|
|
/****************************************************\
|
|
FUNCTION: FtpProgressInternetStatusCB
|
|
|
|
DESCRIPTION:
|
|
This function is exists to be called back during
|
|
long FTP operations so we can update the progress
|
|
dialog during FtpPutFile or FtpGetFile.
|
|
|
|
A pointer to our PROGRESSINFO struct is passed in
|
|
dwContext.
|
|
\****************************************************/
|
|
void FtpProgressInternetStatusCB(IN HINTERNET hInternet, IN DWORD_PTR pdwContext, IN DWORD dwInternetStatus, IN LPVOID lpwStatusInfo, IN DWORD dwStatusInfoLen)
|
|
{
|
|
LPPROGRESSINFO pProgInfo = (LPPROGRESSINFO) pdwContext;
|
|
if (EVAL(pProgInfo))
|
|
{
|
|
switch (dwInternetStatus)
|
|
{
|
|
case INTERNET_STATUS_RESPONSE_RECEIVED:
|
|
case INTERNET_STATUS_REQUEST_SENT:
|
|
if (EVAL(lpwStatusInfo && (sizeof(DWORD) == dwStatusInfoLen)
|
|
&& pProgInfo))
|
|
{
|
|
if (pProgInfo->hint && pProgInfo->ppd->HasUserCancelled())
|
|
{
|
|
EVAL(InternetCloseHandle(pProgInfo->hint));
|
|
pProgInfo->hint = NULL;
|
|
}
|
|
|
|
pProgInfo->dwCompletedInCurFile += *(LPDWORD)lpwStatusInfo;
|
|
|
|
// Has a big enough chunck of the file completed that we need
|
|
// to update the progress? We only want to update the progress
|
|
// every SIZE_PROGRESS_AFTERBYTES (50k) chunck.
|
|
if (pProgInfo->dwLastDisplayed < (pProgInfo->dwCompletedInCurFile / SIZE_PROGRESS_AFTERBYTES))
|
|
{
|
|
ULARGE_INTEGER uliBytesCompleted;
|
|
|
|
pProgInfo->dwLastDisplayed = (pProgInfo->dwCompletedInCurFile / SIZE_PROGRESS_AFTERBYTES);
|
|
|
|
uliBytesCompleted.HighPart = 0;
|
|
uliBytesCompleted.LowPart = pProgInfo->dwCompletedInCurFile;
|
|
uliBytesCompleted.QuadPart += pProgInfo->uliBytesCompleted.QuadPart;
|
|
|
|
if (pProgInfo->ppd)
|
|
EVAL(SUCCEEDED(pProgInfo->ppd->SetProgress64(uliBytesCompleted.QuadPart, pProgInfo->uliBytesTotal.QuadPart)));
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
Misc_CreateHglob
|
|
|
|
Allocate an hglobal of the indicated size, initialized from the
|
|
specified buffer.
|
|
\*****************************************************************************/
|
|
HRESULT Misc_CreateHglob(SIZE_T cb, LPVOID pv, HGLOBAL *phglob)
|
|
{
|
|
HRESULT hres = E_OUTOFMEMORY;
|
|
|
|
*phglob = 0; // Rules are rules
|
|
if (cb)
|
|
{
|
|
*phglob = (HGLOBAL) LocalAlloc(LPTR, cb);
|
|
if (*phglob)
|
|
{
|
|
hres = S_OK;
|
|
CopyMemory(*phglob, pv, cb);
|
|
}
|
|
}
|
|
else
|
|
hres = E_INVALIDARG; // Can't clone a discardable block
|
|
|
|
return hres;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
_HIDA_Create_Tally
|
|
|
|
Worker function for HIDA_Create which tallies up the total size.
|
|
\*****************************************************************************/
|
|
int _HIDA_Create_Tally(LPVOID pvPidl, LPVOID pv)
|
|
{
|
|
LPCITEMIDLIST pidl = (LPCITEMIDLIST) pvPidl;
|
|
UINT *pcb = (UINT *) pv;
|
|
int nContinue = (pv ? TRUE : FALSE);
|
|
|
|
if (pcb)
|
|
{
|
|
*pcb += ILGetSize(pidl);
|
|
}
|
|
|
|
return nContinue;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
_HIDA_Create_AddIdl
|
|
|
|
Worker function for HIDA_Create which appends another ID List
|
|
to the growing HIDA.
|
|
\*****************************************************************************/
|
|
int _HIDA_Create_AddIdl(LPVOID pvPidl, LPVOID pv)
|
|
{
|
|
LPCITEMIDLIST pidl = (LPCITEMIDLIST) pvPidl;
|
|
LPHIDACREATEINFO phci = (LPHIDACREATEINFO) pv;
|
|
UINT cb = ILGetSize(pidl);
|
|
|
|
pidaPhci(phci)->aoffset[phci->ipidl++] = phci->ib;
|
|
CopyMemory(pvByteIndexCb(pidaPhci(phci), phci->ib), pidl, cb);
|
|
phci->ib += cb;
|
|
|
|
return phci ? TRUE : FALSE;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
_Misc_HIDA_Init
|
|
|
|
Once we've allocated the memory for a HIDA, fill it with stuff.
|
|
\*****************************************************************************/
|
|
BOOL _Misc_HIDA_Init(LPVOID hida, LPVOID pv, LPCVOID pvParam2, BOOL fUnicode)
|
|
{
|
|
LPHIDACREATEINFO phci = (LPHIDACREATEINFO) pv;
|
|
|
|
phci->hida = hida;
|
|
pidaPhci(phci)->cidl = phci->cpidl;
|
|
phci->ipidl = 0;
|
|
|
|
phci->pflHfpl->TraceDump(_ILNext(phci->pidlFolder), TEXT("_Misc_HIDA_Init() TraceDump Before"));
|
|
|
|
_HIDA_Create_AddIdl((LPVOID) phci->pidlFolder, (LPVOID) phci);
|
|
phci->pflHfpl->Enum(_HIDA_Create_AddIdl, (LPVOID) phci);
|
|
|
|
phci->pflHfpl->TraceDump(_ILNext(phci->pidlFolder), TEXT("_Misc_HIDA_Init() TraceDump After"));
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
HIDA_Create
|
|
|
|
Swiped from idlist.c in the shell because they didn't ;Internal
|
|
export it. ;Internal
|
|
\*****************************************************************************/
|
|
HIDA Misc_HIDA_Create(LPCITEMIDLIST pidlFolder, CFtpPidlList * pflHfpl)
|
|
{
|
|
HIDACREATEINFO hci;
|
|
LPHIDACREATEINFO phci = &hci;
|
|
HIDA hida;
|
|
|
|
pflHfpl->TraceDump(_ILNext(pidlFolder), TEXT("Misc_HIDA_Create() TraceDump Before"));
|
|
phci->pidlFolder = pidlFolder;
|
|
phci->pflHfpl = pflHfpl;
|
|
phci->cpidl = pflHfpl->GetCount();
|
|
phci->ib = sizeof(CIDA) + sizeof(UINT) * phci->cpidl;
|
|
phci->cb = phci->ib + ILGetSize(pidlFolder);
|
|
|
|
pflHfpl->Enum(_HIDA_Create_Tally, (LPVOID) &phci->cb);
|
|
|
|
hida = AllocHGlob(phci->cb, _Misc_HIDA_Init, phci, NULL, FALSE);
|
|
pflHfpl->TraceDump(_ILNext(pidlFolder), TEXT("Misc_HIDA_Create() TraceDump Before"));
|
|
|
|
return hida;
|
|
}
|
|
|
|
|
|
typedef struct tagURL_FILEGROUP
|
|
{
|
|
LPFILEGROUPDESCRIPTORA pfgdA;
|
|
LPFILEGROUPDESCRIPTORW pfgdW;
|
|
LPCITEMIDLIST pidlParent;
|
|
} URL_FILEGROUP;
|
|
|
|
/*****************************************************************************\
|
|
Misc_HFGD_Create
|
|
|
|
Build a file group descriptor based on an pflHfpl.
|
|
|
|
CFtpObj::_DelayRender_FGD() did the recursive walk to expand the list
|
|
of pidls, so we don't have to.
|
|
\*****************************************************************************/
|
|
#define cbFgdCfdW(cfd) FIELD_OFFSET(FILEGROUPDESCRIPTORW, fgd[cfd])
|
|
#define cbFgdCfdA(cfd) FIELD_OFFSET(FILEGROUPDESCRIPTORA, fgd[cfd])
|
|
|
|
int _Misc_HFGD_Create(LPVOID pvPidl, LPVOID pv)
|
|
{
|
|
BOOL fSucceeded = TRUE;
|
|
URL_FILEGROUP * pUrlFileGroup = (URL_FILEGROUP *) pv;
|
|
LPCITEMIDLIST pidlFull = (LPCITEMIDLIST) pvPidl;
|
|
LPCITEMIDLIST pidl;
|
|
|
|
LPFILEGROUPDESCRIPTORA pfgdA = pUrlFileGroup->pfgdA;
|
|
LPFILEGROUPDESCRIPTORW pfgdW = pUrlFileGroup->pfgdW;
|
|
LPFILEDESCRIPTORA pfdA = (pfgdA ? &pfgdA->fgd[pfgdA->cItems++] : NULL);
|
|
LPFILEDESCRIPTORW pfdW = (pfgdW ? &pfgdW->fgd[pfgdW->cItems++] : NULL);
|
|
|
|
pidl = ILGetLastID(pidlFull);
|
|
if (pfdA)
|
|
{
|
|
#if !DEBUG_LEGACY_PROGRESS
|
|
pfdA->dwFlags = (FD_ATTRIBUTES | FD_FILESIZE | FD_CREATETIME | FD_ACCESSTIME | FD_WRITESTIME | FD_PROGRESSUI);
|
|
#else // !DEBUG_LEGACY_PROGRESS
|
|
pfdA->dwFlags = (FD_ATTRIBUTES | FD_FILESIZE | FD_CREATETIME | FD_ACCESSTIME | FD_WRITESTIME);
|
|
#endif // !DEBUG_LEGACY_PROGRESS
|
|
pfdA->dwFileAttributes = FtpItemID_GetAttributes(pidl);
|
|
pfdA->nFileSizeLow = FtpItemID_GetFileSizeLo(pidl);
|
|
pfdA->nFileSizeHigh = FtpItemID_GetFileSizeHi(pidl);
|
|
|
|
// all WIN32_FIND_DATA want to be stored in TimeZone independent
|
|
// ways, except for WININET's FTP. Also note that we only store Modified
|
|
// time and use if for everything because of another UNIX/Wininet issue.
|
|
// See priv.h on more FTP Time/Date issues.
|
|
pfdA->ftCreationTime = FtpPidl_GetFileTime(ILFindLastID(pidl));
|
|
pfdA->ftLastWriteTime = pfdA->ftCreationTime;
|
|
pfdA->ftLastAccessTime = pfdA->ftCreationTime;
|
|
}
|
|
else
|
|
{
|
|
#if !DEBUG_LEGACY_PROGRESS
|
|
pfdW->dwFlags = (FD_ATTRIBUTES | FD_FILESIZE | FD_CREATETIME | FD_ACCESSTIME | FD_WRITESTIME | FD_PROGRESSUI);
|
|
#else // !DEBUG_LEGACY_PROGRESS
|
|
pfdW->dwFlags = (FD_ATTRIBUTES | FD_FILESIZE | FD_CREATETIME | FD_ACCESSTIME | FD_WRITESTIME);
|
|
#endif // !DEBUG_LEGACY_PROGRESS
|
|
pfdW->dwFileAttributes = FtpItemID_GetAttributes(pidl);
|
|
pfdW->nFileSizeLow = FtpItemID_GetFileSizeLo(pidl);
|
|
pfdW->nFileSizeHigh = FtpItemID_GetFileSizeHi(pidl);
|
|
|
|
// all WIN32_FIND_DATA want to be stored in TimeZone independent
|
|
// ways, except for WININET's FTP. Also note that we only store Modified
|
|
// time and use if for everything because of another UNIX/Wininet issue.
|
|
// See priv.h on more FTP Time/Date issues.
|
|
pfdW->ftCreationTime = FtpPidl_GetFileTime(ILFindLastID(pidl));
|
|
pfdW->ftLastWriteTime = pfdW->ftCreationTime;
|
|
pfdW->ftLastAccessTime = pfdW->ftCreationTime;
|
|
}
|
|
|
|
LPCITEMIDLIST pidlDiff = FtpItemID_FindDifference(pUrlFileGroup->pidlParent, pidlFull);
|
|
|
|
if (pfdA)
|
|
{
|
|
GetWirePathFromPidl(pidlDiff, pfdA->cFileName, ARRAYSIZE(pfdA->cFileName), FALSE);
|
|
UrlPathRemoveSlashA(pfdA->cFileName);
|
|
UrlPathRemoveFrontSlashA(pfdA->cFileName);
|
|
UrlPathToFilePathA(pfdA->cFileName);
|
|
}
|
|
else
|
|
{
|
|
GetDisplayPathFromPidl(pidlDiff, pfdW->cFileName, ARRAYSIZE(pfdW->cFileName), FALSE);
|
|
UrlPathRemoveSlashW(pfdW->cFileName);
|
|
UrlPathRemoveFrontSlashW(pfdW->cFileName);
|
|
UrlPathToFilePathW(pfdW->cFileName);
|
|
}
|
|
|
|
TraceMsg(TF_FTPURL_UTILS, "_Misc_HFGD_Create() pfd(A/W)->dwFileAttributes=%#08lX", (pfdW ? pfdW->dwFileAttributes : pfdA->dwFileAttributes));
|
|
|
|
return fSucceeded;
|
|
}
|
|
|
|
|
|
BOOL _Misc_HFGD_Init(LPVOID pv, LPVOID pvHFPL, LPCVOID pvParam2, BOOL fUnicode)
|
|
{
|
|
CFtpPidlList * pflHfpl = (CFtpPidlList *) pvHFPL;
|
|
URL_FILEGROUP urlFG = {0};
|
|
|
|
urlFG.pidlParent = (LPCITEMIDLIST) pvParam2;
|
|
if (fUnicode)
|
|
urlFG.pfgdW = (LPFILEGROUPDESCRIPTORW) pv;
|
|
else
|
|
urlFG.pfgdA = (LPFILEGROUPDESCRIPTORA) pv;
|
|
|
|
TraceMsg(TF_PIDLLIST_DUMP, "_Misc_HFGD_Init() TraceDump Before");
|
|
pflHfpl->TraceDump(NULL, TEXT("_Misc_HFGD_Init() TraceDump before"));
|
|
|
|
pflHfpl->Enum(_Misc_HFGD_Create, (LPVOID) &urlFG);
|
|
|
|
pflHfpl->TraceDump(NULL, TEXT("_Misc_HFGD_Init() TraceDump after"));
|
|
|
|
return 1;
|
|
}
|
|
|
|
|
|
HGLOBAL Misc_HFGD_Create(CFtpPidlList * pflHfpl, LPCITEMIDLIST pidlItem, BOOL fUnicode)
|
|
{
|
|
DWORD dwCount = pflHfpl->GetCount();
|
|
DWORD cbAllocSize = (fUnicode ? cbFgdCfdW(dwCount) : cbFgdCfdA(dwCount));
|
|
|
|
return AllocHGlob(cbAllocSize, _Misc_HFGD_Init, pflHfpl, (LPCVOID) pidlItem, fUnicode);
|
|
}
|
|
|
|
|
|
// Returns the submenu of the given menu and ID. Returns NULL if there
|
|
// is no submenu
|
|
int _MergePopupMenus(HMENU hmDest, HMENU hmSource, int idCmdFirst, int idCmdLast)
|
|
{
|
|
int i, idFinal = idCmdFirst;
|
|
|
|
for (i = GetMenuItemCount(hmSource) - 1; i >= 0; --i)
|
|
{
|
|
MENUITEMINFO mii;
|
|
|
|
mii.cbSize = SIZEOF(mii);
|
|
mii.fMask = MIIM_ID|MIIM_SUBMENU;
|
|
mii.cch = 0; // just in case
|
|
|
|
if (EVAL(GetMenuItemInfo(hmSource, i, TRUE, &mii)))
|
|
{
|
|
HMENU hmDestSub = GetMenuFromID(hmDest, mii.wID);
|
|
if (hmDestSub)
|
|
{
|
|
int idTemp = Shell_MergeMenus(hmDestSub, mii.hSubMenu, (UINT)0, idCmdFirst, idCmdLast, MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
|
|
|
|
if (idFinal < idTemp)
|
|
idFinal = idTemp;
|
|
}
|
|
}
|
|
}
|
|
|
|
return idFinal;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
FUNCTION: AddToPopupMenu
|
|
|
|
DESCRIPTION:
|
|
Swiped from utils.c in RNAUI, in turn swiped from the ;Internal
|
|
shell. ;Internal
|
|
;Internal
|
|
Takes a destination menu and a (menu id, submenu index) pair,
|
|
and inserts the items from the (menu id, submenu index) at location
|
|
imi in the destination menu, with a separator, returning the number
|
|
of items added. (imi = index to menu item)
|
|
|
|
Returns the first the number of items added.
|
|
|
|
hmenuDst - destination menu
|
|
idMenuToAdd - menu resource identifier
|
|
idSubMenuIndex - submenu from menu resource to act as template
|
|
indexMenu - location at which menu items should be inserted
|
|
idCmdFirst - first available menu identifier
|
|
idCmdLast - first unavailable menu identifier
|
|
uFlags - flags for Shell_MergeMenus
|
|
\*****************************************************************************/
|
|
#define FLAGS_MENUMERGE (MM_SUBMENUSHAVEIDS | MM_DONTREMOVESEPS)
|
|
|
|
UINT AddToPopupMenu(HMENU hmenuDst, UINT idMenuToAdd, UINT idSubMenuIndex, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
|
|
{
|
|
UINT nLastItem = 0;
|
|
|
|
HMENU hmenuSrc = LoadMenu(g_hinst, MAKEINTRESOURCE(idMenuToAdd));
|
|
if (hmenuSrc)
|
|
{
|
|
nLastItem = Shell_MergeMenus(hmenuDst, GetSubMenu(hmenuSrc, idSubMenuIndex), indexMenu, idCmdFirst, idCmdLast, (uFlags | FLAGS_MENUMERGE));
|
|
DestroyMenu(hmenuSrc);
|
|
}
|
|
|
|
return nLastItem;
|
|
}
|
|
|
|
|
|
UINT MergeInToPopupMenu(HMENU hmenuDst, UINT idMenuToMerge, UINT indexMenu, UINT idCmdFirst, UINT idCmdLast, UINT uFlags)
|
|
{
|
|
UINT nLastItem = 0;
|
|
|
|
HMENU hmenuSrc = LoadMenu(g_hinst, MAKEINTRESOURCE(idMenuToMerge));
|
|
if (hmenuSrc)
|
|
{
|
|
nLastItem = _MergePopupMenus(hmenuDst, hmenuSrc, idCmdFirst, idCmdLast);
|
|
DestroyMenu(hmenuSrc);
|
|
}
|
|
|
|
return nLastItem;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
|
|
GetMenuFromID
|
|
|
|
Swiped from defviewx.c in the shell. ;Internal
|
|
;Internal
|
|
Given an actual menu and a menu identifier which corresponds
|
|
to a submenu, return the submenu handle.
|
|
|
|
hmenu - source menu
|
|
idm - menu identifier
|
|
|
|
\*****************************************************************************/
|
|
HMENU GetMenuFromID(HMENU hmenu, UINT idm)
|
|
{
|
|
HMENU hmenuRet = NULL;
|
|
if (!hmenu)
|
|
return NULL;
|
|
|
|
MENUITEMINFO mii;
|
|
mii.cbSize = sizeof(mii);
|
|
mii.fMask = MIIM_SUBMENU;
|
|
mii.cch = 0; // just in case
|
|
mii.hSubMenu = 0; // in case GetMenuItemInfo fails
|
|
|
|
if (GetMenuItemInfo(hmenu, idm, 0, &mii))
|
|
hmenuRet = mii.hSubMenu;
|
|
|
|
return hmenuRet;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
MergeMenuHierarchy
|
|
|
|
Swiped from defcm.c in the shell. ;Internal
|
|
;Internal
|
|
Given an actual menu (hmenuDst), iterate over its submenus
|
|
and merge corresponding submenus whose IDs match the IDs of
|
|
actuals.
|
|
|
|
hmenuDst - menu being adjusted
|
|
hmenuSrc - template menu
|
|
idcMin - first available index
|
|
idcMax - first unavailable index
|
|
\*****************************************************************************/
|
|
UINT MergeMenuHierarchy(HMENU hmenuDst, HMENU hmenuSrc, UINT idcMin, UINT idcMax)
|
|
{
|
|
int imi;
|
|
UINT idcMaxUsed = idcMin;
|
|
|
|
imi = GetMenuItemCount(hmenuSrc);
|
|
while (--imi >= 0)
|
|
{
|
|
UINT idcT;
|
|
MENUITEMINFO mii;
|
|
|
|
mii.cbSize = sizeof(mii);
|
|
mii.fMask = MIIM_ID|MIIM_SUBMENU;
|
|
mii.cch = 0; /* just in case */
|
|
|
|
if (GetMenuItemInfo(hmenuSrc, imi, 1, &mii))
|
|
{
|
|
idcT = Shell_MergeMenus(GetMenuFromID(hmenuDst, mii.wID),
|
|
mii.hSubMenu, (UINT)0, idcMin, idcMax,
|
|
MM_ADDSEPARATOR | MM_SUBMENUSHAVEIDS);
|
|
idcMaxUsed = max(idcMaxUsed, idcT);
|
|
}
|
|
}
|
|
|
|
return idcMaxUsed;
|
|
}
|
|
|
|
|
|
HRESULT _SetStatusBarZone(CStatusBar * psb, CFtpSite * pfs)
|
|
{
|
|
if (EVAL(psb && pfs))
|
|
{
|
|
LPITEMIDLIST pidl = pfs->GetPidl();
|
|
|
|
if (pidl)
|
|
{
|
|
TCHAR szUrl[MAX_URL_STRING];
|
|
|
|
UrlCreateFromPidl(pidl, SHGDN_FORPARSING, szUrl, ARRAYSIZE(szUrl), 0, TRUE);
|
|
psb->UpdateZonesPane(szUrl);
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
|
|
Misc_CopyPidl
|
|
|
|
I wrote this on my own, and discovered months later ;Internal
|
|
that this is the same as SHILClone... ;Internal
|
|
;Internal
|
|
\*****************************************************************************/
|
|
HRESULT Misc_CopyPidl(LPCITEMIDLIST pidl, LPITEMIDLIST * ppidlOut)
|
|
{
|
|
*ppidlOut = ILClone(pidl);
|
|
return *ppidlOut ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
/*****************************************************************************\
|
|
|
|
Misc_CloneHglobal
|
|
|
|
\*****************************************************************************/
|
|
HRESULT Misc_CloneHglobal(HGLOBAL hglob, HGLOBAL *phglob)
|
|
{
|
|
LPVOID pv;
|
|
HRESULT hres;
|
|
|
|
ASSERT(hglob);
|
|
*phglob = 0; /* Rules are rules */
|
|
pv = GlobalLock(hglob);
|
|
if (EVAL(pv))
|
|
{
|
|
hres = Misc_CreateHglob(GlobalSize(hglob), pv, phglob);
|
|
GlobalUnlock(hglob);
|
|
}
|
|
else
|
|
{ /* Not a valid global handle */
|
|
hres = E_INVALIDARG;
|
|
}
|
|
return hres;
|
|
}
|
|
|
|
|
|
#define FTP_PROPPAGES_FROM_INETCPL (INET_PAGE_SECURITY | INET_PAGE_CONTENT | INET_PAGE_CONNECTION)
|
|
|
|
HRESULT AddFTPPropertyPages(LPFNADDPROPSHEETPAGE pfnAddPropSheetPage, LPARAM lParam, HINSTANCE * phinstInetCpl, IUnknown * punkSite)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (NULL == *phinstInetCpl)
|
|
*phinstInetCpl = LoadLibrary(TEXT("inetcpl.cpl"));
|
|
|
|
// First add the pages from the Internet Control Panel.
|
|
if (*phinstInetCpl)
|
|
{
|
|
PFNADDINTERNETPROPERTYSHEETSEX pfnAddSheet = (PFNADDINTERNETPROPERTYSHEETSEX)GetProcAddress(*phinstInetCpl, STR_ADDINTERNETPROPSHEETSEX);
|
|
if (EVAL(pfnAddSheet))
|
|
{
|
|
IEPROPPAGEINFO iepi = {0};
|
|
|
|
iepi.cbSize = sizeof(iepi);
|
|
iepi.dwFlags = (DWORD)-1; // all pages
|
|
|
|
hr = pfnAddSheet(pfnAddPropSheetPage, lParam, 0, 0, &iepi);
|
|
}
|
|
// Don't FreeLibrary here, otherwise PropertyPage will GP-fault!
|
|
}
|
|
|
|
ASSERT(SUCCEEDED(hr));
|
|
|
|
if (((LPPROPSHEETHEADER)lParam)->nPages > 0)
|
|
return hr;
|
|
else
|
|
return S_FALSE;
|
|
|
|
}
|
|
|
|
|
|
#if 0
|
|
/*****************************************************************************\
|
|
|
|
Misc_SetDataDword
|
|
|
|
\*****************************************************************************/
|
|
HRESULT Misc_SetDataDword(IDataObject *pdto, FORMATETC *pfe, DWORD dw)
|
|
{
|
|
HRESULT hres;
|
|
HGLOBAL hglob;
|
|
|
|
hres = Misc_CreateHglob(sizeof(dw), &dw, &hglob);
|
|
if (SUCCEEDED(hres))
|
|
{
|
|
STGMEDIUM stg = { TYMED_HGLOBAL, hglob, 0 };
|
|
hres = pdto->SetData(&fe, &stg, 1);
|
|
|
|
if (!(EVAL(SUCCEEDED(hres))))
|
|
GlobalFree(hglob);
|
|
}
|
|
else
|
|
hres = E_OUTOFMEMORY;
|
|
|
|
return hres;
|
|
}
|
|
#endif
|
|
|
|
|
|
CFtpPidlList * CreateRelativePidlList(CFtpFolder * pff, CFtpPidlList * pPidlListFull)
|
|
{
|
|
int nSize = pPidlListFull->GetCount();
|
|
CFtpPidlList * pPidlListNew = NULL;
|
|
|
|
if (nSize > 0)
|
|
{
|
|
LPCITEMIDLIST pidlFirst = pff->GetPrivatePidlReference();
|
|
int nCount = 0;
|
|
|
|
while (!ILIsEmpty(pidlFirst))
|
|
{
|
|
pidlFirst = _ILNext(pidlFirst);
|
|
nCount++;
|
|
}
|
|
|
|
if (nSize > 0)
|
|
{
|
|
for (int nIndex = 0; nIndex < nSize; nIndex++)
|
|
{
|
|
int nLeft = nCount;
|
|
LPITEMIDLIST pidl = pPidlListFull->GetPidl(nIndex);
|
|
|
|
while (nLeft--)
|
|
pidl = _ILNext(pidl);
|
|
|
|
AssertMsg((pidl ? TRUE : FALSE), TEXT("CreateRelativePidlList() pPidlListFull->GetPidl() should never fail because we got the size and no mem allocation is needed."));
|
|
if (0 == nIndex)
|
|
{
|
|
CFtpPidlList_Create(1, (LPCITEMIDLIST *)&pidl, &pPidlListNew);
|
|
if (!pPidlListNew)
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// We only want to add top level nodes.
|
|
// ftp://s/d1/d2/ <- Root of copy.
|
|
// ftp://s/d1/d2/d3a/ <- First Top Level Item
|
|
// ftp://s/d1/d2/d3a/f1 <- Skip non-top level items
|
|
// ftp://s/d1/d2/d3b/ <- Second Top Level Item
|
|
if (pidl && !ILIsEmpty(pidl) && ILIsEmpty(_ILNext(pidl)))
|
|
pPidlListNew->InsertSorted(pidl);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return pPidlListNew;
|
|
}
|
|
|
|
|
|
#define SZ_VERB_DELETEA "delete"
|
|
/*****************************************************************************\
|
|
FUNCTION: Misc_DeleteHfpl
|
|
|
|
DESCRIPTION:
|
|
Delete the objects described by a pflHfpl.
|
|
\*****************************************************************************/
|
|
HRESULT Misc_DeleteHfpl(CFtpFolder * pff, HWND hwnd, CFtpPidlList * pflHfpl)
|
|
{
|
|
IContextMenu * pcm;
|
|
HRESULT hr = pff->GetUIObjectOfHfpl(hwnd, pflHfpl, IID_IContextMenu, (LPVOID *)&pcm, FALSE);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CMINVOKECOMMANDINFO ici = {
|
|
sizeof(ici), // cbSize
|
|
CMIC_MASK_FLAG_NO_UI, // fMask
|
|
hwnd, // hwnd
|
|
SZ_VERB_DELETEA, // lpVerb
|
|
0, // lpParameters
|
|
0, // lpDirectory
|
|
0, // nShow
|
|
0, // dwHotKey
|
|
0, // hIcon
|
|
};
|
|
hr = pcm->InvokeCommand(&ici);
|
|
pcm->Release();
|
|
}
|
|
else
|
|
{
|
|
// Couldn't delete source; oh well. Don't need UI because
|
|
// this should only happen in out of memory.
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
|
|
Misc_FindStatusBar
|
|
|
|
Get the status bar from a browser window.
|
|
|
|
_UNDOCUMENTED_: The following quirks are not documented.
|
|
|
|
Note that we need to be very paranoid about the way GetControlWindow
|
|
works. Some people (Desktop) properly return error if the window
|
|
does not exist. Others (Explorer) return S_OK when the window
|
|
does not exist, but they kindly set *lphwndOut = 0. Still others
|
|
(Find File) return S_OK but leave *lphwndOut unchanged!
|
|
|
|
In order to work with all these, we must manually set hwnd = 0
|
|
before calling, and continue only if GetControlWindow returns success
|
|
*and* the outgoing hwnd is nonzero.
|
|
|
|
Furthermore, the documentation for GetControlWindow says that we
|
|
have to check the window class before trusting the hwnd.
|
|
|
|
\*****************************************************************************/
|
|
|
|
#pragma BEGIN_CONST_DATA
|
|
|
|
TCHAR c_tszStatusBarClass[] = STATUSCLASSNAME;
|
|
|
|
#pragma END_CONST_DATA
|
|
|
|
HWND Misc_FindStatusBar(HWND hwndOwner)
|
|
{
|
|
HWND hwnd = 0; // Must preinit in case GetControlWindow fails
|
|
|
|
if (EVAL(hwndOwner))
|
|
{
|
|
IShellBrowser * psb = FileCabinet_GetIShellBrowser(hwndOwner);
|
|
|
|
if (psb)
|
|
{
|
|
if (SUCCEEDED(psb->GetControlWindow(FCW_STATUS, &hwnd)) && hwnd) // This won't work when hosted in an IFRAME
|
|
{
|
|
// Make sure it really is a status bar...
|
|
TCHAR tszClass[ARRAYSIZE(c_tszStatusBarClass)+1];
|
|
|
|
if (GetClassName(hwnd, tszClass, ARRAYSIZE(tszClass)) &&
|
|
!StrCmpI(tszClass, c_tszStatusBarClass))
|
|
{
|
|
// We have a winner
|
|
}
|
|
else
|
|
hwnd = 0; // False positive
|
|
}
|
|
}
|
|
}
|
|
|
|
return hwnd;
|
|
}
|
|
|
|
#ifdef DEBUG
|
|
void TraceMsgWithCurrentDir(DWORD dwTFOperation, LPCSTR pszMessage, HINTERNET hint)
|
|
{
|
|
// For debugging...
|
|
TCHAR szCurrentDir[MAX_PATH];
|
|
DWORD cchDebugSize = ARRAYSIZE(szCurrentDir);
|
|
|
|
DEBUG_CODE(DebugStartWatch());
|
|
// PERF: Status FtpGetCurrentDirectory/FtpSetCurrentDirectory() takes
|
|
// 180-280ms on ftp.microsoft.com on average.
|
|
// 500-2000ms on ftp://ftp.tu-clausthal.de/ on average
|
|
// 0-10ms on ftp://shapitst/ on average
|
|
EVAL(FtpGetCurrentDirectory(hint, szCurrentDir, &cchDebugSize));
|
|
DEBUG_CODE(TraceMsg(TF_WININET_DEBUG, "TraceMsgWithCurrentDir() FtpGetCurrentDirectory() returned %ls and took %lu milliseconds", szCurrentDir, DebugStopWatch()));
|
|
TraceMsg(dwTFOperation, pszMessage, szCurrentDir);
|
|
}
|
|
|
|
|
|
void DebugStartWatch(void)
|
|
{
|
|
LARGE_INTEGER liStopWatchStart;
|
|
|
|
liStopWatchStart.HighPart = PtrToUlong(TlsGetValue(g_TLSliStopWatchStartHi));
|
|
liStopWatchStart.LowPart = PtrToUlong(TlsGetValue(g_TLSliStopWatchStartLo));
|
|
|
|
ASSERT(!liStopWatchStart.QuadPart); // If you hit this, then the stopwatch is nested.
|
|
QueryPerformanceFrequency(&g_liStopWatchFreq);
|
|
QueryPerformanceCounter(&liStopWatchStart);
|
|
|
|
TlsSetValue(g_TLSliStopWatchStartHi, UlongToPtr(liStopWatchStart.HighPart));
|
|
TlsSetValue(g_TLSliStopWatchStartLo, UlongToPtr(liStopWatchStart.LowPart));
|
|
}
|
|
|
|
DWORD DebugStopWatch(void)
|
|
{
|
|
LARGE_INTEGER liDiff;
|
|
LARGE_INTEGER liStopWatchStart;
|
|
|
|
QueryPerformanceCounter(&liDiff);
|
|
liStopWatchStart.HighPart = PtrToUlong(TlsGetValue(g_TLSliStopWatchStartHi));
|
|
liStopWatchStart.LowPart = PtrToUlong(TlsGetValue(g_TLSliStopWatchStartLo));
|
|
liDiff.QuadPart -= liStopWatchStart.QuadPart;
|
|
|
|
ASSERT(0 != g_liStopWatchFreq.QuadPart); // I don't like to fault with div 0.
|
|
DWORD dwTime = (DWORD)((liDiff.QuadPart * 1000) / g_liStopWatchFreq.QuadPart);
|
|
|
|
TlsSetValue(g_TLSliStopWatchStartHi, (LPVOID) 0);
|
|
TlsSetValue(g_TLSliStopWatchStartLo, (LPVOID) 0);
|
|
|
|
return dwTime;
|
|
}
|
|
#endif // DEBUG
|
|
|
|
|
|
/*****************************************************************************\
|
|
|
|
GetCfBuf
|
|
|
|
Convert a clipboard format name to something stringable.
|
|
|
|
\*****************************************************************************/
|
|
void GetCfBufA(UINT cf, LPSTR pszOut, int cchOut)
|
|
{
|
|
if (!GetClipboardFormatNameA(cf, pszOut, cchOut))
|
|
wnsprintfA(pszOut, cchOut, "[%04x]", cf);
|
|
}
|
|
|
|
/*****************************************************************************\
|
|
|
|
AllocHGlob
|
|
|
|
Allocate a moveable HGLOBAL of the requested size, lock it, then call
|
|
the callback. On return, unlock it and get out.
|
|
|
|
Returns the allocated HGLOBAL, or 0.
|
|
|
|
\*****************************************************************************/
|
|
|
|
HGLOBAL AllocHGlob(UINT cb, HGLOBWITHPROC pfn, LPVOID pvRef, LPCVOID pvParam2, BOOL fUnicode)
|
|
{
|
|
HGLOBAL hglob = GlobalAlloc(GHND, cb);
|
|
if (hglob)
|
|
{
|
|
LPVOID pv = GlobalLock(hglob);
|
|
if (pv)
|
|
{
|
|
BOOL fRc = pfn(pv, pvRef, pvParam2, fUnicode);
|
|
GlobalUnlock(hglob);
|
|
if (!fRc)
|
|
{
|
|
GlobalFree(hglob);
|
|
hglob = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GlobalFree(hglob);
|
|
hglob = 0;
|
|
}
|
|
}
|
|
|
|
return hglob;
|
|
}
|
|
|
|
|
|
SHELL_VERSION g_ShellVersion = SHELL_VERSION_UNKNOWN;
|
|
#define SHELL_VERSION_FOR_WIN95_AND_NT4 4
|
|
|
|
|
|
SHELL_VERSION GetShellVersion(void)
|
|
{
|
|
if (SHELL_VERSION_UNKNOWN == g_ShellVersion)
|
|
{
|
|
g_ShellVersion = SHELL_VERSION_W95NT4;
|
|
HINSTANCE hInst = LoadLibrary(TEXT("shell32.dll"));
|
|
|
|
if (hInst)
|
|
{
|
|
DLLGETVERSIONPROC pfnDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hInst, "DllGetVersion");
|
|
|
|
if (pfnDllGetVersion)
|
|
{
|
|
DLLVERSIONINFO dllVersionInfo;
|
|
|
|
g_ShellVersion = SHELL_VERSION_IE4; // Assume this.
|
|
dllVersionInfo.cbSize = sizeof(dllVersionInfo);
|
|
if (SUCCEEDED(pfnDllGetVersion(&dllVersionInfo)))
|
|
{
|
|
if (SHELL_VERSION_FOR_WIN95_AND_NT4 < dllVersionInfo.dwMajorVersion)
|
|
g_ShellVersion = SHELL_VERSION_NT5; // Assume this.
|
|
}
|
|
}
|
|
FreeLibrary(hInst);
|
|
}
|
|
}
|
|
|
|
return g_ShellVersion;
|
|
}
|
|
|
|
DWORD GetShdocvwVersion(void)
|
|
{
|
|
static DWORD majorVersion=0; // cache for perf
|
|
|
|
if (majorVersion)
|
|
return majorVersion;
|
|
|
|
HINSTANCE hInst = LoadLibrary(TEXT("shdocvw.dll"));
|
|
if (hInst)
|
|
{
|
|
DLLGETVERSIONPROC pfnDllGetVersion = (DLLGETVERSIONPROC) GetProcAddress(hInst, "DllGetVersion");
|
|
|
|
if (pfnDllGetVersion)
|
|
{
|
|
DLLVERSIONINFO dllVersionInfo;
|
|
|
|
dllVersionInfo.cbSize = sizeof(dllVersionInfo);
|
|
if (SUCCEEDED(pfnDllGetVersion(&dllVersionInfo)))
|
|
{
|
|
majorVersion = dllVersionInfo.dwMajorVersion;
|
|
}
|
|
}
|
|
|
|
FreeLibrary(hInst);
|
|
}
|
|
|
|
return majorVersion;
|
|
}
|
|
|
|
|
|
BOOL ShouldSkipDropFormat(int nIndex)
|
|
{
|
|
// Allow DROP_IDList or repositioning items withing
|
|
// ftp windows won't work.
|
|
/*
|
|
// We want to skip DROP_IDList on Win95 and WinNT4's shell
|
|
// because it will cause the old shell to only offer DROPEFFECT_LINK
|
|
// so download isn't available.
|
|
if (((DROP_IDList == nIndex)) &&
|
|
(SHELL_VERSION_W95NT4 == GetShellVersion()))
|
|
{
|
|
return TRUE;
|
|
}
|
|
*/
|
|
|
|
#ifndef BROWSERONLY_DRAGGING
|
|
if (((DROP_FGDW == nIndex) || (DROP_FGDA == nIndex)) &&
|
|
(SHELL_VERSION_NT5 != GetShellVersion()))
|
|
{
|
|
return TRUE;
|
|
}
|
|
#endif // BROWSERONLY_DRAGGING
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
void SetWindowBits(HWND hWnd, int iWhich, DWORD dwBits, DWORD dwValue)
|
|
{
|
|
DWORD dwStyle;
|
|
DWORD dwNewStyle;
|
|
|
|
dwStyle = GetWindowLong(hWnd, iWhich);
|
|
dwNewStyle = ( dwStyle & ~dwBits ) | (dwValue & dwBits);
|
|
if (dwStyle != dwNewStyle) {
|
|
SetWindowLong(hWnd, iWhich, dwNewStyle);
|
|
}
|
|
}
|
|
|
|
|
|
void InitComctlForNaviteFonts(void)
|
|
{
|
|
// hinst is ignored because we set it at our LibMain()
|
|
INITCOMMONCONTROLSEX icex = {0};
|
|
|
|
icex.dwSize = sizeof(INITCOMMONCONTROLSEX);
|
|
icex.dwICC = ICC_USEREX_CLASSES|ICC_NATIVEFNTCTL_CLASS;
|
|
InitCommonControlsEx(&icex);
|
|
}
|
|
|
|
|
|
BOOL DoesUrlContainNTDomainName(LPCTSTR pszUrl)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
LPCTSTR pszPointer = pszUrl;
|
|
|
|
if (lstrlen(pszPointer) > ARRAYSIZE(SZ_FTPURL))
|
|
{
|
|
pszPointer += ARRAYSIZE(SZ_FTPURL); // Skip past the scheme.
|
|
pszPointer = StrChr(pszPointer, CH_URL_SLASH);
|
|
if (pszPointer)
|
|
{
|
|
pszPointer = StrChr(CharNext(pszPointer), CH_URL_PASSWORD_SEPARATOR);
|
|
if (pszPointer)
|
|
{
|
|
pszPointer = StrChr(CharNext(pszPointer), CH_URL_LOGON_SEPARATOR);
|
|
if (pszPointer)
|
|
fResult = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fResult;
|
|
}
|
|
|
|
|
|
HRESULT CharReplaceWithStrW(LPWSTR pszLocToInsert, DWORD cchSize, DWORD cchChars, LPWSTR pszStrToInsert)
|
|
{
|
|
WCHAR szTemp[MAX_URL_STRING];
|
|
|
|
StrCpyNW(szTemp, pszLocToInsert, ARRAYSIZE(szTemp));
|
|
|
|
pszLocToInsert[0] = 0; // Terminate String here to kill char.
|
|
StrCatBuffW(pszLocToInsert, pszStrToInsert, cchSize);
|
|
StrCatBuffW(pszLocToInsert, &szTemp[cchChars], cchSize);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT CharReplaceWithStrA(LPSTR pszLocToInsert, DWORD cchSize, DWORD cchChars, LPSTR pszStrToInsert)
|
|
{
|
|
CHAR szTemp[MAX_URL_STRING];
|
|
|
|
StrCpyNA(szTemp, pszLocToInsert, ARRAYSIZE(szTemp));
|
|
|
|
pszLocToInsert[0] = 0; // Terminate String here to kill char.
|
|
StrCatBuffA(pszLocToInsert, pszStrToInsert, cchSize);
|
|
StrCatBuffA(pszLocToInsert, &szTemp[cchChars], cchSize);
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT RemoveCharsFromString(LPTSTR pszLocToRemove, DWORD cchSizeToRemove)
|
|
{
|
|
LPTSTR pszRest = &pszLocToRemove[cchSizeToRemove];
|
|
|
|
MoveMemory((LPVOID) pszLocToRemove, (LPVOID) pszRest, (lstrlen(pszRest) + 1) * sizeof(TCHAR));
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
HRESULT RemoveCharsFromStringA(LPSTR pszLocToRemove, DWORD cchSizeToRemove)
|
|
{
|
|
LPSTR pszRest = &pszLocToRemove[cchSizeToRemove];
|
|
|
|
MoveMemory((LPVOID) pszLocToRemove, (LPVOID) pszRest, (lstrlenA(pszRest) + 1) * sizeof(CHAR));
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
// Helper function to convert Ansi string to allocated BSTR
|
|
#ifndef UNICODE
|
|
BSTR AllocBStrFromString(LPCTSTR psz)
|
|
{
|
|
OLECHAR wsz[INFOTIPSIZE]; // assumes INFOTIPSIZE number of chars max
|
|
|
|
SHAnsiToUnicode(psz, wsz, ARRAYSIZE(wsz));
|
|
return SysAllocString(wsz);
|
|
|
|
}
|
|
#endif // UNICODE
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: StrListLength
|
|
|
|
DESCRIPTION:
|
|
\****************************************************/
|
|
DWORD StrListLength(LPCTSTR ppszStrList)
|
|
{
|
|
LPTSTR pszStr = (LPTSTR) ppszStrList;
|
|
DWORD cchLength = 0;
|
|
|
|
while (pszStr[0])
|
|
{
|
|
pszStr += (lstrlen(pszStr) + 1);
|
|
cchLength++;
|
|
}
|
|
|
|
return cchLength;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: CalcStrListSizeA
|
|
|
|
DESCRIPTION:
|
|
\****************************************************/
|
|
DWORD CalcStrListSizeA(LPCSTR ppszStrList)
|
|
{
|
|
LPSTR pszStr = (LPSTR) ppszStrList;
|
|
DWORD cchSize = 1;
|
|
|
|
while (pszStr[0])
|
|
{
|
|
DWORD cchSizeCurr = lstrlenA(pszStr) + 1;
|
|
|
|
cchSize += cchSizeCurr;
|
|
pszStr += cchSizeCurr;
|
|
}
|
|
|
|
return cchSize;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: CalcStrListSizeW
|
|
|
|
DESCRIPTION:
|
|
\****************************************************/
|
|
DWORD CalcStrListSizeW(LPCWSTR ppwzStrList)
|
|
{
|
|
LPWSTR pwzStr = (LPWSTR) ppwzStrList;
|
|
DWORD cchSize = 1;
|
|
|
|
while (pwzStr[0])
|
|
{
|
|
DWORD cchSizeCurr = lstrlenW(pwzStr) + 1;
|
|
|
|
cchSize += cchSizeCurr;
|
|
pwzStr += cchSizeCurr;
|
|
}
|
|
|
|
return cchSize;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: AnsiToUnicodeStrList
|
|
|
|
DESCRIPTION:
|
|
\****************************************************/
|
|
void AnsiToUnicodeStrList(LPCSTR ppszStrListIn, LPCWSTR ppwzStrListOut, DWORD cchSize)
|
|
{
|
|
LPWSTR pwzStrOut = (LPWSTR) ppwzStrListOut;
|
|
LPSTR pszStrIn = (LPSTR) ppszStrListIn;
|
|
|
|
while (pszStrIn[0])
|
|
{
|
|
SHAnsiToUnicode(pszStrIn, pwzStrOut, lstrlenA(pszStrIn) + 2);
|
|
|
|
pszStrIn += lstrlenA(pszStrIn) + 1;
|
|
pwzStrOut += lstrlenW(pwzStrOut) + 1;
|
|
}
|
|
|
|
pwzStrOut[0] = L'\0';
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: UnicodeToAnsiStrList
|
|
|
|
DESCRIPTION:
|
|
\****************************************************/
|
|
void UnicodeToAnsiStrList(LPCWSTR ppwzStrListIn, LPCSTR ppszStrListOut, DWORD cchSize)
|
|
{
|
|
LPSTR pszStrOut = (LPSTR) ppszStrListOut;
|
|
LPWSTR pwzStrIn = (LPWSTR) ppwzStrListIn;
|
|
|
|
while (pwzStrIn[0])
|
|
{
|
|
SHUnicodeToAnsi(pwzStrIn, pszStrOut, lstrlenW(pwzStrIn) + 2);
|
|
|
|
pwzStrIn += lstrlenW(pwzStrIn) + 1;
|
|
pszStrOut += lstrlenA(pszStrOut) + 1;
|
|
}
|
|
|
|
pszStrOut[0] = '\0';
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: Str_StrAndThunkA
|
|
|
|
DESCRIPTION:
|
|
\****************************************************/
|
|
HRESULT Str_StrAndThunkA(LPTSTR * ppszOut, LPCSTR pszIn, BOOL fStringList)
|
|
{
|
|
#ifdef UNICODE
|
|
if (!fStringList)
|
|
{
|
|
DWORD cchSize = (lstrlenA(pszIn) + 2);
|
|
LPWSTR pwzBuffer = (LPWSTR) LocalAlloc(LPTR, cchSize * SIZEOF(WCHAR));
|
|
|
|
if (!pwzBuffer)
|
|
return E_OUTOFMEMORY;
|
|
|
|
SHAnsiToUnicode(pszIn, pwzBuffer, cchSize);
|
|
Str_SetPtrW(ppszOut, pwzBuffer);
|
|
}
|
|
else
|
|
{
|
|
DWORD cchSize = CalcStrListSizeA(pszIn);
|
|
Str_SetPtrW(ppszOut, NULL); // Free
|
|
|
|
*ppszOut = (LPTSTR) LocalAlloc(LPTR, cchSize * sizeof(WCHAR));
|
|
if (*ppszOut)
|
|
AnsiToUnicodeStrList(pszIn, *ppszOut, cchSize);
|
|
}
|
|
|
|
#else // UNICODE
|
|
|
|
if (!fStringList)
|
|
{
|
|
// No thunking needed.
|
|
Str_SetPtrA(ppszOut, pszIn);
|
|
}
|
|
else
|
|
{
|
|
DWORD cchSize = CalcStrListSizeA(pszIn);
|
|
Str_SetPtrA(ppszOut, NULL); // Free
|
|
|
|
*ppszOut = (LPTSTR) LocalAlloc(LPTR, cchSize * sizeof(CHAR));
|
|
if (*ppszOut)
|
|
CopyMemory(*ppszOut, pszIn, cchSize * sizeof(CHAR));
|
|
}
|
|
#endif // UNICODE
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
BOOL IsValidFtpAnsiFileName(LPCTSTR pszString)
|
|
{
|
|
#ifdef UNICODE
|
|
// TODO:
|
|
#endif // UNICODE
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: Str_StrAndThunkW
|
|
|
|
DESCRIPTION:
|
|
\****************************************************/
|
|
HRESULT Str_StrAndThunkW(LPTSTR * ppszOut, LPCWSTR pwzIn, BOOL fStringList)
|
|
{
|
|
#ifdef UNICODE
|
|
if (!fStringList)
|
|
{
|
|
// No thunking needed.
|
|
Str_SetPtrW(ppszOut, pwzIn);
|
|
}
|
|
else
|
|
{
|
|
DWORD cchSize = CalcStrListSizeW(pwzIn);
|
|
Str_SetPtrW(ppszOut, NULL); // Free
|
|
|
|
*ppszOut = (LPTSTR) LocalAlloc(LPTR, cchSize * sizeof(WCHAR));
|
|
if (*ppszOut)
|
|
CopyMemory(*ppszOut, pwzIn, cchSize * sizeof(WCHAR));
|
|
}
|
|
|
|
#else // UNICODE
|
|
|
|
if (!fStringList)
|
|
{
|
|
DWORD cchSize = (lstrlenW(pwzIn) + 2);
|
|
LPSTR pszBuffer = (LPSTR) LocalAlloc(LPTR, cchSize * SIZEOF(CHAR));
|
|
|
|
if (!pszBuffer)
|
|
return E_OUTOFMEMORY;
|
|
|
|
SHUnicodeToAnsi(pwzIn, pszBuffer, cchSize);
|
|
Str_SetPtrA(ppszOut, pszBuffer);
|
|
}
|
|
else
|
|
{
|
|
DWORD cchSize = CalcStrListSizeW(pwzIn);
|
|
Str_SetPtrA(ppszOut, NULL); // Free
|
|
|
|
*ppszOut = (LPTSTR) LocalAlloc(LPTR, cchSize * sizeof(CHAR));
|
|
if (*ppszOut)
|
|
UnicodeToAnsiStrList(pwzIn, *ppszOut, cchSize * sizeof(CHAR));
|
|
}
|
|
#endif // UNICODE
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
#ifndef UNICODE
|
|
// TruncateString
|
|
//
|
|
// purpose: cut a string at the given length in dbcs safe manner.
|
|
// the string may be truncated at cch-2 if the sz[cch] points
|
|
// to a lead byte that would result in cutting in the middle
|
|
// of double byte character.
|
|
//
|
|
// update: made it faster for sbcs environment (5/26/97)
|
|
// now returns adjusted cch (6/20/97)
|
|
//
|
|
void TruncateString(char *sz, int cchBufferSize)
|
|
{
|
|
if (!sz || cchBufferSize <= 0) return;
|
|
|
|
int cch = cchBufferSize - 1; // get index position to NULL out
|
|
|
|
LPSTR psz = &sz[cch];
|
|
|
|
while (psz >sz)
|
|
{
|
|
psz--;
|
|
if (!IsDBCSLeadByte(*psz))
|
|
{
|
|
// Found non-leadbyte for the first time.
|
|
// This is either a trail byte of double byte char
|
|
// or a single byte character we've first seen.
|
|
// Thus, the next pointer must be at either of a leadbyte
|
|
// or &sz[cch]
|
|
psz++;
|
|
break;
|
|
}
|
|
}
|
|
if (((&sz[cch] - psz) & 1) && cch > 0)
|
|
{
|
|
// we're truncating the string in the middle of dbcs
|
|
cch--;
|
|
}
|
|
sz[cch] = '\0';
|
|
return;
|
|
}
|
|
|
|
#endif // UNICODE
|
|
|
|
|
|
|
|
HRESULT CopyStgMediumWrap(const STGMEDIUM * pcstgmedSrc, STGMEDIUM * pstgmedDest)
|
|
{
|
|
HRESULT hr = CopyStgMedium(pcstgmedSrc, pstgmedDest);
|
|
|
|
// if pstgmedDest->pUnkForElease is NULL,
|
|
// then we need to free hglobal because we own freeing the memory.
|
|
// else someone else owns the lifetime of the memory and releasing
|
|
// pUnkForElease is the way to indicate that we won't use it anymore.
|
|
//
|
|
// The problem is that urlmon's CopyStgMedium() ERRouniously copies the
|
|
// pUnkForElease param in addition to cloning the memory. This means
|
|
// that we own freeing the memory but the pointer being non-NULL would
|
|
// indicate that we don't own freeing the memory.
|
|
|
|
// ASSERT(NULL == pstgmedDest->pUnkForElease);
|
|
pstgmedDest->pUnkForRelease = NULL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
HRESULT SHBindToIDList(LPCITEMIDLIST pidl, LPBC pbc, REFIID riid, void **ppv)
|
|
{
|
|
IShellFolder * psf;
|
|
HRESULT hr = SHGetDesktopFolder(&psf);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = psf->BindToObject(pidl, pbc, riid, ppv);
|
|
psf->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
STDAPI DataObj_GetDropTarget(IDataObject *pdtobj, CLSID *pclsid)
|
|
{
|
|
STGMEDIUM medium;
|
|
FORMATETC fmte = {(CLIPFORMAT) g_cfTargetCLSID, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
|
|
HRESULT hr = pdtobj->GetData(&fmte, &medium);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CLSID *pdw = (CLSID *)GlobalLock(medium.hGlobal);
|
|
if (pdw)
|
|
{
|
|
*pclsid = *pdw;
|
|
GlobalUnlock(medium.hGlobal);
|
|
}
|
|
else
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
}
|
|
ReleaseStgMedium(&medium);
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
STDAPI DataObj_SetPreferredEffect(IDataObject *pdtobj, DWORD dwEffect)
|
|
{
|
|
return DataObj_SetDWORD(pdtobj, g_dropTypes[DROP_PrefDe].cfFormat, dwEffect);
|
|
}
|
|
|
|
|
|
STDAPI DataObj_SetPasteSucceeded(IDataObject *pdtobj, DWORD dwEffect)
|
|
{
|
|
return DataObj_SetDWORD(pdtobj, g_formatPasteSucceeded.cfFormat, dwEffect);
|
|
}
|
|
|
|
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: ShowEnableWindow
|
|
|
|
DESCRIPTION:
|
|
If you don't want a window to be visible or
|
|
usable by the user, you need to call both
|
|
ShowWindow(SW_HIDE) and EnableWindow(FALSE) or
|
|
the window may be hidden but still accessible via
|
|
the keyboard.
|
|
\****************************************************/
|
|
void ShowEnableWindow(HWND hwnd, BOOL fShow)
|
|
{
|
|
ShowWindow(hwnd, (fShow ? SW_SHOW : SW_HIDE));
|
|
EnableWindow(hwnd, fShow);
|
|
}
|
|
|
|
|
|
STDAPI StringToStrRetW(LPCWSTR pwzString, STRRET *pstrret)
|
|
{
|
|
HRESULT hr = SHStrDupW(pwzString, &pstrret->pOleStr);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
pstrret->uType = STRRET_WSTR;
|
|
}
|
|
return hr;
|
|
}
|
|
|
|
|
|
#define BIT_8_SET 0x80
|
|
|
|
BOOL Is7BitAnsi(LPCWIRESTR pwByteStr)
|
|
{
|
|
BOOL fIs7BitAnsi = TRUE;
|
|
|
|
if (pwByteStr)
|
|
{
|
|
while (pwByteStr[0])
|
|
{
|
|
if (BIT_8_SET & pwByteStr[0])
|
|
{
|
|
fIs7BitAnsi = FALSE;
|
|
break;
|
|
}
|
|
|
|
pwByteStr++;
|
|
}
|
|
}
|
|
|
|
return fIs7BitAnsi;
|
|
}
|
|
|
|
|
|
HRESULT LoginAs(HWND hwnd, CFtpFolder * pff, CFtpDir * pfd, IUnknown * punkSite)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CFtpSite * pfs = pfd->GetFtpSite();
|
|
|
|
ASSERT(hwnd && pff);
|
|
if (pfs)
|
|
{
|
|
CAccounts cAccounts;
|
|
TCHAR szServer[INTERNET_MAX_HOST_NAME_LENGTH];
|
|
TCHAR szUser[INTERNET_MAX_USER_NAME_LENGTH];
|
|
TCHAR szPassword[INTERNET_MAX_PASSWORD_LENGTH];
|
|
LPCITEMIDLIST pidlPrevious = pfd->GetPidlReference();
|
|
|
|
pfs->GetServer(szServer, ARRAYSIZE(szServer));
|
|
pfs->GetUser(szUser, ARRAYSIZE(szUser));
|
|
pfs->GetPassword(szPassword, ARRAYSIZE(szPassword));
|
|
|
|
hr = cAccounts.DisplayLoginDialog(hwnd, LOGINFLAGS_DEFAULT, szServer, szUser, ARRAYSIZE(szUser), szPassword, ARRAYSIZE(szPassword));
|
|
if (S_OK == hr)
|
|
{
|
|
LPITEMIDLIST pidlNew;
|
|
|
|
ASSERT(pff->GetItemAllocatorDirect());
|
|
hr = PidlReplaceUserPassword(pidlPrevious, &pidlNew, pff->GetItemAllocatorDirect(), szUser, szPassword);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
CFtpSite * pfs;
|
|
LPITEMIDLIST pidlRedirect;
|
|
|
|
// We need to update the password in the site to redirect to the correct or new one.
|
|
if (EVAL(SUCCEEDED(PidlReplaceUserPassword(pidlNew, &pidlRedirect, pff->GetItemAllocatorDirect(), szUser, TEXT(""))) &&
|
|
SUCCEEDED(SiteCache_PidlLookup(pidlRedirect, TRUE, pff->GetItemAllocatorDirect(), &pfs))))
|
|
{
|
|
EVAL(SUCCEEDED(pfs->SetRedirPassword(szPassword)));
|
|
pfs->Release();
|
|
ILFree(pidlRedirect);
|
|
}
|
|
|
|
// pidl is a full private pidl. pidlFull will be a full public pidl because
|
|
// that's what the browser needs to get back from the root of THE public
|
|
// name space back to and into us.
|
|
LPITEMIDLIST pidlFull = pff->CreateFullPublicPidl(pidlNew);
|
|
if (pidlFull)
|
|
{
|
|
hr = IUnknown_PidlNavigate(punkSite, pidlFull, TRUE);
|
|
ILFree(pidlFull);
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
|
|
ILFree(pidlNew);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
HRESULT LoginAsViaFolder(HWND hwnd, CFtpFolder * pff, IUnknown * punkSite)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
CFtpDir * pfd = pff->GetFtpDir();
|
|
|
|
if (pfd)
|
|
{
|
|
hr = LoginAs(hwnd, pff, pfd, punkSite);
|
|
pfd->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
#define PATH_IS_DRIVE(wzPath) (-1 != PathGetDriveNumberW(wzPath))
|
|
|
|
HRESULT SHPathPrepareForWriteWrapW(HWND hwnd, IUnknown *punkEnableModless, LPCWSTR pwzPath, UINT wFunc, DWORD dwFlags)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (SHELL_VERSION_NT5 == GetShellVersion())
|
|
{
|
|
// NT5's version of the API is better.
|
|
hr = _SHPathPrepareForWriteW(hwnd, punkEnableModless, pwzPath, dwFlags);
|
|
}
|
|
else
|
|
{
|
|
if (PATH_IS_DRIVE(pwzPath))
|
|
{
|
|
hr = (SHCheckDiskForMediaW(hwnd, punkEnableModless, pwzPath, wFunc) ? S_OK : E_FAIL);
|
|
}
|
|
else
|
|
{
|
|
if (PathIsUNCW(pwzPath))
|
|
{
|
|
hr = (PathFileExistsW(pwzPath) ? S_OK : E_FAIL);
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
// Helper function
|
|
int _LoadStringW(HINSTANCE hinst, UINT id, LPWSTR wsz, UINT cchMax)
|
|
{
|
|
char szT[512];
|
|
if (LoadStringA(hinst, id, szT, ARRAYSIZE(szT)))
|
|
{
|
|
TraceMsg(0, "LoadStringW just loaded (%s)", szT);
|
|
return SHAnsiToUnicode(szT, wsz, cchMax) - 1; // -1 for terminator
|
|
}
|
|
else
|
|
{
|
|
TraceMsg(DM_TRACE, "sdv TR LoadStringW(%x) failed", id);
|
|
wsz[0] = L'\0';
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
HRESULT HrShellExecute(HWND hwnd, LPCTSTR lpVerb, LPCTSTR lpFile, LPCTSTR lpParameters, LPCTSTR lpDirectory, INT nShowCmd)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
HINSTANCE hReturn = ShellExecute(hwnd, lpVerb, lpFile, lpParameters, lpDirectory, nShowCmd);
|
|
|
|
if ((HINSTANCE)32 > hReturn)
|
|
{
|
|
hr = ResultFromLastError();
|
|
}
|
|
|
|
return hr;
|
|
}
|