2698 lines
94 KiB
C++
2698 lines
94 KiB
C++
/**************************************************************\
|
|
FILE: shellurl.cpp
|
|
|
|
DESCRIPTION:
|
|
Implements CShellUrl.
|
|
\**************************************************************/
|
|
|
|
#include "priv.h"
|
|
#include "resource.h"
|
|
#include "util.h"
|
|
#include "shellurl.h"
|
|
|
|
#include "bandprxy.h"
|
|
|
|
#include "mluisupp.h"
|
|
|
|
#ifdef UNIX
|
|
#include "unixstuff.h"
|
|
#endif
|
|
|
|
// We need to reroute radio urls
|
|
#define WZ_RADIO_PROTOCOL L"vnd.ms.radio:"
|
|
|
|
|
|
//#define FEATURE_WILDCARD_SUPPORT
|
|
|
|
#define CH_DOT TEXT('.')
|
|
#define CH_SPACE TEXT(' ')
|
|
#define CH_SEPARATOR TEXT('/')
|
|
#define CH_FRAGMENT TEXT('#')
|
|
|
|
#ifdef FEATURE_WILDCARD_SUPPORT
|
|
#define CH_ASTRISK TEXT('*')
|
|
#define CH_QUESTIONMARK TEXT('?')
|
|
#endif // FEATURE_WILDCARD_SUPPORT
|
|
|
|
#define SZ_SPACE TEXT(" ")
|
|
#define SZ_SEPARATOR TEXT("/")
|
|
#define SZ_UNC TEXT("\\\\")
|
|
|
|
#ifndef UNIX
|
|
#define CH_FILESEPARATOR TEXT('\\')
|
|
#define SZ_FILESEPARATOR TEXT("\\")
|
|
#else
|
|
#define CH_FILESEPARATOR TEXT('/')
|
|
#define SZ_FILESEPARATOR TEXT("/")
|
|
#endif
|
|
|
|
#define CE_PATHGROW 1
|
|
|
|
#define IS_SHELL_SEPARATOR(ch) ((CH_SEPARATOR == ch) || (CH_FILESEPARATOR == ch))
|
|
|
|
// Private Functions
|
|
BOOL _FixDriveDisplayName(LPCTSTR pszStart, LPCTSTR pszCurrent, LPCITEMIDLIST pidl);
|
|
|
|
#define TF_CHECKITEM 0 // TF_BAND|TF_GENERAL
|
|
|
|
|
|
/****************************************************\
|
|
CShellUrl Constructor
|
|
\****************************************************/
|
|
CShellUrl::CShellUrl()
|
|
{
|
|
TraceMsg(TF_SHDLIFE, "ctor CShellUrl %x", this);
|
|
|
|
// Don't want this object to be on the stack
|
|
ASSERT(!m_pszURL);
|
|
ASSERT(!m_pszArgs);
|
|
ASSERT(!m_pstrRoot);
|
|
ASSERT(!m_pidl);
|
|
ASSERT(!m_pidlWorkingDir);
|
|
ASSERT(!m_hdpaPath);
|
|
ASSERT(!m_dwGenType);
|
|
ASSERT(!m_hwnd);
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
CShellUrl destructor
|
|
\****************************************************/
|
|
CShellUrl::~CShellUrl()
|
|
{
|
|
Reset();
|
|
if (m_pstrRoot)
|
|
{
|
|
LocalFree(m_pstrRoot);
|
|
m_pstrRoot = NULL;
|
|
}
|
|
|
|
if (m_pidlWorkingDir)
|
|
ILFree(m_pidlWorkingDir);
|
|
|
|
_DeletePidlDPA(m_hdpaPath);
|
|
TraceMsg(TF_SHDLIFE, "dtor CShellUrl %x", this);
|
|
}
|
|
|
|
|
|
//*** CShellUrl::IUnknown::* {
|
|
|
|
ULONG CShellUrl::AddRef()
|
|
{
|
|
return InterlockedIncrement(&_cRef);
|
|
}
|
|
|
|
ULONG CShellUrl::Release()
|
|
{
|
|
ASSERT(_cRef > 0);
|
|
|
|
// n.b. returns <0,=0,>0 (not actual dec result)
|
|
if (InterlockedDecrement(&_cRef))
|
|
return _cRef;
|
|
|
|
delete this;
|
|
return 0;
|
|
}
|
|
|
|
HRESULT CShellUrl::QueryInterface(REFIID riid, void **ppvObj)
|
|
{
|
|
static const QITAB qit[] = {
|
|
QITABENT(CShellUrl, IAddressBarParser), // IID_IUserAssist
|
|
{ 0 },
|
|
};
|
|
|
|
return QISearch(this, qit, riid, ppvObj);
|
|
}
|
|
|
|
//+-------------------------------------------------------------------------
|
|
// Creates and instance of CShellUrl
|
|
//--------------------------------------------------------------------------
|
|
STDAPI CShellUrl_CreateInstance(IUnknown* pUnkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
|
|
{
|
|
// Note - Aggregation checking is handled in class factory
|
|
|
|
*ppunk = NULL;
|
|
CShellUrl* pShellUrl = new CShellUrl();
|
|
if (pShellUrl)
|
|
{
|
|
*ppunk = SAFECAST(pShellUrl, IAddressBarParser *);
|
|
(*ppunk)->AddRef();
|
|
return S_OK;
|
|
}
|
|
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: Clone
|
|
|
|
PARAMETERS
|
|
pShellUrl - This is the pointer to object that
|
|
we want to clone
|
|
|
|
DESCRIPTION:
|
|
This function will make a deep copy of the passed
|
|
object into 'this'
|
|
\****************************************************/
|
|
HRESULT CShellUrl::Clone(CShellUrl * pShellUrl)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
if (!pShellUrl)
|
|
{
|
|
hr = E_POINTER;
|
|
goto exit;
|
|
}
|
|
|
|
Str_SetPtr(&m_pszURL, pShellUrl->m_pszURL);
|
|
Str_SetPtr(&m_pszDisplayName, pShellUrl->m_pszDisplayName);
|
|
Str_SetPtr(&m_pszArgs, pShellUrl->m_pszArgs);
|
|
Str_SetPtr(&m_pstrRoot, pShellUrl->m_pstrRoot);
|
|
|
|
m_dwGenType = pShellUrl->m_dwGenType;
|
|
m_dwFlags = pShellUrl->m_dwFlags;
|
|
m_hwnd = pShellUrl->m_hwnd;
|
|
|
|
if (m_pidl)
|
|
{
|
|
ILFree(m_pidl);
|
|
m_pidl = NULL;
|
|
}
|
|
|
|
if (pShellUrl->m_pidl)
|
|
{
|
|
m_pidl = ILClone(pShellUrl->m_pidl);
|
|
if (!m_pidl)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
if (m_pidlWorkingDir)
|
|
{
|
|
ILFree(m_pidlWorkingDir);
|
|
m_pidlWorkingDir = NULL;
|
|
}
|
|
|
|
if (pShellUrl->m_pidlWorkingDir)
|
|
{
|
|
m_pidlWorkingDir = ILClone(pShellUrl->m_pidlWorkingDir);
|
|
if (!m_pidlWorkingDir)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
}
|
|
|
|
_DeletePidlDPA(m_hdpaPath);
|
|
m_hdpaPath = NULL;
|
|
if (pShellUrl->m_hdpaPath)
|
|
{
|
|
m_hdpaPath = DPA_Create(CE_PATHGROW);
|
|
for(int nPathIndex = 0; nPathIndex < DPA_GetPtrCount(pShellUrl->m_hdpaPath); nPathIndex++)
|
|
{
|
|
LPITEMIDLIST pidlCurrPath = (LPITEMIDLIST) DPA_GetPtr(pShellUrl->m_hdpaPath, nPathIndex);
|
|
LPITEMIDLIST pidlNew = ILClone(pidlCurrPath);
|
|
if (pidlNew)
|
|
DPA_AppendPtr(m_hdpaPath, pidlNew);
|
|
else
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
goto exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
exit:
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: Execute
|
|
|
|
PARAMETERS
|
|
pbp - This is the pointer to the interface
|
|
which is needed to find a new topmost
|
|
window or the associated browser window.
|
|
pfDidShellExec (Out Optional) - This parameter
|
|
can be NULL. If not NULL, it will be set
|
|
to TRUE if this Execute() called ShellExec.
|
|
This is needed by callers that wait for
|
|
DISPID_NAVIGATECOMPLETE which will never happen
|
|
in this case.
|
|
|
|
DESCRIPTION:
|
|
This command will determine if the current
|
|
shell url needs to be shell executed or navigated
|
|
to. If it needs to be navigated to, it will try
|
|
to navigate to the PIDL, otherwise, it will navigate
|
|
to the string version.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::Execute(IBandProxy * pbp, BOOL * pfDidShellExec, DWORD dwExecFlags)
|
|
{
|
|
HRESULT hr = S_FALSE; // S_FALSE until navigation occurs.
|
|
ULONG ulShellExecFMask = (IsFlagSet(dwExecFlags, SHURL_EXECFLAGS_SEPVDM)) ? SEE_MASK_FLAG_SEPVDM : 0;
|
|
|
|
ASSERT(IS_VALID_CODE_PTR(pbp, IBandProxy *));
|
|
ASSERT(!pfDidShellExec || IS_VALID_WRITE_PTR(pfDidShellExec, BOOL));
|
|
|
|
if (!EVAL(pbp))
|
|
return E_INVALIDARG;
|
|
|
|
#ifdef UNIX
|
|
|
|
// When trying to execute a shellurl we will first check if it is a local file
|
|
// url. If so check if there is a proper file association with it. If not give
|
|
// error and bail out.
|
|
TCHAR szTmpPath[MAX_URL_STRING];
|
|
BOOL bCheckForAssoc = FALSE;
|
|
if (m_pidl)
|
|
{
|
|
// Get Path from pidl
|
|
IEGetNameAndFlags(m_pidl, SHGDN_FORPARSING, szTmpPath, SIZECHARS(szTmpPath), NULL);
|
|
//SHTCharToAnsi( szTmpPath, szTmpPathAnsi, ARRAYSIZE(szTmpPathAnsi) );
|
|
// Path is file path only in Ansi ??
|
|
if (PathIsFilePath(szTmpPath) && PathFileExists(szTmpPath) )
|
|
bCheckForAssoc = TRUE;
|
|
}
|
|
else
|
|
if (GetUrlScheme(m_pszURL) == URL_SCHEME_FILE )
|
|
{
|
|
HRESULT hr = S_FALSE;
|
|
TCHAR szQualifiedUrl[MAX_URL_STRING];
|
|
DWORD cchSize = ARRAYSIZE(szQualifiedUrl);
|
|
hr = (ParseURLFromOutsideSource(m_pszURL, szQualifiedUrl, &cchSize, NULL) ? S_OK : E_FAIL);
|
|
if (EVAL(SUCCEEDED(hr)))
|
|
{
|
|
cchSize = ARRAYSIZE(szTmpPath);
|
|
|
|
hr = PathCreateFromUrl(szQualifiedUrl, szTmpPath, &cchSize, 0);
|
|
if (EVAL(SUCCEEDED(hr)) && PathFileExists(szTmpPath))
|
|
bCheckForAssoc = TRUE;
|
|
}
|
|
}
|
|
|
|
if (bCheckForAssoc)
|
|
{
|
|
// FileHasProperAssociation returns true for all known
|
|
// file types ( even directories )
|
|
DWORD cch;
|
|
if (!PathIsExe( szTmpPath )
|
|
&& !FileHasProperAssociation(szTmpPath))
|
|
{
|
|
MLShellMessageBox(m_hwnd,
|
|
MAKEINTRESOURCE(IDS_SHURL_ERR_NOASSOC),
|
|
MAKEINTRESOURCE(IDS_SHURL_ERR_TITLE),
|
|
(MB_OK | MB_ICONERROR));
|
|
return E_FAIL;
|
|
}
|
|
}
|
|
|
|
#endif
|
|
|
|
// Is the following true: 1) The caller wants other browsers to be able to handle the URLs,
|
|
// 2) The ShellUrl is a Web Url, and 3) IE doesn't own HTML files.
|
|
// If all of these are true, then we will just ShellExec() the URL String so the
|
|
// default handler can handle it.
|
|
// Also if the user wants us to browse in a new process and we are currently in the shell process,
|
|
// we will launch IE to handle the url.
|
|
|
|
if ((IsFlagSet(dwExecFlags, SHURL_EXECFLAGS_DONTFORCEIE) && IsWebUrl() && !IsIEDefaultBrowser())
|
|
#ifdef BROWSENEWPROCESS_STRICT // "Nav in new process" has become "Launch in new process", so this is no longer needed
|
|
|| (IsWebUrl() && IsBrowseNewProcessAndExplorer())
|
|
#endif
|
|
)
|
|
{
|
|
hr = _UrlShellExec();
|
|
ASSERT(S_OK == hr);
|
|
}
|
|
|
|
if ((S_OK != hr) && m_pidl && _CanUseAdvParsing())
|
|
{
|
|
// We will only Shell Exec it if:
|
|
// 1. We want to Force IE (over other web browsers) and it's not browsable, even by non-default owners.
|
|
// 2. It's not browsable by default owners.
|
|
if (!ILIsBrowsable(m_pidl, NULL))
|
|
{
|
|
if (pfDidShellExec)
|
|
*pfDidShellExec = TRUE;
|
|
|
|
DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: Execute() Going to _PidlShellExec(>%s<)", Dbg_PidlStr(m_pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
|
|
// If NULL == m_pidl, then the String will be used.
|
|
hr = _PidlShellExec(m_pidl, ulShellExecFMask);
|
|
}
|
|
}
|
|
|
|
if (S_OK != hr)
|
|
{
|
|
VARIANT vFlags = {0};
|
|
|
|
vFlags.vt = VT_I4;
|
|
vFlags.lVal = navAllowAutosearch;
|
|
|
|
if (pfDidShellExec)
|
|
*pfDidShellExec = FALSE;
|
|
|
|
// We prefer pidls, thank you
|
|
if (m_pidl)
|
|
{
|
|
DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: Execute() Going to pbp->NavigateToPIDL(>%s<)", Dbg_PidlStr(m_pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
|
|
hr = pbp->NavigateToPIDL(m_pidl);
|
|
}
|
|
else
|
|
{
|
|
ASSERT(m_pszURL);
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: Execute() Going to pbp->NavigateToURL(%s)", m_pszURL);
|
|
#ifdef UNICODE
|
|
hr = pbp->NavigateToURL(m_pszURL, &vFlags);
|
|
#else
|
|
WCHAR wszURL[MAX_URL_STRING];
|
|
SHTCharToUnicode(m_pszURL, wszURL, ARRAYSIZE(wszURL));
|
|
hr = pbp->NavigateToURL(wszURL, &vFlags);
|
|
#endif
|
|
}
|
|
|
|
VariantClearLazy(&vFlags);
|
|
}
|
|
|
|
#ifdef UNIX
|
|
// PidlExec failed or not done at all.
|
|
// Before passing it for navigation check if it is a shell url
|
|
// if so, execute it.
|
|
// The above comment is nolonger true. This code is moved here
|
|
// from before the navigate to fix attempt to createprocess problem
|
|
if (S_OK != hr && m_pszURL && IsShellUrl( m_pszURL, TRUE ) )
|
|
{
|
|
if (pfDidShellExec)
|
|
*pfDidShellExec = TRUE;
|
|
|
|
hr = _UrlShellExec();
|
|
ASSERT(S_OK == hr);
|
|
}
|
|
#endif
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _PidlShellExec
|
|
|
|
PARAMETERS
|
|
pidl - The Pidl to execute.
|
|
|
|
DESCRIPTION:
|
|
This function will call ShellExecEx() on the
|
|
pidl specified. It will also fill in the Current
|
|
Working Directory and Command Line Arguments if there
|
|
are any.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_PidlShellExec(LPCITEMIDLIST pidl, ULONG ulShellExecFMask)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
SHELLEXECUTEINFO sei = {0};
|
|
|
|
ASSERT(IS_VALID_PIDL(pidl));
|
|
|
|
DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _PidlShellExec() Going to execute pidl=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
|
|
|
|
if (m_pidlWorkingDir)
|
|
{
|
|
// note, this must be MAX_URL_STRING since IEGetDisplayName can return a URL.
|
|
WCHAR szCWD[MAX_URL_STRING];
|
|
|
|
IEGetDisplayName(m_pidlWorkingDir, szCWD, SHGDN_FORPARSING);
|
|
if (PathIsFilePath(szCWD))
|
|
{
|
|
sei.lpDirectory = szCWD;
|
|
}
|
|
}
|
|
/**** TODO: Get the Current Working Directory of top most window
|
|
if (!sei.lpDirectory || !sei.lpDirectory[0])
|
|
{
|
|
GetCurrentDirectory(SIZECHARS(szCurrWorkDir), szCurrWorkDir);
|
|
sei.lpDirectory = szCurrWorkDir;
|
|
}
|
|
*****/
|
|
|
|
sei.cbSize = sizeof(SHELLEXECUTEINFO);
|
|
sei.lpIDList = (LPVOID) pidl;
|
|
sei.lpParameters = m_pszArgs;
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
sei.fMask = SEE_MASK_FLAG_NO_UI | (pidl ? SEE_MASK_INVOKEIDLIST : 0) | ulShellExecFMask;
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _PidlShellExec() Cmd=>%s<, Args=>%s<, WorkDir=>%s<",
|
|
GEN_DEBUGSTR(sei.lpFile), GEN_DEBUGSTR(sei.lpParameters), GEN_DEBUGSTR(sei.lpDirectory));
|
|
|
|
if (ShellExecuteEx(&sei))
|
|
hr = S_OK;
|
|
else
|
|
{
|
|
#ifdef DEBUG
|
|
DWORD dwGetLastError = GetLastError();
|
|
TraceMsg(TF_ERROR, "ShellUrl: _PidlShellExec() ShellExecuteEx() failed for this item. Cmd=>%s<; dwGetLastError=%lx", GEN_DEBUGSTR(sei.lpParameters), dwGetLastError);
|
|
#endif // DEBUG
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _UrlShellExec
|
|
|
|
DESCRIPTION:
|
|
This function will call ShellExecEx() on the
|
|
URL. This is so other popular browsers can handle
|
|
the URL if they own HTML and other web files.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_UrlShellExec(void)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
SHELLEXECUTEINFO sei = {0};
|
|
|
|
ASSERT(m_pszURL);
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _UrlShellExec() Going to execute URL=>%s<", m_pszURL);
|
|
|
|
sei.cbSize = sizeof(sei);
|
|
sei.lpFile = m_pszURL;
|
|
sei.nShow = SW_SHOWNORMAL;
|
|
sei.fMask = SEE_MASK_FLAG_NO_UI;
|
|
|
|
if (m_pszURL && ShellExecuteEx(&sei))
|
|
hr = S_OK;
|
|
else
|
|
hr = E_FAIL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
// The following function is identical to ParseURLFromOutsideSource except that it
|
|
// enables autocorrect and sets pbWasCorrected to TRUE if the string was corrected
|
|
BOOL CShellUrl::_ParseURLFromOutsideSource
|
|
(
|
|
LPCWSTR psz,
|
|
LPWSTR pszOut,
|
|
LPDWORD pcchOut,
|
|
LPBOOL pbWasSearchURL, // if converted to a search string
|
|
LPBOOL pbWasCorrected // if url was autocorrected
|
|
)
|
|
{
|
|
// This is our hardest case. Users and outside applications might
|
|
// type fully-escaped, partially-escaped, or unescaped URLs at us.
|
|
// We need to handle all these correctly. This API will attempt to
|
|
// determine what sort of URL we've got, and provide us a returned URL
|
|
// that is guaranteed to be FULLY escaped.
|
|
|
|
IURLQualify(psz, UQF_DEFAULT | UQF_AUTOCORRECT, pszOut, pbWasSearchURL, pbWasCorrected);
|
|
|
|
//
|
|
// Go ahead and canonicalize this appropriately
|
|
//
|
|
if (FAILED(UrlCanonicalize(pszOut, pszOut, pcchOut, URL_ESCAPE_SPACES_ONLY)))
|
|
{
|
|
//
|
|
// we cant resize from here.
|
|
// NOTE UrlCan will return E_POINTER if it is an insufficient buffer
|
|
//
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
#ifdef UNICODE
|
|
HRESULT CShellUrl::ParseFromOutsideSource(LPCSTR pcszUrlIn, DWORD dwParseFlags, PBOOL pfWasCorrected)
|
|
{
|
|
WCHAR wzUrl[MAX_URL_STRING];
|
|
|
|
SHAnsiToUnicode(pcszUrlIn, wzUrl, ARRAYSIZE(wzUrl));
|
|
return ParseFromOutsideSource(wzUrl, dwParseFlags, pfWasCorrected);
|
|
}
|
|
#endif // UNICODE
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _TryQuickParse
|
|
|
|
PARAMETERS
|
|
pcszUrlIn - String to parse.
|
|
dwParseFlags - Flags to modify parsing. (Defined in iedev\inc\shlobj.w)
|
|
|
|
DESCRIPTION:
|
|
We prefer to call g_psfDesktop->ParseDisplayName()
|
|
and have it do the parse really quickly and without
|
|
enumerating the name space. We need this for things
|
|
that are parsed but not enumerated, which includes:
|
|
a) hidden files, b) other.
|
|
|
|
However, we need to not parse URLs if the caller
|
|
doesn't want to accept them.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_TryQuickParse(LPCTSTR pszUrl, DWORD dwParseFlags)
|
|
{
|
|
HRESULT hr = E_FAIL; // E_FAIL means we don't know yet.
|
|
int nScheme = GetUrlScheme(pszUrl);
|
|
|
|
// Don't parse unknown schemes because we may
|
|
// want to "AutoCorrect" them later.
|
|
if (URL_SCHEME_UNKNOWN != nScheme)
|
|
{
|
|
if ((dwParseFlags & SHURL_FLAGS_NOWEB) &&
|
|
(URL_SCHEME_INVALID != nScheme) &&
|
|
(URL_SCHEME_UNKNOWN != nScheme) &&
|
|
(URL_SCHEME_MK != nScheme) &&
|
|
(URL_SCHEME_SHELL != nScheme) &&
|
|
(URL_SCHEME_LOCAL != nScheme) &&
|
|
(URL_SCHEME_RES != nScheme) &&
|
|
(URL_SCHEME_ABOUT != nScheme))
|
|
{
|
|
// Skip parsing this because it's a web item, and
|
|
// the caller wants to filter those out.
|
|
}
|
|
else
|
|
{
|
|
hr = IEParseDisplayNameWithBCW(CP_ACP, pszUrl, NULL, &m_pidl);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: ParseFromOutsideSource
|
|
|
|
PARAMETERS
|
|
pcszUrlIn - String to parse.
|
|
dwParseFlags - Flags to modify parsing. (Defined in iedev\inc\shlobj.w)
|
|
pfWasCorrected - [out] if url was autocorrected (can be null)
|
|
|
|
DESCRIPTION:
|
|
Convert a string to a fully qualified shell url. Parsing
|
|
falls into one of the following categories:
|
|
|
|
1. If the URL starts with "\\", check if it's a UNC Path.
|
|
2. If the URL starts something that appears to indicate that it starts
|
|
from the root of the shell name space (Desktop), then check if it
|
|
is an absolute ShellUrl.
|
|
(Only do #3 and #4 if #2 was false)
|
|
3. Check if the string is relative to the Current Working Directory.
|
|
4. Check if the string is relative to one of the items in the
|
|
"Shell Path".
|
|
5. Check if the string is in the system's AppPath or DOS Path.
|
|
6. Check if this is a URL to Navigate to. This call will pretty much
|
|
always succeeded, because it will accept anything as an AutoSearch
|
|
URL.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::ParseFromOutsideSource(LPCTSTR pcszUrlIn, DWORD dwParseFlags, PBOOL pfWasCorrected)
|
|
{
|
|
HRESULT hr = E_FAIL; // E_FAIL means we don't know yet.
|
|
TCHAR szUrlExpanded[MAX_URL_STRING];
|
|
LPTSTR pszUrlInMod = (LPTSTR) szUrlExpanded; // For iteration only
|
|
LPTSTR pszErrorURL = NULL;
|
|
BOOL fPossibleWebUrl = FALSE;
|
|
int nScheme;
|
|
BOOL fDisable = SHRestricted(REST_NORUN);
|
|
m_dwFlags = dwParseFlags;
|
|
|
|
if (pfWasCorrected)
|
|
*pfWasCorrected = FALSE;
|
|
|
|
if (!pcszUrlIn[0])
|
|
return E_FAIL;
|
|
|
|
if (!StrCmpNIW(WZ_RADIO_PROTOCOL, pcszUrlIn, ARRAYSIZE(WZ_RADIO_PROTOCOL)-1))
|
|
{
|
|
// We need to reroute vnd.ms.radio: urls to the regular player, since we don't support the radio bar anymore.
|
|
// (Media bar or the external player)
|
|
StrCpyN(szUrlExpanded, pcszUrlIn+ARRAYSIZE(WZ_RADIO_PROTOCOL)-1, SIZECHARS(szUrlExpanded));
|
|
}
|
|
else
|
|
{
|
|
SHExpandEnvironmentStrings(pcszUrlIn, szUrlExpanded, SIZECHARS(szUrlExpanded));
|
|
}
|
|
|
|
PathRemoveBlanks(pszUrlInMod);
|
|
|
|
Reset(); // Empty info because we will fill it in if successful or leave empty if we fail.
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: ParseFromOutsideSource() Begin. pszUrlInMod=%s", pszUrlInMod);
|
|
// The display Name will be exactly what the user entered.
|
|
Str_SetPtr(&m_pszDisplayName, pszUrlInMod);
|
|
|
|
nScheme = GetUrlScheme(pszUrlInMod);
|
|
if ((URL_SCHEME_FILE != nScheme) || !fDisable) // Don't parse FILE: URLs if Start->Run is disabled.
|
|
{
|
|
// For HTTP and FTP we can make a few minor corrections
|
|
if (IsFlagSet(dwParseFlags, SHURL_FLAGS_AUTOCORRECT) &&
|
|
(URL_SCHEME_HTTP == nScheme || URL_SCHEME_FTP == nScheme || URL_SCHEME_HTTPS == nScheme))
|
|
{
|
|
if (S_OK == UrlFixupW(szUrlExpanded, szUrlExpanded, ARRAYSIZE(szUrlExpanded)) &&
|
|
pfWasCorrected)
|
|
{
|
|
*pfWasCorrected = TRUE;
|
|
}
|
|
}
|
|
|
|
hr = _TryQuickParse(szUrlExpanded, dwParseFlags);
|
|
if (FAILED(hr))
|
|
{
|
|
// Does this string refer to something in the shell namespace that is
|
|
// not a standard URL AND can we do shell namespace parsing AND
|
|
// can we use advanced parsing on it?
|
|
if (((URL_SCHEME_UNKNOWN == nScheme) ||
|
|
(URL_SCHEME_SHELL == nScheme) ||
|
|
(URL_SCHEME_INVALID == nScheme)) &&
|
|
!(SHURL_FLAGS_NOSNS & dwParseFlags) && _CanUseAdvParsing())
|
|
{
|
|
fPossibleWebUrl = TRUE;
|
|
|
|
// Yes; is this URL absolute (e.g., "\foo" or "Desktop\foo")?
|
|
if (IS_SHELL_SEPARATOR(pszUrlInMod[0]) ||
|
|
(S_OK == StrCmpIWithRoot(pszUrlInMod, FALSE, &m_pstrRoot)))
|
|
{
|
|
// Yes
|
|
|
|
// CASE #1.
|
|
// It starts with "\\", so it's probably a UNC,
|
|
// so _ParseUNC() will call _ParseRelativePidl() with the Network
|
|
// Neighborhood PIDL as the relative location. This is needed
|
|
// because commands like this "\\bryanst2\public\program.exe Arg1 Arg2"
|
|
// that need to be shell executed.
|
|
if (PathIsUNC(pszUrlInMod))
|
|
{
|
|
hr = _ParseUNC(pszUrlInMod, &fPossibleWebUrl, dwParseFlags, FALSE);
|
|
// If we got this far, don't pass off to Navigation if _ParseUNC() failed.
|
|
fPossibleWebUrl = FALSE;
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
if (IS_SHELL_SEPARATOR(pszUrlInMod[0]))
|
|
{
|
|
pszErrorURL = pszUrlInMod; // We want to keep the '\' for the error message.
|
|
pszUrlInMod++; // Skip past '\'.
|
|
}
|
|
|
|
// See if we need to advance past a "Desktop".
|
|
if (S_OK == StrCmpIWithRoot(pszUrlInMod, FALSE, &m_pstrRoot))
|
|
{
|
|
pszUrlInMod += lstrlen(m_pstrRoot);
|
|
if (IS_SHELL_SEPARATOR(pszUrlInMod[0]))
|
|
pszUrlInMod++;
|
|
if (!pszUrlInMod[0])
|
|
{
|
|
// The only thing the user entered was [...]"desktop"[\]
|
|
// so just clone the Root pidl.
|
|
return _SetPidl(&s_idlNULL);
|
|
}
|
|
}
|
|
|
|
// CASE #2. Passing NULL indicates that it should test relative
|
|
// to the root.
|
|
hr = _ParseRelativePidl(pszUrlInMod, &fPossibleWebUrl, dwParseFlags, NULL, FALSE, FALSE);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No; it is relative
|
|
int nPathCount = 0;
|
|
int nPathIndex;
|
|
|
|
if (m_hdpaPath)
|
|
nPathCount = DPA_GetPtrCount(m_hdpaPath);
|
|
|
|
// CASE #3. Parse relative to the Current Working Directory.
|
|
// Only valid if this object's ::SetCurrentWorkingDir()
|
|
// method was called.
|
|
if (m_pidlWorkingDir)
|
|
{
|
|
hr = _ParseRelativePidl(pszUrlInMod, &fPossibleWebUrl, dwParseFlags, m_pidlWorkingDir, TRUE, TRUE);
|
|
#ifdef FEATURE_WILDCARD_SUPPORT
|
|
if (FAILED(hr) && m_pidlWorkingDir &&
|
|
!StrChr(pszUrlInMod, CH_SEPARATOR) && !StrChr(pszUrlInMod, CH_FILESEPARATOR))
|
|
{
|
|
LPTSTR pszWildCard = StrChr(pszUrlInMod, CH_ASTRISK);
|
|
if (!pszWildCard)
|
|
pszWildCard = StrChr(pszUrlInMod, CH_QUESTIONMARK);
|
|
|
|
if (pszWildCard)
|
|
{
|
|
IOleWindow * pow;
|
|
m_pidlWorkingDir
|
|
}
|
|
}
|
|
#endif // FEATURE_WILDCARD_SUPPORT
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
//
|
|
// Check if the place we are navigating to is the same as the current
|
|
// working directory. If so then there is a good chance that the user just
|
|
// pressed the enter key / go button in the addressbar and we should simply
|
|
// refresh the current directory.
|
|
//
|
|
WCHAR szCurrentDir[MAX_URL_STRING];
|
|
HRESULT hr2 = IEGetNameAndFlags(m_pidlWorkingDir, SHGDN_FORPARSING | SHGDN_FORADDRESSBAR, szCurrentDir, ARRAYSIZE(szCurrentDir), NULL);
|
|
if (FAILED(hr2))
|
|
{
|
|
// Sometimes SHGDN_FORPARSING fails and the addressbar then tries SHGDN_NORMAL
|
|
hr2 = IEGetNameAndFlags(m_pidlWorkingDir, SHGDN_NORMAL | SHGDN_FORADDRESSBAR, szCurrentDir, ARRAYSIZE(szCurrentDir), NULL);
|
|
}
|
|
|
|
if (SUCCEEDED(hr2))
|
|
{
|
|
if (0 == StrCmpI(pszUrlInMod, szCurrentDir))
|
|
{
|
|
// It matches so stay in the current working directory
|
|
_SetPidl(m_pidlWorkingDir);
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// TODO: Get the Current Working Directory of the top most window.
|
|
// hr = _ParseRelativePidl(pszUrlInMod, &fPossibleWebUrl, dwParseFlags, pshurlCWD, TRUE, TRUE);
|
|
}
|
|
|
|
// CASE #4. Parse relative to the entries in the "Shell Path".
|
|
// Only valid if this object's ::AddPath() method was
|
|
// called at least once.
|
|
for (nPathIndex = 0; FAILED(hr) && nPathIndex < nPathCount; nPathIndex++)
|
|
{
|
|
LPITEMIDLIST pidlCurrPath = (LPITEMIDLIST) DPA_GetPtr(m_hdpaPath, nPathIndex);
|
|
|
|
if (EVAL(pidlCurrPath))
|
|
{
|
|
ASSERT(IS_VALID_PIDL(pidlCurrPath));
|
|
hr = _ParseRelativePidl(pszUrlInMod, &fPossibleWebUrl, dwParseFlags, pidlCurrPath, FALSE, FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
// CASE #5. We need to see if the beginning of the string matches
|
|
// the entry in the AppPaths or DOS Path
|
|
|
|
if (FAILED(hr) && IsFlagClear(dwParseFlags, SHURL_FLAGS_NOPATHSEARCH))
|
|
hr = _QualifyFromPath(pszUrlInMod, dwParseFlags);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (URL_SCHEME_FILE != nScheme)
|
|
fPossibleWebUrl = TRUE;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (FAILED(hr) && !fPossibleWebUrl && !fDisable)
|
|
{
|
|
// Did the caller want to suppress UI (Error Messages)
|
|
if (IsFlagClear(dwParseFlags, SHURL_FLAGS_NOUI))
|
|
{
|
|
if (!pszErrorURL)
|
|
pszErrorURL = pszUrlInMod;
|
|
ASSERT(pszErrorURL);
|
|
|
|
// We were able to parse part of it, but failed parsing the second or
|
|
// later segment. This means we need to inform the user of their
|
|
// misspelling. They can force AutoSearch with "go xxx" or "? xxx"
|
|
// if they are trying to AutoSearch something that appears in their
|
|
// Shell Name Space.
|
|
MLShellMessageBox(m_hwnd, MAKEINTRESOURCE(IDS_SHURL_ERR_PARSE_FAILED),
|
|
MAKEINTRESOURCE(IDS_SHURL_ERR_TITLE),
|
|
(MB_OK | MB_ICONERROR), pszErrorURL);
|
|
}
|
|
}
|
|
else if (S_OK != hr)
|
|
{
|
|
if (!(dwParseFlags & SHURL_FLAGS_NOWEB))
|
|
{
|
|
TCHAR szQualifiedUrl[MAX_URL_STRING];
|
|
DWORD cchSize = SIZECHARS(szQualifiedUrl);
|
|
|
|
SHExpandEnvironmentStrings(pcszUrlIn, szUrlExpanded, SIZECHARS(szUrlExpanded));
|
|
PathRemoveBlanks(szUrlExpanded);
|
|
|
|
// Unintialized szQualifiedUrl causes junk characters to appear on
|
|
// addressbar and causes registry corruption on UNIX.
|
|
szQualifiedUrl[0] = TEXT('\0');
|
|
|
|
// CASE #6. Just check if this is a URL to Navigate to. This call will
|
|
// pretty much always succeeded, because it will accept
|
|
// anything as a search URL.
|
|
if (IsFlagSet(dwParseFlags, SHURL_FLAGS_AUTOCORRECT))
|
|
{
|
|
hr = (_ParseURLFromOutsideSource(szUrlExpanded, szQualifiedUrl, &cchSize, NULL, pfWasCorrected) ? S_OK : E_FAIL);
|
|
}
|
|
else
|
|
{
|
|
hr = (ParseURLFromOutsideSource(szUrlExpanded, szQualifiedUrl, &cchSize, NULL) ? S_OK : E_FAIL);
|
|
}
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
SetUrl(szQualifiedUrl, GENTYPE_FROMURL);
|
|
Str_SetPtr(&m_pszDisplayName, szQualifiedUrl); // The display Name will be exactly what the user entered.
|
|
}
|
|
|
|
ASSERT(!m_pidl);
|
|
if (fDisable && SUCCEEDED(hr))
|
|
{
|
|
nScheme = GetUrlScheme(szQualifiedUrl);
|
|
// We will allow all but the following schemes:
|
|
if ((URL_SCHEME_SHELL != nScheme) &&
|
|
(URL_SCHEME_FILE != nScheme) &&
|
|
(URL_SCHEME_UNKNOWN != nScheme) &&
|
|
(URL_SCHEME_INVALID != nScheme))
|
|
{
|
|
fDisable = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fDisable && ((URL_SCHEME_FILE == nScheme) || (URL_SCHEME_INVALID == nScheme) || (URL_SCHEME_UNKNOWN == nScheme)))
|
|
{
|
|
if (IsFlagClear(dwParseFlags, SHURL_FLAGS_NOUI))
|
|
{
|
|
MLShellMessageBox(m_hwnd, MAKEINTRESOURCE(IDS_SHURL_ERR_PARSE_NOTALLOWED),
|
|
MAKEINTRESOURCE(IDS_SHURL_ERR_TITLE),
|
|
(MB_OK | MB_ICONERROR), pszUrlInMod);
|
|
}
|
|
hr = E_ACCESSDENIED;
|
|
Reset(); // Just in case the caller ignores the return value.
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _QualifyFromPath
|
|
|
|
PARAMETERS:
|
|
pcszFilePathIn - String that may be in the Path.
|
|
dwFlags - Parse Flags, not currently used.
|
|
|
|
DESCRIPTION:
|
|
This function will call _QualifyFromAppPath()
|
|
to see if the item exists in the AppPaths. If not,
|
|
it will check in the DOS Path Env. variable with a
|
|
call to _QualifyFromDOSPath().
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_QualifyFromPath(LPCTSTR pcszFilePathIn, DWORD dwFlags)
|
|
{
|
|
HRESULT hr = _QualifyFromAppPath(pcszFilePathIn, dwFlags);
|
|
|
|
if (FAILED(hr))
|
|
hr = _QualifyFromDOSPath(pcszFilePathIn, dwFlags);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _QualifyFromDOSPath
|
|
|
|
PARAMETERS:
|
|
pcszFilePathIn - String that may be in the Path.
|
|
dwFlags - Parse Flags, not currently used.
|
|
|
|
DESCRIPTION:
|
|
See if pcszFilePathIn exists in the DOS Path Env
|
|
variable. If so, set the ShellUrl to that location.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_QualifyFromDOSPath(LPCTSTR pcszFilePathIn, DWORD dwFlags)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
TCHAR szPath[MAX_PATH];
|
|
LPTSTR pszEnd = (LPTSTR) pcszFilePathIn;
|
|
BOOL fContinue = TRUE;
|
|
|
|
do
|
|
{
|
|
hr = _GetNextPossibleFullPath(pcszFilePathIn, &pszEnd, szPath, SIZECHARS(szPath), &fContinue);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (PathFindOnPathEx(szPath, NULL, (PFOPEX_OPTIONAL | PFOPEX_COM | PFOPEX_BAT | PFOPEX_PIF | PFOPEX_EXE)))
|
|
{
|
|
_GeneratePidl(szPath, GENTYPE_FROMPATH);
|
|
if (!ILIsFileSysFolder(m_pidl))
|
|
{
|
|
Str_SetPtr(&m_pszArgs, pszEnd); // Set aside Args
|
|
break;
|
|
}
|
|
}
|
|
if (fContinue)
|
|
pszEnd = CharNext(pszEnd);
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
while (FAILED(hr) && fContinue);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _QualifyFromAppPath
|
|
|
|
PARAMETERS:
|
|
pcszFilePathIn - String that may be in the Path.
|
|
dwFlags - Parse Flags, not currently used.
|
|
|
|
DESCRIPTION:
|
|
See if pcszFilePathIn exists in the AppPaths
|
|
Registry Section. If so, set the ShellUrl to that location.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_QualifyFromAppPath(LPCTSTR pcszFilePathIn, DWORD dwFlags)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
TCHAR szFileName[MAX_PATH];
|
|
TCHAR szRegKey[MAX_PATH];
|
|
DWORD dwType;
|
|
DWORD cbData = sizeof(szFileName);
|
|
DWORD cchNewPathSize;
|
|
|
|
StrCpyN(szFileName, pcszFilePathIn, SIZECHARS(szFileName));
|
|
PathRemoveArgs(szFileName); // Get Rid of Args (Will be added later)
|
|
cchNewPathSize = lstrlen(szFileName); // Get size so we known where to find args in pcszFilePathIn
|
|
PathAddExtension(szFileName, TEXT(".exe")); // Add extension if needed.
|
|
|
|
wnsprintf(szRegKey, ARRAYSIZE(szRegKey), TEXT("%s\\%s"), STR_REGKEY_APPPATH, szFileName);
|
|
if (NOERROR == SHGetValue(HKEY_LOCAL_MACHINE, szRegKey, TEXT(""), &dwType, (LPVOID) szFileName, &cbData))
|
|
{
|
|
// 1. Create Pidl from String.
|
|
hr = _GeneratePidl(szFileName, GENTYPE_FROMPATH);
|
|
|
|
// 2. Set aside Args
|
|
ASSERT((DWORD)lstrlen(pcszFilePathIn) >= cchNewPathSize);
|
|
Str_SetPtr(&m_pszArgs, &(pcszFilePathIn[cchNewPathSize]));
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _ParseUNC
|
|
|
|
PARAMETERS:
|
|
pcszUrlIn - URL, which can be a UNC path.
|
|
pfPossibleWebUrl - Set to FALSE if we find that the user has attempted
|
|
to enter a Shell Url or File url but misspelled one
|
|
of the segments.
|
|
dwFlags - Parse Flags
|
|
fQualifyDispName - If TRUE when we known that we need to force the
|
|
URL to be fully qualified if we bind to the destination.
|
|
This is needed because we are using state information to
|
|
find the destination URL and that state information won't
|
|
be available later.
|
|
|
|
DESCRIPTION:
|
|
See if the URL passed in is a valid path
|
|
relative to "SHELL:Desktop/Network Neighborhood".
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_ParseUNC(LPCTSTR pcszUrlIn, BOOL * pfPossibleWebUrl, DWORD dwFlags, BOOL fQualifyDispName)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
LPITEMIDLIST pidlNN = NULL;
|
|
|
|
SHGetSpecialFolderLocation(NULL, CSIDL_NETWORK, &pidlNN); // Get Pidl for "Network Neighborhood"
|
|
if (pidlNN)
|
|
{
|
|
hr = _ParseRelativePidl(pcszUrlIn, pfPossibleWebUrl, dwFlags, pidlNN, FALSE, fQualifyDispName);
|
|
ILFree(pidlNN);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _ParseSeparator
|
|
|
|
PARAMETERS:
|
|
pidl - PIDL to ISF that has been parsed so far.
|
|
pcszSeg - Str of rest of Url to parse.
|
|
pfPossibleWebUrl - Set to FALSE if we know that the user attempted
|
|
but failed to enter a correct Shell Url.
|
|
fQualifyDispName - If TRUE when we known that we need to force the
|
|
URL to be fully qualified if we bind to the
|
|
destination. This is needed because we are using
|
|
state information to find the destination URL and
|
|
that state information won't be available later.
|
|
|
|
DESCRIPTION:
|
|
This function is called after at least one
|
|
segment in the SHELL URL has bound to a valid
|
|
Shell Item/Folder (i.e., ITEMID). It is called
|
|
each time a segment in the Shell Url binds to a PIDL.
|
|
It will then evaluate the rest of the string and
|
|
determine if:
|
|
1. The URL has been completely parsed
|
|
and is valid. This will include getting
|
|
the command line arguments if appropriate.
|
|
2. More segments in the URL exist and ::_ParseNextSegment()
|
|
needs to be called to continue the recursive parsing
|
|
of the URL.
|
|
3. The rest of the URL indicates that it's an invalid url.
|
|
|
|
This function is always called by ::_ParseNextSegment() and basically
|
|
decides if it wants to continue the recursion by calling back into
|
|
::_ParseNextSegment() or not. Recursion is used because it's necessary
|
|
to back out of parsing something and go down a path if we received
|
|
a false positive.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_ParseSeparator(LPCITEMIDLIST pidl, LPCTSTR pcszSeg, BOOL * pfPossibleWebUrl, BOOL fAllowRelative, BOOL fQualifyDispName)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
BOOL fIgnoreArgs = FALSE;
|
|
|
|
ASSERT(pidl && IS_VALID_PIDL(pidl));
|
|
|
|
// Does anything follow this separator?
|
|
if ((CH_FRAGMENT == pcszSeg[0]) || (IS_SHELL_SEPARATOR(pcszSeg[0]) && pcszSeg[1]))
|
|
{
|
|
// Yes, continue parsing recursively.
|
|
|
|
// Do we need to skip the '/' or '\' separator?
|
|
if (CH_FRAGMENT != pcszSeg[0])
|
|
pcszSeg++; // Skip separator
|
|
|
|
hr = _ParseNextSegment(pidl, pcszSeg, pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
|
|
DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _ParseSeparator() Current Level pidl=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
|
|
|
|
if (FAILED(hr) && pfPossibleWebUrl)
|
|
{
|
|
*pfPossibleWebUrl = FALSE;
|
|
// We bound to at least one level when parsing, so don't do a web search because
|
|
// of a failure.
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No, we will see if we have reached a valid Shell Item.
|
|
|
|
// Is the remaining string args?
|
|
if (CH_SPACE == pcszSeg[0])
|
|
{
|
|
// If there are still chars left in the string, we need to
|
|
// verify the first one is a space to indicate Command line args.
|
|
// Also, we need to make sure the PIDL isn't browsable because browsable
|
|
// Shell folders/items don't take Cmd Line Args.
|
|
|
|
if (ILIsBrowsable(pidl, NULL))
|
|
{
|
|
// No
|
|
//
|
|
// The remaining chars cannot be Command Line Args if the PIDL
|
|
// doesn't point to something that is shell executable. This
|
|
// case actually happens often.
|
|
// Example: (\\bryanst\... and \\bryanst2\.. both exist and
|
|
// user enters \\bryanst2\... but parsing attempts
|
|
// to use \\bryanst because it was found first. This
|
|
// will cause recursion to crawl back up the stack and try \\bryanst2.
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
else if (pcszSeg[0])
|
|
{
|
|
// No
|
|
// The only time we allow a char after a folder segment is if it is a Shell Separator
|
|
// Example: "E:\dir1\"
|
|
|
|
if (IS_SHELL_SEPARATOR(*pcszSeg) && 0 == pcszSeg[1])
|
|
fIgnoreArgs = TRUE;
|
|
else
|
|
hr = E_FAIL; // Invalid because there is more to be parsed.
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _ParseSeparator() Parsing Finished. pidl=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
|
|
_SetPidl(pidl);
|
|
|
|
if (!fIgnoreArgs && pcszSeg[0])
|
|
Str_SetPtr(&m_pszArgs, pcszSeg);
|
|
|
|
if (fQualifyDispName)
|
|
_GenDispNameFromPidl(pidl, pcszSeg);
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//
|
|
// Returns TRUE is the pidl is a network server
|
|
//
|
|
BOOL _IsNetworkServer(LPCITEMIDLIST pidl)
|
|
{
|
|
BOOL fRet = FALSE;
|
|
|
|
// First see if this is a network pidl
|
|
if (IsSpecialFolderChild(pidl, CSIDL_NETWORK, FALSE))
|
|
{
|
|
// See if it ends in a share name
|
|
WCHAR szUrl[MAX_URL_STRING];
|
|
HRESULT hr = IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szUrl, ARRAYSIZE(szUrl), NULL);
|
|
if (FAILED(hr))
|
|
{
|
|
// On non-integrated browsers SHGDN_FORPARSING may fail so try
|
|
// again without this flag. The preceeding back slashes will be
|
|
// missing so we add them ourselves
|
|
szUrl[0] = CH_FILESEPARATOR;
|
|
szUrl[1] = CH_FILESEPARATOR;
|
|
hr = IEGetNameAndFlags(pidl, SHGDN_NORMAL | SHGDN_FORADDRESSBAR, szUrl+2, ARRAYSIZE(szUrl)-2, NULL);
|
|
}
|
|
|
|
fRet = SUCCEEDED(hr) && PathIsUNCServer(szUrl);
|
|
}
|
|
return fRet;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _ParseNextSegment
|
|
|
|
PARAMETERS:
|
|
pidlParent - Fully Qualified PIDL to ISF to find next ITEMID in pcszStrToParse.
|
|
pcszStrToParse - pcszStrToParse will begin with either
|
|
a valid display name of a child ITEMID of pidlParent
|
|
or the Shell URL is invalid relative to pidlParent.
|
|
fAllowRelative - Should relative moves be allowed?
|
|
fQualifyDispName - If TRUE when we known that we need to force the
|
|
URL to be fully qualified if we bind to the destination.
|
|
This is needed because we are using state information to
|
|
find the destination URL and that state information won't
|
|
be available later.
|
|
|
|
DESCRIPTION/PERF:
|
|
This function exists to take the string (pcszStrToParse)
|
|
passed in and attempt to bind to a ITEMID which
|
|
has a DisplayName that matches the beginning of
|
|
pcszStrToParse. This function will check all the
|
|
ITEMIDs under the pidlParent section of the Shell
|
|
Name Space.
|
|
|
|
The only two exceptions to the above method is if
|
|
1) the string begins with "..", in which case, we
|
|
bind to the pidlParent's Parent ITEMID. - or -
|
|
2) The pidlParent passes the ::_IsFilePidl()
|
|
test and we are guaranteed the item is in the
|
|
File System or a UNC item. This will allow us
|
|
to call IShellFolder::ParseDisplayName() to
|
|
find the child ITEMID of pidlParent.
|
|
|
|
This function will iterate through the items under
|
|
pidlParent instead of call IShellFolder::ParseDisplayName
|
|
for two reasons: 1) The ::ParseDisplayName for "The Internet"
|
|
will accept any string because of AutoSearch, and
|
|
2) We never know the location of the end of one segment and
|
|
the beginning of the next segment in pcszStrToParse. This is
|
|
because DisplayNames for ISFs can contain almost any character.
|
|
|
|
If this function has successfully bind to a child ITEMID
|
|
of pidlParent, it will call ::_ParseSeparator() with the
|
|
rest of pcszStrToParse to parse. _ParseSeparator() will determine
|
|
if the end of the URL has been parsed or call back into this function
|
|
recursively to continue parsing segments. In the former case,
|
|
_ParseSeparator() will set this object's PIDL and arguments which
|
|
can be used later. In the latter case, the recursion stack will
|
|
unwind and my take a different path (Cases exists that require this).
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_ParseNextSegment(LPCITEMIDLIST pidlParent,
|
|
LPCTSTR pcszStrToParse, BOOL * pfPossibleWebUrl,
|
|
BOOL fAllowRelative, BOOL fQualifyDispName)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
if (!pidlParent || !pcszStrToParse)
|
|
return E_INVALIDARG;
|
|
|
|
// Is this ".."?
|
|
if (fAllowRelative && CH_DOT == pcszStrToParse[0] && CH_DOT == pcszStrToParse[1])
|
|
{
|
|
// Yes
|
|
LPITEMIDLIST pidl = ILClone(pidlParent);
|
|
if (pidl && !ILIsEmpty(pidl))
|
|
{
|
|
ILRemoveLastID(pidl); // pidl/psfFolder now point to the new shell item, which is the parent in this case.
|
|
DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _ParseNextSegment() Nav '..'. PIDL=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
|
|
|
|
// Parse the next segment or finish up if we reached the end
|
|
// (we're skipping the ".." here)
|
|
hr = _ParseSeparator(pidl, &(pcszStrToParse[2]), pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// No
|
|
LPTSTR pszNext = NULL; // Remove const because we will iterate only.
|
|
long i = 0;
|
|
|
|
// Can we parse this display name quickly?
|
|
if (!ILIsRooted(pidlParent) && _IsFilePidl(pidlParent) &&
|
|
|
|
// Quick way fails for shares right off of the network server
|
|
!_IsNetworkServer(pidlParent))
|
|
{
|
|
// Yes
|
|
TCHAR szParseChunk[MAX_PATH+1];
|
|
|
|
do
|
|
{
|
|
++i;
|
|
|
|
hr = _GetNextPossibleSegment(pcszStrToParse, &pszNext, szParseChunk, SIZECHARS(szParseChunk), TRUE);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = _QuickParse(pidlParent, szParseChunk, pszNext, pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
|
|
|
|
//
|
|
// Certain network shares like \\foo\Printers and "\\foo\Scheduled Tasks" will fail the if we
|
|
// combine the server and share in a segment. So we try parsing the server separately.
|
|
//
|
|
if ((S_OK != hr) && (i == 1) && PathIsUNCServerShare(szParseChunk))
|
|
{
|
|
pszNext = NULL;
|
|
hr = _GetNextPossibleSegment(pcszStrToParse, &pszNext, szParseChunk, SIZECHARS(szParseChunk), FALSE);
|
|
if (S_OK == hr)
|
|
{
|
|
hr = _QuickParse(pidlParent, szParseChunk, pszNext, pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
|
|
}
|
|
}
|
|
|
|
#ifdef FEATURE_SUPPORT_FRAGS_INFILEURLS
|
|
// Did we fail to parse the traditional way and the first char of this
|
|
// next chunk indicates it's probably a URL Fragment?
|
|
if (FAILED(hr) && (CH_FRAGMENT == pcszStrToParse[0]))
|
|
{
|
|
TCHAR szUrl[MAX_URL_STRING];
|
|
// Yes, so try parsing in another way that will work
|
|
// with URL fragments.
|
|
|
|
hr = ::IEGetDisplayName(pidlParent, szUrl, SHGDN_FORPARSING);
|
|
if (EVAL(SUCCEEDED(hr)))
|
|
{
|
|
TCHAR szFullUrl[MAX_URL_STRING];
|
|
DWORD cchFullUrlSize = ARRAYSIZE(szFullUrl);
|
|
|
|
hr = UrlCombine(szUrl, szParseChunk, szFullUrl, &cchFullUrlSize, 0);
|
|
if (EVAL(SUCCEEDED(hr)))
|
|
{
|
|
LPITEMIDLIST pidl = NULL;
|
|
|
|
hr = IEParseDisplayName(CP_ACP, szFullUrl, &pidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
_SetPidl(pidl);
|
|
|
|
if (fQualifyDispName)
|
|
_GenDispNameFromPidl(pidl, szFullUrl);
|
|
|
|
ILFree(pidl);
|
|
}
|
|
else
|
|
ASSERT(!pidl); // Verify IEParseDisplayName() didn't fail but return a pidl.
|
|
}
|
|
}
|
|
}
|
|
#endif // FEATURE_SUPPORT_FRAGS_INFILEURLS
|
|
}
|
|
}
|
|
while (FAILED(hr));
|
|
|
|
if (S_OK != hr)
|
|
hr = E_FAIL; // Not Found
|
|
}
|
|
else if (FAILED(hr))
|
|
{
|
|
// No; use the slow method
|
|
IShellFolder * psfFolder = NULL;
|
|
|
|
DWORD dwAttrib = SFGAO_FOLDER;
|
|
IEGetAttributesOf(pidlParent, &dwAttrib);
|
|
|
|
if (IsFlagSet(dwAttrib, SFGAO_FOLDER))
|
|
{
|
|
IEBindToObject(pidlParent, &psfFolder);
|
|
ASSERT(psfFolder);
|
|
}
|
|
|
|
if (psfFolder)
|
|
{
|
|
LPENUMIDLIST penumIDList = NULL;
|
|
HWND hwnd = _GetWindow();
|
|
|
|
// Is this an FTP Pidl?
|
|
if (IsFTPFolder(psfFolder))
|
|
{
|
|
// NT #274795: Yes so, we need to NULL out the hwnd to prevent
|
|
// displaying UI because enumerator of that folder may need to display
|
|
// UI (to collect passwords, etc.). This is not valid because pcszStrToParse
|
|
// may be an absolute path and psfFolder points to the current location which
|
|
// isn't valid. This should probaby be done for all IShellFolder::EnumObjects()
|
|
// calls, but it's too risky right before ship.
|
|
hwnd = NULL;
|
|
}
|
|
|
|
// Warning Docfind returns S_FALSE to indicate no enumerator and returns NULL..
|
|
if (S_OK == IShellFolder_EnumObjects(psfFolder, hwnd, SHCONTF_FOLDERS | SHCONTF_NONFOLDERS | SHCONTF_INCLUDEHIDDEN, &penumIDList))
|
|
{
|
|
LPITEMIDLIST pidlRelative; // NOT a FULLY Qualified Pidl
|
|
LPITEMIDLIST pidlResult; // PIDL after it has been made Fully Qualified
|
|
ULONG cFetched;
|
|
LPTSTR pszRemaining = NULL;
|
|
|
|
while (FAILED(hr) && NOERROR == penumIDList->Next(1, &pidlRelative, &cFetched) && cFetched)
|
|
{
|
|
// The user will have entered the name in one of the three formats and they need to be
|
|
// checked from the longest string to the smallest. This is necessary because the
|
|
// parser will check to see if the item's DisplayName is the first part of the user
|
|
// string.
|
|
//
|
|
// #1. (FORPARSING): This will be the full name.
|
|
// Example: razzle.lnk on desktop = D:\nt\public\tools\razzle.lnk.
|
|
// #2. (FORPARSING | SHGDN_INFOLDER): This will be only the full name w/Extension.
|
|
// Example: razzle.lnk on desktop = razzle.lnk
|
|
// #3. (SHGDN_INFOLDER): This will be the full name w/o extension if "Hide File Extensions for Known File Types" is on.
|
|
// Example: razzle.lnk on desktop = D:\nt\public\tools\razzle.lnk.
|
|
// The user may have entered the "SHGDN_FORPARSING" Display Name or the "SHGDN_INFOLDER", so we need
|
|
// to check both.
|
|
hr = _CheckItem(psfFolder, pidlParent, pidlRelative, &pidlResult, pcszStrToParse, &pszRemaining, SHGDN_FORPARSING);
|
|
if (FAILED(hr)) // Used for file items w/extensions. (Like razzle.lnk on the Desktop)
|
|
hr = _CheckItem(psfFolder, pidlParent, pidlRelative, &pidlResult, pcszStrToParse, &pszRemaining, SHGDN_FORPARSING | SHGDN_INFOLDER);
|
|
if (FAILED(hr))
|
|
hr = _CheckItem(psfFolder, pidlParent, pidlRelative, &pidlResult, pcszStrToParse, &pszRemaining, SHGDN_INFOLDER);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// See if the Display Name for a Drive ate the separator for the next segment.
|
|
if (_FixDriveDisplayName(pcszStrToParse, pszRemaining, pidlResult))
|
|
{
|
|
// FIX: "E:\dir1\dir2". We expent display names to not claim the '\' separator between
|
|
// names. The problem is that drive letters claim to be "E:\" instead
|
|
// of "E:". So, we need to back up so we use the '\' as a separator.
|
|
pszRemaining--;
|
|
}
|
|
#ifndef UNIX
|
|
// Our root is equal to a separator, so it's N/A on UNIX.
|
|
ASSERT(pcszStrToParse != pszRemaining);
|
|
#endif
|
|
// Parse the next segment or finish up if we reached the end.
|
|
hr = _ParseSeparator(pidlResult, pszRemaining, pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
|
|
|
|
if (pidlResult)
|
|
ILFree(pidlResult);
|
|
}
|
|
|
|
ILFree(pidlRelative);
|
|
}
|
|
penumIDList->Release();
|
|
}
|
|
psfFolder->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _GetNextPossibleSegment
|
|
|
|
PARAMETERS:
|
|
pcszFullPath - Full Path
|
|
ppszSegIterator - Pointer to iterator to maintain state.
|
|
WARNING: This needs to be NULL on first call.
|
|
pszSegOut - Of S_OK is returned, this will contain the next possible segment
|
|
cchSegOutSize - char Size of pszSegOut buffer
|
|
|
|
DESCRIPTION:
|
|
Generate the next possible segment that can
|
|
be parsed. If "one two three/four five" is passed
|
|
in, this function will return S_OK three times
|
|
with these values in pszSegOut:
|
|
1) "one two three",
|
|
2) "one two", and
|
|
3) "one".
|
|
|
|
In this example, S_OK will be returned for the first
|
|
three calls, and S_FALSE will be returned for the
|
|
fourth to indicate that no more possible segments can be obtained
|
|
from that string.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_GetNextPossibleSegment(LPCTSTR pcszFullPath,
|
|
LPTSTR * ppszSegIterator, LPTSTR pszSegOut, DWORD cchSegOutSize, BOOL fSkipShare)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPTSTR szStart = (LPTSTR) pcszFullPath;
|
|
|
|
// We need to treat UNCs Specially.
|
|
if (PathIsUNC(szStart))
|
|
{
|
|
LPTSTR szUNCShare;
|
|
// This is a UNC so we need to make the "Segment" include
|
|
// the "\\server\share" because Network Neighborhood's
|
|
// IShellFolder::ParseDisplayName() is increadibly slow
|
|
// and makes mistakes when it parses "server" and then "share"
|
|
// separately.
|
|
|
|
// This if clause will advance szStart past the Server
|
|
// section of the UNC path so the rest of the algorithm will
|
|
// naturally continue working on the share section of the UNC.
|
|
szStart += 2; // Skip past the "\\" UNC header.
|
|
|
|
// Is there a share?
|
|
if (fSkipShare && (szUNCShare = StrChr(szStart, CH_FILESEPARATOR)))
|
|
{
|
|
// Yes, so advanced to the first char in the share
|
|
// name so the algorithm below works correctly.
|
|
szStart = szUNCShare + 1;
|
|
}
|
|
}
|
|
|
|
// Do we need to initialize the iterator? If so, set it to the
|
|
// largest possible segment in the string because we will be
|
|
// working backwards.
|
|
ASSERT(ppszSegIterator);
|
|
if (*ppszSegIterator)
|
|
{
|
|
*ppszSegIterator = StrRChr(szStart, *ppszSegIterator, CH_SPACE);
|
|
if (!*ppszSegIterator)
|
|
{
|
|
pszSegOut[0] = TEXT('\0'); // Make sure caller doesn't ignore return and recurse infinitely.
|
|
return S_FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// We have not yet started the iteration, so set the ppszSegIterator to the end of the possible
|
|
// segment. This will be a segment separator character ('\' || '/') or the end of the string
|
|
// if either of those don't exist. This will be the first segment to try.
|
|
#ifndef UNIX
|
|
*ppszSegIterator = StrChr(szStart, CH_FILESEPARATOR);
|
|
if (!*ppszSegIterator)
|
|
*ppszSegIterator = StrChr(szStart, CH_SEPARATOR);
|
|
#else
|
|
// On UNIX, we always skip the 1st "/" and go to the 2nd.
|
|
if (szStart[0] == CH_FILESEPARATOR)
|
|
*ppszSegIterator = StrChr(szStart+1, CH_FILESEPARATOR);
|
|
#endif
|
|
|
|
LPTSTR pszFrag = StrChr(szStart, CH_FRAGMENT);
|
|
// Is the next separator a fragment?
|
|
if (pszFrag && (!*ppszSegIterator || (pszFrag < *ppszSegIterator)))
|
|
{
|
|
TCHAR szFile[MAX_URL_STRING];
|
|
|
|
StrCpyN(szFile, szStart, (int)(pszFrag - szStart + 1));
|
|
if (PathIsHTMLFile(szFile))
|
|
*ppszSegIterator = pszFrag;
|
|
}
|
|
|
|
if (!*ppszSegIterator)
|
|
{
|
|
// Go to end of the string because this is the last seg.
|
|
*ppszSegIterator = (LPTSTR) &((szStart)[lstrlen(szStart)]);
|
|
}
|
|
}
|
|
|
|
// Fill the pszSegOut parameter.
|
|
ASSERT(*ppszSegIterator);
|
|
|
|
// This is weird but correct. pszEnd - pszBeginning results count of chars, not
|
|
// count of bytes.
|
|
if (cchSegOutSize >= (DWORD)((*ppszSegIterator - pcszFullPath) + 1))
|
|
StrCpyN(pszSegOut, pcszFullPath, (int)(*ppszSegIterator - pcszFullPath + 1));
|
|
else
|
|
StrCpyN(pszSegOut, pcszFullPath, cchSegOutSize-1);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _GetNextPossibleFullPath
|
|
|
|
DESCRIPTION:
|
|
This function will attempt to see if strParseChunk
|
|
is a Parsible DisplayName under pidlParent.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_GetNextPossibleFullPath(LPCTSTR pcszFullPath,
|
|
LPTSTR * ppszSegIterator, LPTSTR pszSegOut, DWORD cchSegOutSize,
|
|
BOOL * pfContinue)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPTSTR pszNext = StrChr(*ppszSegIterator, CH_SPACE);
|
|
DWORD cchAmountToCopy = cchSegOutSize;
|
|
|
|
if (TEXT('\0') == (*ppszSegIterator)[0])
|
|
{
|
|
if (pfContinue)
|
|
*pfContinue = FALSE;
|
|
return E_FAIL; // Nothing Left.
|
|
}
|
|
|
|
if (!pszNext)
|
|
pszNext = &((*ppszSegIterator)[lstrlen(*ppszSegIterator)]); // Go to end of the string because this is the last seg.
|
|
|
|
// Copy as much of the string as we have room for.
|
|
// The compiler will take care of adding '/ sizeof(TCHAR)'.
|
|
if ((cchAmountToCopy-1) > (DWORD)(pszNext - pcszFullPath + 1))
|
|
cchAmountToCopy = (int)(pszNext - pcszFullPath + 1);
|
|
|
|
StrCpyN(pszSegOut, pcszFullPath, cchAmountToCopy);
|
|
|
|
if (CH_SPACE == pszNext[0])
|
|
{
|
|
*pfContinue = TRUE;
|
|
}
|
|
else
|
|
*pfContinue = FALSE;
|
|
|
|
*ppszSegIterator = pszNext;
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _QuickParse
|
|
|
|
PARAMETERS:
|
|
pidlParent - Pidl to ISF to parse from.
|
|
pszParseChunk - Display Name of item in pidlParent.
|
|
pszNext - Rest of string to parse if we succeed at parsing pszParseChunk.
|
|
pfPossibleWebUrl - Set to FALSE if we find that the user has attempted to enter
|
|
a Shell Url or File url but misspelled one of the segments.
|
|
fAllowRelative - Allow relative parsing. ("..")
|
|
fQualifyDispName - If TRUE when we known that we need to force the
|
|
URL to be fully qualified if we bind to the destination.
|
|
This is needed because we are using state information to
|
|
find the destination URL and that state information won't
|
|
be available later.
|
|
|
|
DESCRIPTION:
|
|
This function will attempt to see if strParseChunk
|
|
is a Parsible DisplayName under pidlParent.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_QuickParse(LPCITEMIDLIST pidlParent, LPTSTR pszParseChunk,
|
|
LPTSTR pszNext, BOOL * pfPossibleWebUrl, BOOL fAllowRelative,
|
|
BOOL fQualifyDispName)
|
|
{
|
|
HRESULT hr;
|
|
IShellFolder * psfFolder;
|
|
|
|
hr = IEBindToObject(pidlParent, &psfFolder);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
ULONG ulEatten; // Not used.
|
|
SHSTRW strParseChunkThunked;
|
|
|
|
hr = strParseChunkThunked.SetStr(pszParseChunk);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
LPITEMIDLIST pidl = NULL;
|
|
|
|
// TODO: In the future, we may want to cycle through commonly used extensions in case the
|
|
// user doesn't add them.
|
|
hr = psfFolder->ParseDisplayName(_GetWindow(), NULL, strParseChunkThunked.GetInplaceStr(), &ulEatten, &pidl, NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// IShellFolder::ParseDisplayName() only generates PIDLs that are relative to the ISF. We need
|
|
// to make them Absolute.
|
|
LPITEMIDLIST pidlFull = ILCombine(pidlParent, pidl);
|
|
|
|
if (pidlFull)
|
|
{
|
|
// Parse the next segment or finish up if we reached the end.
|
|
hr = _ParseSeparator(pidlFull, pszNext, pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
|
|
ILFree(pidlFull);
|
|
}
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
psfFolder->Release();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _CheckItem
|
|
|
|
DESCRIPTION:
|
|
This function will obtain the Display Name
|
|
of the ITEMID (pidlRelative) which is a child of
|
|
psfFolder. If it's Display Name matches the first
|
|
part of pcszStrToParse, we will return successful
|
|
and set ppszRemaining to the section of pcszStrToParse
|
|
after the segment just parsed.
|
|
|
|
This function will also see if the Display Name ends
|
|
in something that would indicate it's executable.
|
|
(.EXE, .BAT, .COM, ...). If so, we will match if
|
|
pcszStrToParse matches the Display Name without the
|
|
Extension.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_CheckItem(IShellFolder * psfFolder,
|
|
LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidlRelative,
|
|
LPITEMIDLIST * ppidlChild, LPCTSTR pcszStrToParse,
|
|
LPTSTR * ppszRemaining, DWORD dwFlags)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
|
|
*ppidlChild = NULL;
|
|
|
|
TCHAR szISFName[MAX_URL_STRING];
|
|
if (SUCCEEDED(DisplayNameOf(psfFolder, pidlRelative, dwFlags, szISFName, SIZECHARS(szISFName))))
|
|
{
|
|
DWORD cchISFLen = lstrlen(szISFName);
|
|
DWORD cchStrToParse = lstrlen(pcszStrToParse);
|
|
BOOL fEqual = FALSE;
|
|
|
|
// Either the item needs to match exactly, or it needs to do a partial match
|
|
// if the Shell Object is an executable file. For Example: "msdev" should match the
|
|
// "msdev.exe" file object.
|
|
if (cchISFLen > 0)
|
|
{
|
|
// We want to see if pcszStrToParse is a match to the first part of szISFName.
|
|
|
|
// First we will try to see if it's a direct match.
|
|
// Example: User="file.exe" Shell Item="file.exe"
|
|
// But we DON'T want to match if the StrToParse is longer than
|
|
// ISFName, unless the next char in StrToParse is a separator.
|
|
// If StrToParse is shorter than ISFName, then it can't be an exact match.
|
|
if (cchStrToParse >= cchISFLen &&
|
|
0 == StrCmpNI(szISFName, pcszStrToParse, cchISFLen) &&
|
|
(cchStrToParse == cchISFLen || IS_SHELL_SEPARATOR(pcszStrToParse[cchISFLen])))
|
|
{
|
|
fEqual = TRUE;
|
|
}
|
|
else
|
|
{
|
|
int cchRoot = (int)((PathFindExtension(szISFName)-szISFName));
|
|
// If that failed, we try to see if the Shell Item is
|
|
// executable (.EXE, .COM, .BAT, .CMD, ...) and if so,
|
|
// we will see if pcszStrToParse matches Shell Item w/o the file
|
|
// extension.
|
|
|
|
// REARCHITECT this will match if there happens to be a space in the user's
|
|
// filename that doesn't denote commandline arguments.
|
|
// Example: User="foo file.doc" Shell Item="foo.exe"
|
|
|
|
if (PathIsExe(szISFName) && // shell object is executable
|
|
(!((dwFlags & SHGDN_INFOLDER) && !(dwFlags & SHGDN_FORPARSING))) && // we didn't strip extension
|
|
((lstrlen(pcszStrToParse) >= cchRoot) && // and user entered at least root chars
|
|
((pcszStrToParse[cchRoot] == TEXT('\0')) || // and user entered exact root
|
|
(pcszStrToParse[cchRoot] == TEXT(' ')))) && // or possible commandline args
|
|
(0 == StrCmpNI(szISFName, pcszStrToParse, cchRoot))) // and the root matches
|
|
{
|
|
// This wasn't a direct match, but we found that the segment entered
|
|
// by the user (pcszStrToParse) matched
|
|
|
|
// We found that the ISF item is an executable object and the
|
|
// string matched w/o the extension.
|
|
fEqual = TRUE;
|
|
cchISFLen = cchRoot; // So that we generate *ppszRemaining correctly
|
|
}
|
|
}
|
|
}
|
|
|
|
if (fEqual)
|
|
{
|
|
hr = S_OK; // We were able to navigate to this shell item token.
|
|
*ppszRemaining = (LPTSTR) &(pcszStrToParse[cchISFLen]); // We will only iterate over the string, so it's ok that we loose the const.
|
|
*ppidlChild = ILCombine(pidlParent, pidlRelative);
|
|
TraceMsg(TF_CHECKITEM, "ShellUrl: _CheckItem() PIDL=>%s< IS EQUAL TO StrIn=>%s<", pcszStrToParse, szISFName);
|
|
}
|
|
else
|
|
TraceMsg(TF_CHECKITEM, "ShellUrl: _CheckItem() PIDL=>%s< not equal to StrIn=>%s<", pcszStrToParse, szISFName);
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _IsFilePidl
|
|
|
|
PARAMETERS:
|
|
pidl (IN) - Pidl to check if it is a File Pidl
|
|
|
|
DESCRIPTION:
|
|
The PIDL is a file pidl if:
|
|
1. The pidl equals "Network Neighborhood" or descendent
|
|
2. The pidl's grandparent or farther removed from "My Computer".
|
|
|
|
This algorithm only allows "Network Neighborhood" because
|
|
that ISF contains a huge number of PIDLs and takes for ever
|
|
to enumerate. The second clause will work in any part of the
|
|
file system except for the root drive (A:\, C:\). This is
|
|
because we need to allow other direct children of "My Computer"
|
|
to use the other parsing.
|
|
\****************************************************/
|
|
BOOL CShellUrl::_IsFilePidl(LPCITEMIDLIST pidl)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
BOOL fNeedToSkip = FALSE;
|
|
|
|
if (!pidl || ILIsEmpty(pidl))
|
|
return fResult;
|
|
|
|
// Test for Network Neighborhood because it will take forever to enum.
|
|
fResult = IsSpecialFolderChild(pidl, CSIDL_NETWORK, FALSE);
|
|
|
|
if (!fResult)
|
|
{
|
|
// We only want to do this if we are not the immediate
|
|
// child.
|
|
if (IsSpecialFolderChild(pidl, CSIDL_DRIVES, FALSE))
|
|
{
|
|
TCHAR szActualPath[MAX_URL_STRING]; // IEGetDisplayName() needs the buffer to be this large.
|
|
IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szActualPath, SIZECHARS(szActualPath), NULL);
|
|
|
|
DWORD dwOutSize = MAX_URL_STRING;
|
|
if (SUCCEEDED(PathCreateFromUrl(szActualPath, szActualPath, &dwOutSize, 0)))
|
|
{
|
|
PathStripToRoot(szActualPath);
|
|
fResult = PathIsRoot(szActualPath);
|
|
}
|
|
#ifdef UNIX
|
|
else
|
|
{
|
|
fResult = (szActualPath[0]==TEXT('/'));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
|
|
return fResult;
|
|
}
|
|
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: IsWebUrl
|
|
|
|
PARAMETERS
|
|
none.
|
|
|
|
DESCRIPTION:
|
|
Return TRUE if the URL is a Web Url (http,
|
|
ftp, other, ...). Return FALSE if it's a Shell Url
|
|
or File Url.
|
|
\****************************************************/
|
|
BOOL CShellUrl::IsWebUrl(void)
|
|
{
|
|
if (m_pidl)
|
|
{
|
|
if (!IsURLChild(m_pidl, TRUE))
|
|
return FALSE;
|
|
}
|
|
else
|
|
{
|
|
ASSERT(m_pszURL); // This CShellUrl hasn't been set.
|
|
if (m_pszURL && IsShellUrl(m_pszURL, TRUE))
|
|
return FALSE;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: SetCurrentWorkingDir
|
|
|
|
PARAMETERS
|
|
pShellUrlNew - Pointer to a CShellUrl that will
|
|
be the "Current Working Directory"
|
|
|
|
DESCRIPTION:
|
|
This Shell Url will have a new current working
|
|
directory, which will be the CShellUrl passed in.
|
|
|
|
MEMORY ALLOCATION:
|
|
The caller needs to Allocate pShellUrlNew and
|
|
this object will take care of freeing it. WARNING:
|
|
this means it cannot be on the stack.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::SetCurrentWorkingDir(LPCITEMIDLIST pidlCWD)
|
|
{
|
|
Pidl_Set(&m_pidlWorkingDir, pidlCWD);
|
|
|
|
DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: SetCurrentWorkingDir() pidl=>%s<", Dbg_PidlStr(m_pidlWorkingDir, szDbgBuffer, SIZECHARS(szDbgBuffer)));
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
PARAMETERS
|
|
pvPidl1 - First pidl to compare
|
|
pvPidl2 - Second pidl to compare
|
|
|
|
DESCRIPTION:
|
|
Return if the pidl matches. This doesn't work
|
|
for sorted lists (because we can't determine less
|
|
than or greater than).
|
|
\****************************************************/
|
|
int DPAPidlCompare(LPVOID pvPidl1, LPVOID pvPidl2, LPARAM lParam)
|
|
{
|
|
// return < 0 for pvPidl1 before pvPidl2.
|
|
// return == 0 for pvPidl1 equals pvPidl2.
|
|
// return > 0 for pvPidl1 after pvPidl2.
|
|
return (ILIsEqual((LPCITEMIDLIST)pvPidl1, (LPCITEMIDLIST)pvPidl2) ? 0 : 1);
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
PARAMETERS
|
|
pShellUrlNew - Pointer to a CShellUrl that will
|
|
be added to the "Shell Path"
|
|
|
|
DESCRIPTION:
|
|
This Shell Url will have the ShellUrl that's
|
|
passed in added to the "Shell Path", which will be
|
|
searched when trying to qualify the Shell Url during
|
|
parsing.
|
|
|
|
MEMORY ALLOCATION:
|
|
The caller needs to Allocate pShellUrlNew and
|
|
this object will take care of freeing it. WARNING:
|
|
this means it cannot be on the stack.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::AddPath(LPCITEMIDLIST pidl)
|
|
{
|
|
ASSERT(IS_VALID_PIDL(pidl));
|
|
|
|
// we dont want to add any paths that arent derived from
|
|
// our root.
|
|
if (ILIsRooted(m_pidlWorkingDir) && !ILIsParent(m_pidlWorkingDir, pidl, FALSE))
|
|
return S_FALSE;
|
|
|
|
if (!m_hdpaPath)
|
|
{
|
|
m_hdpaPath = DPA_Create(CE_PATHGROW);
|
|
if (!m_hdpaPath)
|
|
return E_OUTOFMEMORY;
|
|
}
|
|
|
|
// Does the path already exist in our list?
|
|
if (-1 == DPA_Search(m_hdpaPath, (void *)pidl, 0, DPAPidlCompare, NULL, 0))
|
|
{
|
|
// No, so let's add it.
|
|
LPITEMIDLIST pidlNew = ILClone(pidl);
|
|
if (pidlNew)
|
|
DPA_AppendPtr(m_hdpaPath, pidlNew);
|
|
}
|
|
|
|
DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: AddPath() pidl=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: Reset
|
|
|
|
PARAMETERS:
|
|
none.
|
|
|
|
DESCRIPTION:
|
|
This function will "Clean" out the object and
|
|
reset it. Normally called when the caller is about
|
|
to set new values.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::Reset(void)
|
|
{
|
|
Pidl_Set(&m_pidl, NULL);
|
|
Str_SetPtr(&m_pszURL, NULL);
|
|
Str_SetPtr(&m_pszArgs, NULL);
|
|
Str_SetPtr(&m_pszDisplayName, NULL);
|
|
m_dwGenType = 0;
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _CanUseAdvParsing
|
|
|
|
PARAMETERS:
|
|
none.
|
|
|
|
DESCRIPTION:
|
|
This function will return TRUE if Advanced
|
|
Parsing (Shell URLs) should be supported. This
|
|
function will keep track of whether the user
|
|
has turn off Shell Parsing from the Control Panel.
|
|
\****************************************************/
|
|
#define REGSTR_USEADVPARSING_PATH TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Band\\Address")
|
|
#define REGSTR_USEADVPARSING_VALUE TEXT("UseShellParsing")
|
|
|
|
BOOL CShellUrl::_CanUseAdvParsing(void)
|
|
{
|
|
// WARNING: Since this is static, changes to the registry entry won't be
|
|
// read in until the time the process is launched. This is okay,
|
|
// because this feature will probably be removed from the released
|
|
// product and can be added back in as power toy.
|
|
static TRI_STATE fCanUseAdvParsing = TRI_UNKNOWN;
|
|
|
|
if (TRI_UNKNOWN == fCanUseAdvParsing)
|
|
fCanUseAdvParsing = (TRI_STATE) SHRegGetBoolUSValue(REGSTR_USEADVPARSING_PATH, REGSTR_USEADVPARSING_VALUE, FALSE, TRUE);
|
|
|
|
return fCanUseAdvParsing;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _FixDriveDisplayName
|
|
|
|
PARAMETERS:
|
|
pszStart - Pointer to the beginning of the URL string.
|
|
pszCurrent - Pointer into current location in the URL string.
|
|
pidl - PIDL pointing to location of Shell Name space that
|
|
has been parsed so far.
|
|
|
|
DESCRIPTION:
|
|
This function exists to check if we are parsing
|
|
a drive letter. This is necessary because the Display
|
|
Name of drive letters end in '\', which will is needed
|
|
later to determine the start of the next segment.
|
|
\****************************************************/
|
|
#ifndef UNIX
|
|
#define DRIVE_STRENDING TEXT(":\\")
|
|
#define DRIVE_STRSIZE 3 // "C:\"
|
|
#else
|
|
#define DRIVE_STRSIZE 1 // "/"
|
|
#endif
|
|
|
|
BOOL _FixDriveDisplayName(LPCTSTR pszStart, LPCTSTR pszCurrent, LPCITEMIDLIST pidl)
|
|
{
|
|
BOOL fResult = FALSE;
|
|
|
|
ASSERT(pszCurrent >= pszStart);
|
|
|
|
#ifndef UNIX
|
|
// The compiler will take care of adding '/ sizeof(TCHAR)'.
|
|
if (((pszCurrent - pszStart) == DRIVE_STRSIZE) &&
|
|
(0 == StrCmpN(&(pszStart[1]), DRIVE_STRENDING, SIZECHARS(DRIVE_STRENDING)-1)))
|
|
#else
|
|
if ((((pszCurrent - pszStart)/sizeof(TCHAR)) == DRIVE_STRSIZE))
|
|
#endif
|
|
{
|
|
if (IsSpecialFolderChild(pidl, CSIDL_DRIVES, TRUE))
|
|
fResult = TRUE;
|
|
}
|
|
|
|
return fResult;
|
|
}
|
|
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _ParseRelativePidl
|
|
|
|
PARAMETERS:
|
|
pcszUrlIn - Pointer to URL to Parse.
|
|
dwFlags - Flags to modify the way the string is parsed.
|
|
pidl - This function will see if pcszUrlIn is a list of display names
|
|
relative to this pidl.
|
|
fAllowRelative - Do we allow relative parsing, which
|
|
means strings containing "..".
|
|
fQualifyDispName - If TRUE when we known that we need to force the
|
|
URL to be fully qualified if we bind to the destination.
|
|
This is needed because we are using state information to
|
|
find the destination URL and that state information won't
|
|
be available later.
|
|
|
|
DESCRIPTION:
|
|
Start the parsing by getting the pidl of ShellUrlRelative
|
|
and call _ParseNextSegment(). _ParseNextSegment() will
|
|
recursively parse each segment of the PIDL until either
|
|
it fails to fully parse of it finishes.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_ParseRelativePidl(LPCTSTR pcszUrlIn,
|
|
BOOL * pfPossibleWebUrl, DWORD dwFlags, LPCITEMIDLIST pidl,
|
|
BOOL fAllowRelative, BOOL fQualifyDispName)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fFreePidl = FALSE;
|
|
|
|
if (!pcszUrlIn)
|
|
return E_INVALIDARG;
|
|
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _ParseRelativePidl() Begin. pcszUrlIn=%s", pcszUrlIn);
|
|
|
|
hr = _ParseNextSegment(pidl, pcszUrlIn, pfPossibleWebUrl, fAllowRelative, fQualifyDispName);
|
|
|
|
if (pidl && fFreePidl)
|
|
ILFree((LPITEMIDLIST)pidl);
|
|
|
|
DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _ParseRelativePidl() m_pidl=>%s<", Dbg_PidlStr(m_pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: IsShellUrl
|
|
|
|
PARAMETERS:
|
|
LPCTSTR szUrl - URL from Outside Source.
|
|
return - Whether the URL is an Internet URL.
|
|
|
|
DESCRIPTION:
|
|
This function will determine if the URL is
|
|
a shell URL which includes the following:
|
|
1. File Urls (E;\dir1\dir2)
|
|
2. Shell Urls (shell:desktop)
|
|
\****************************************************/
|
|
BOOL IsShellUrl(LPCTSTR pcszUrl, BOOL fIncludeFileUrls)
|
|
{
|
|
int nSchemeBefore, nSchemeAfter;
|
|
TCHAR szParsedUrl[MAX_URL_STRING];
|
|
|
|
nSchemeBefore = GetUrlScheme(pcszUrl);
|
|
IURLQualifyT(pcszUrl, UQF_GUESS_PROTOCOL, szParsedUrl, NULL, NULL);
|
|
nSchemeAfter = GetUrlScheme(szParsedUrl);
|
|
|
|
// This is a "shell url" if it is a file: (and fIncludeFileUrls is
|
|
// set), or it is a shell:, or it is an invalid scheme (which
|
|
// occurs for things like "My Computer" and "Control Panel").
|
|
|
|
return ((fIncludeFileUrls && URL_SCHEME_FILE == nSchemeAfter) ||
|
|
URL_SCHEME_SHELL == nSchemeAfter ||
|
|
URL_SCHEME_INVALID == nSchemeBefore);
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: IsSpecialFolderChild
|
|
|
|
PARAMETERS:
|
|
pidlToTest (In) - Is this PIDL to test and see if it's
|
|
a child of SpecialFolder(nFolder).
|
|
psfParent (In Optional)- The psf passed to
|
|
SHGetSpecialFolderLocation() if needed.
|
|
nFolder (In) - Special Folder Number (CSIDL_INTERNET, CSIDL_DRIVES, ...).
|
|
pdwLevels (In Optional) - Pointer to DWORD to receive levels between
|
|
pidlToTest and it's parent (nFolder) if S_OK is returned.
|
|
|
|
DESCRIPTION:
|
|
This function will see if pidlToTest is a child
|
|
of the Special Folder nFolder.
|
|
\****************************************************/
|
|
BOOL IsSpecialFolderChild(LPCITEMIDLIST pidlToTest, int nFolder, BOOL fImmediate)
|
|
{
|
|
LPITEMIDLIST pidlThePidl = NULL;
|
|
BOOL fResult = FALSE;
|
|
|
|
if (!pidlToTest)
|
|
return FALSE;
|
|
|
|
ASSERT(IS_VALID_PIDL(pidlToTest));
|
|
if (NOERROR == SHGetSpecialFolderLocation(NULL, nFolder, &pidlThePidl))
|
|
{
|
|
fResult = ILIsParent(pidlThePidl, pidlToTest, fImmediate);
|
|
ILFree(pidlThePidl);
|
|
}
|
|
return fResult; // Shell Items (My Computer, Control Panel)
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: GetPidl
|
|
|
|
PARAMETERS
|
|
ppidl - Pointer that will receive the current PIDL.
|
|
|
|
DESCRIPTION:
|
|
This function will retrieve the pidl that the
|
|
Shell Url is set to.
|
|
|
|
MEMORY ALLOCATION:
|
|
This function will allocate the PIDL that ppidl
|
|
points to, and the caller needs to free the PIDL when
|
|
done with it.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::GetPidl(LPITEMIDLIST * ppidl)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (ppidl)
|
|
*ppidl = NULL;
|
|
if (!m_pidl)
|
|
hr = _GeneratePidl(m_pszURL, m_dwGenType);
|
|
|
|
if (ppidl)
|
|
{
|
|
if (m_pidl)
|
|
{
|
|
*ppidl = ILClone(m_pidl);
|
|
if (!*ppidl)
|
|
hr = E_FAIL;
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
}
|
|
|
|
// Callers only free *ppidl if SUCCEDED(hr), so assert we act this way.
|
|
ASSERT((*ppidl && SUCCEEDED(hr)) || (!*ppidl && FAILED(hr)));
|
|
|
|
DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: GetPidl() *ppidl=>%s<", Dbg_PidlStr(*ppidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
|
|
return hr;
|
|
}
|
|
|
|
//
|
|
// This is a wacky class! If GetPidl member of this class is called and
|
|
// a m_pidl is generated from and url and then Execute() assumes we have
|
|
// a valid location in our namespace and calls code that will not autoscan.
|
|
// This hacky function is used to return a pidl only if we have one to
|
|
// avoid the above problem.
|
|
//
|
|
HRESULT CShellUrl::GetPidlNoGenerate(LPITEMIDLIST * ppidl)
|
|
{
|
|
HRESULT hr = E_FAIL;
|
|
if (m_pidl && ppidl)
|
|
{
|
|
*ppidl = ILClone(m_pidl);
|
|
if (*ppidl)
|
|
{
|
|
hr = S_OK;
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************\
|
|
FUNCTION: _GeneratePidl
|
|
|
|
PARAMETERS
|
|
pcszUrl - This URL will be used to generate the m_pidl.
|
|
dwGenType - This is needed to know how to parse pcszUrl
|
|
to generate the PIDL.
|
|
|
|
DESCRIPTION:
|
|
This CShellUrl maintains a pointer to the object
|
|
in the Shell Name Space by using either the string URL
|
|
or the PIDL. When this CShellUrl is set to one, we
|
|
delay generating the other one for PERF reasons.
|
|
This function generates the PIDL from the string URL
|
|
when we do need the string.
|
|
\****************************************************/
|
|
|
|
HRESULT CShellUrl::_GeneratePidl(LPCTSTR pcszUrl, DWORD dwGenType)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (!pcszUrl && m_pidl)
|
|
return S_OK; // The caller only wants the PIDL to be created if it doesn't exist.
|
|
|
|
if (pcszUrl && m_pidl)
|
|
{
|
|
ILFree(m_pidl);
|
|
m_pidl = NULL;
|
|
}
|
|
|
|
switch (dwGenType)
|
|
{
|
|
case GENTYPE_FROMURL:
|
|
if (ILIsRooted(m_pidlWorkingDir))
|
|
hr = E_FAIL; // MSN Displays error dialogs on IShellFolder::ParseDisplayName()
|
|
// fall through
|
|
case GENTYPE_FROMPATH:
|
|
hr = IECreateFromPath(pcszUrl, &m_pidl);
|
|
// This may fail if it's something like "ftp:/" and not yet valid".
|
|
break;
|
|
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
break;
|
|
}
|
|
|
|
if (!m_pidl && SUCCEEDED(hr))
|
|
hr = E_FAIL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: SetPidl
|
|
|
|
PARAMETERS
|
|
pidl - New pidl to use.
|
|
|
|
DESCRIPTION:
|
|
The shell url will now consist of the new pidl
|
|
passed in.
|
|
|
|
MEMORY ALLOCATION:
|
|
The caller is responsible for Allocating and Freeing
|
|
the PIDL parameter.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::SetPidl(LPCITEMIDLIST pidl)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
ASSERT(!pidl || IS_VALID_PIDL(pidl));
|
|
|
|
Reset(); // External Calls to this will reset the entire CShellUrl.
|
|
return _SetPidl(pidl);
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _SetPidl
|
|
|
|
PARAMETERS
|
|
pidl - New pidl to use.
|
|
|
|
DESCRIPTION:
|
|
This function will reset the m_pidl member
|
|
variable without modifying m_szURL. This is only used
|
|
internally, and callers that want to reset the entire
|
|
CShellUrl to a PIDL should call the public method
|
|
SetPidl().
|
|
|
|
MEMORY ALLOCATION:
|
|
The caller is responsible for Allocating and Freeing
|
|
the PIDL parameter.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_SetPidl(LPCITEMIDLIST pidl)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DEBUG_CODE(TCHAR szDbgBuffer[MAX_PATH];)
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: _SetPidl() pidl=>%s<", Dbg_PidlStr(pidl, szDbgBuffer, SIZECHARS(szDbgBuffer)));
|
|
|
|
Pidl_Set(&m_pidl, pidl);
|
|
if (!m_pidl)
|
|
hr = E_FAIL;
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: GetUrl
|
|
|
|
PARAMETERS
|
|
pszUrlOut (Out Optional) - If the caller wants the string.
|
|
cchUrlOutSize (In) - Size of String Buffer Passed in.
|
|
|
|
DESCRIPTION:
|
|
This function will retrieve the string value of
|
|
the shell url. This will not include the command line
|
|
arguments or other information needed for correct navigation
|
|
(AutoSearch=On/Off, ...). Note that this may be of the
|
|
form "Shell:/desktop/My Computer/...".
|
|
\****************************************************/
|
|
HRESULT CShellUrl::GetUrl(LPTSTR pszUrlOut, DWORD cchUrlOutSize)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!m_pszURL)
|
|
{
|
|
if (m_pidl)
|
|
hr = _GenerateUrl(m_pidl);
|
|
else
|
|
hr = E_FAIL; // User never set the CShellUrl.
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && pszUrlOut)
|
|
StrCpyN(pszUrlOut, m_pszURL, cchUrlOutSize);
|
|
|
|
return hr;
|
|
}
|
|
|
|
/****************************************************\
|
|
!!! WARNING - extremely specific to the ShellUrl/AddressBar - ZekeL - 18-NOV-98
|
|
!!! it depends on the bizarre pathology of the ShellUrl in order
|
|
!!! to be reparsed into a pidl later. cannot be used for anything else
|
|
|
|
PARAMETERS:
|
|
pidlIn - Pointer to PIDL to generate Display Names.
|
|
pszUrlOut - String Buffer to store list of Display Names for ITEMIDs
|
|
in pidlIn.
|
|
cchUrlOutSize - Size of Buffer in characters.
|
|
|
|
DESCRIPTION:
|
|
This function will take the PIDL passed in and
|
|
generate a string containing the ILGDN_ITEMONLY Display names
|
|
of each ITEMID in the pidl separated by '\'.
|
|
\****************************************************/
|
|
#define SZ_SEPARATOR TEXT("/")
|
|
|
|
HRESULT MutantGDNForShellUrl(LPCITEMIDLIST pidlIn, LPTSTR pszUrlOut, int cchUrlOutSize)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPCITEMIDLIST pidlCur;
|
|
IShellFolder *psfCur = NULL;
|
|
|
|
if (ILIsRooted(pidlIn))
|
|
{
|
|
// need to start off with our virtual root
|
|
LPITEMIDLIST pidlFirst = ILCloneFirst(pidlIn);
|
|
if (pidlFirst)
|
|
{
|
|
IEBindToObject(pidlFirst, &psfCur);
|
|
ILFree(pidlFirst);
|
|
}
|
|
|
|
pidlCur = _ILNext(pidlIn);
|
|
}
|
|
else
|
|
{
|
|
SHGetDesktopFolder(&psfCur);
|
|
pidlCur = pidlIn;
|
|
}
|
|
|
|
ASSERT(pidlCur && IS_VALID_PIDL(pidlCur));
|
|
while (psfCur && SUCCEEDED(hr) && !ILIsEmpty(pidlCur) && (cchUrlOutSize > 0))
|
|
{
|
|
LPITEMIDLIST pidlCopy = ILCloneFirst(pidlCur);
|
|
if (pidlCopy)
|
|
{
|
|
StrCpyN(pszUrlOut, SZ_SEPARATOR, cchUrlOutSize);
|
|
cchUrlOutSize -= SIZECHARS(SZ_SEPARATOR);
|
|
|
|
TCHAR szCurrDispName[MAX_PATH];
|
|
hr = DisplayNameOf(psfCur, pidlCopy, SHGDN_NORMAL, szCurrDispName, SIZECHARS(szCurrDispName));
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (TBOOL((int)cchUrlOutSize > lstrlen(szCurrDispName)))
|
|
{
|
|
StrCatBuff(pszUrlOut, szCurrDispName, cchUrlOutSize);
|
|
cchUrlOutSize -= lstrlen(szCurrDispName);
|
|
}
|
|
|
|
// may fail, in that case we terminate the loop
|
|
IShellFolder *psfCurNew = NULL; // for buggy BindToObject impls
|
|
hr = psfCur->BindToObject(pidlCopy, NULL, IID_IShellFolder, (void **)&psfCurNew);
|
|
|
|
psfCur->Release();
|
|
psfCur = psfCurNew;
|
|
}
|
|
pidlCur = _ILNext(pidlCur);
|
|
ILFree(pidlCopy);
|
|
}
|
|
else
|
|
hr = E_FAIL;
|
|
}
|
|
if (psfCur)
|
|
psfCur->Release();
|
|
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: MutantGDNForShellUrl() End. pszUrlOut=%s", pszUrlOut);
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _GenerateUrl
|
|
|
|
PARAMETERS
|
|
pidl - This PIDL will be used to generate the m_pszURL, string URL.
|
|
|
|
DESCRIPTION:
|
|
This CShellUrl maintains a pointer to the object
|
|
in the Shell Name Space by using either the string URL
|
|
or the PIDL. When this CShellUrl is set to one, we
|
|
delay generating the other one for PERF reasons.
|
|
This function generates the string URL from the PIDL
|
|
when we do need the string.
|
|
\****************************************************/
|
|
#define SZ_THEINTERNET_PARSENAME TEXT("::{")
|
|
|
|
HRESULT CShellUrl::_GenerateUrl(LPCITEMIDLIST pidl)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
TCHAR szUrl[MAX_URL_STRING];
|
|
|
|
ASSERT(IS_VALID_PIDL(pidl));
|
|
if (IsURLChild(pidl, TRUE) || _IsFilePidl(pidl))
|
|
{
|
|
hr = IEGetNameAndFlags(pidl, SHGDN_FORPARSING, szUrl, SIZECHARS(szUrl), NULL);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// Was the pidl pointing to "The Internet"?
|
|
if (0 == StrCmpN(szUrl, SZ_THEINTERNET_PARSENAME, (ARRAYSIZE(SZ_THEINTERNET_PARSENAME) - 1)))
|
|
{
|
|
// Yes, so we don't want the SHGDN_FORPARSING name
|
|
// because the user doesn't know what the heck it is. Since we
|
|
// navigate to the home page, let's display that.
|
|
hr = IEGetNameAndFlags(pidl, SHGDN_NORMAL, szUrl, SIZECHARS(szUrl), NULL);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// hr = MutantGDNForShellUrl(pidl, szUrl, SIZECHARS(szUrl));
|
|
hr = IEGetNameAndFlags(pidl, SHGDN_NORMAL, szUrl, SIZECHARS(szUrl), NULL);
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
Str_SetPtr(&m_pszURL, szUrl);
|
|
|
|
if (!m_pszURL)
|
|
hr = E_OUTOFMEMORY;
|
|
|
|
if (FAILED(hr))
|
|
Str_SetPtr(&m_pszURL, NULL); // Clear it
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: SetUrl
|
|
|
|
PARAMETERS
|
|
szUrlOut (Out) - Url
|
|
|
|
DESCRIPTION:
|
|
Set the ShellUrl from a string that is parsible from
|
|
the root (desktop) ISF. This is normally used for
|
|
File Paths.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::SetUrl(LPCTSTR pcszUrlIn, DWORD dwGenType)
|
|
{
|
|
Reset(); // External Calls to this will reset the entire CShellUrl.
|
|
return _SetUrl(pcszUrlIn, dwGenType);
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _SetUrl
|
|
|
|
PARAMETERS
|
|
pcszUrlIn (In) - The string URL for this CShellUrl
|
|
dwGenType (In) - Method to use when generating the PIDL
|
|
from pcszUrlIn.
|
|
|
|
DESCRIPTION:
|
|
This function will reset the m_pszURL member
|
|
variable without modifying m_pidl. This is only used
|
|
internally, and callers that want to reset the entire
|
|
CShellUrl to an URL should call the public method
|
|
SetUrl().
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_SetUrl(LPCTSTR pcszUrlIn, DWORD dwGenType)
|
|
{
|
|
m_dwGenType = dwGenType;
|
|
|
|
return Str_SetPtr(&m_pszURL, pcszUrlIn) ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: GetDisplayName
|
|
|
|
PARAMETERS
|
|
pszUrlOut (Out) - Get the Shell Url in String Form.
|
|
cchUrlOutSize (In) - Size of String Buffer Passed in.
|
|
|
|
DESCRIPTION:
|
|
This function will Fill in pszUrlOut with nice
|
|
versions of the Shell Url that can be displayed in
|
|
the AddressBar or in the Titles of windows.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::GetDisplayName(LPTSTR pszUrlOut, DWORD cchUrlOutSize)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (!m_pszDisplayName)
|
|
{
|
|
if (m_pidl)
|
|
{
|
|
LPITEMIDLIST pidl = NULL;
|
|
|
|
hr = GetPidl(&pidl);
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
hr = _GenDispNameFromPidl(pidl, NULL);
|
|
ILFree(pidl);
|
|
}
|
|
}
|
|
else if (m_pszURL)
|
|
{
|
|
// In this case, we will just give back the URL.
|
|
Str_SetPtr(&m_pszDisplayName, m_pszURL);
|
|
|
|
if (NULL == m_pszDisplayName)
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
hr = E_FAIL;
|
|
}
|
|
}
|
|
|
|
if (SUCCEEDED(hr) && pszUrlOut && m_pszDisplayName)
|
|
StrCpyN(pszUrlOut, m_pszDisplayName, cchUrlOutSize);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: _GenDispNameFromPidl
|
|
|
|
PARAMETERS
|
|
pidl (In) - This will be used to generate the Display Name.
|
|
pcszArgs (In) - These will be added to the end of the Display Name
|
|
|
|
DESCRIPTION:
|
|
This function will generate the Display Name
|
|
from the pidl and pcszArgs parameters. This is
|
|
normally not needed when this CShellUrl was parsed
|
|
from an outside source, because the Display Name
|
|
was generated at that time.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::_GenDispNameFromPidl(LPCITEMIDLIST pidl, LPCTSTR pcszArgs)
|
|
{
|
|
HRESULT hr;
|
|
TCHAR szDispName[MAX_URL_STRING];
|
|
|
|
hr = GetUrl(szDispName, SIZECHARS(szDispName));
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (pcszArgs)
|
|
StrCatBuff(szDispName, pcszArgs, ARRAYSIZE(szDispName));
|
|
PathMakePretty(szDispName);
|
|
|
|
hr = Str_SetPtr(&m_pszDisplayName, szDispName) ? S_OK : E_OUTOFMEMORY;
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: GetArgs
|
|
|
|
PARAMETERS
|
|
pszArgsOut - The arguments to the Shell Url. (Only
|
|
for ShellExec().
|
|
cchArgsOutSize - Size of pszArgsOut in chars.
|
|
|
|
DESCRIPTION:
|
|
Get the arguments that will be passed to
|
|
ShellExec() if 1) the Pidl is navigated to, 2) it's
|
|
a File URL, and 3) it's not navigatable.
|
|
\****************************************************/
|
|
HRESULT CShellUrl::GetArgs(LPTSTR pszArgsOut, DWORD cchArgsOutSize)
|
|
{
|
|
ASSERT(pszArgsOut);
|
|
|
|
if (m_pszArgs)
|
|
StrCpyN(pszArgsOut, m_pszArgs, cchArgsOutSize);
|
|
else
|
|
*pszArgsOut = 0;
|
|
|
|
TraceMsg(TF_BAND|TF_GENERAL, "ShellUrl: GetArgs() pszArgsOut=%s", pszArgsOut);
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
/****************************************************\
|
|
FUNCTION: SetDefaultShellPath
|
|
|
|
PARAMETERS
|
|
psu - CShellUrl to set path.
|
|
|
|
DESCRIPTION:
|
|
"Desktop";"Desktop/My Computer" is the
|
|
most frequently used Shell Path for parsing. This
|
|
function will add those two items to the CShellUrl
|
|
passed in the paramter.
|
|
\****************************************************/
|
|
HRESULT SetDefaultShellPath(CShellUrl * psu)
|
|
{
|
|
ASSERT(psu);
|
|
LPITEMIDLIST pidl;
|
|
|
|
// We need to set the "Shell Path" which will allow
|
|
// the user to enter Display Names of items in Shell
|
|
// Folders that are frequently used. We add "Desktop"
|
|
// and "Desktop/My Computer" to the Shell Path because
|
|
// that is what users use most often.
|
|
|
|
// _pshuUrl will free pshuPath, so we can't.
|
|
psu->AddPath(&s_idlNULL);
|
|
|
|
SHGetSpecialFolderLocation(NULL, CSIDL_DRIVES, &pidl); // Get Pidl for "My Computer"
|
|
if (pidl)
|
|
{
|
|
// psu will free pshuPath, so we can't.
|
|
psu->AddPath(pidl);
|
|
ILFree(pidl);
|
|
}
|
|
|
|
// Add favorites folder too
|
|
SHGetSpecialFolderLocation(NULL, CSIDL_FAVORITES, &pidl);
|
|
if (pidl)
|
|
{
|
|
// psu will free pshuPath, so we can't.
|
|
psu->AddPath(pidl);
|
|
ILFree(pidl);
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
void CShellUrl::SetMessageBoxParent(HWND hwnd)
|
|
{
|
|
// Find the topmost window so that the messagebox disables
|
|
// the entire frame
|
|
HWND hwndTopmost = NULL;
|
|
while (hwnd)
|
|
{
|
|
hwndTopmost = hwnd;
|
|
hwnd = GetParent(hwnd);
|
|
}
|
|
|
|
m_hwnd = hwndTopmost;
|
|
};
|