windows-nt/Source/XPSP1/NT/shell/inc/brutil.cpp
2020-09-26 16:20:57 +08:00

1532 lines
45 KiB
C++

/*
NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
This file is #include'd in browseui\ and shdocvw\ util.cpp. these are too small
to add an extra dependency, so they're just shared. ideally, these should move
to shlwapi or comctl32 or some lib or ...
NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
*/
#include "ccstock2.h"
#include "mluisupp.h"
#include "richedit.h" //for charformat2
STDAPI_(BOOL) IsBrowseNewProcess()
{
return SHRegGetBoolUSValue(REGSTR_PATH_EXPLORER TEXT("\\BrowseNewProcess"), TEXT("BrowseNewProcess"), FALSE, FALSE);
}
// Should we run browser in a new process?
STDAPI_(BOOL) IsBrowseNewProcessAndExplorer()
{
if (GetModuleHandle(TEXT("EXPLORER.EXE")))
return IsBrowseNewProcess();
return FALSE; // Not in shell process so ignore browse new process flag
}
HRESULT _NavigateFrame(IUnknown *punkFrame, LPCTSTR pszPath, BOOL fIsInternetShortcut)
{
HRESULT hr = E_OUTOFMEMORY;
BSTR bstr = SysAllocStringT(pszPath);
if (bstr)
{
if (fIsInternetShortcut)
{
IOleCommandTarget *pcmdt;
hr = IUnknown_QueryService(punkFrame, SID_SHlinkFrame, IID_PPV_ARG(IOleCommandTarget, &pcmdt));
if (SUCCEEDED(hr))
{
VARIANT varShortCutPath = {0};
VARIANT varFlag = {0};
varFlag.vt = VT_BOOL;
varFlag.boolVal = VARIANT_TRUE;
varShortCutPath.vt = VT_BSTR;
varShortCutPath.bstrVal = bstr;
hr = pcmdt->Exec(&CGID_Explorer, SBCMDID_IESHORTCUT, 0, &varShortCutPath, &varFlag);
pcmdt->Release();
}
}
else
{
IWebBrowser2 *pwb;
hr = IUnknown_QueryService(punkFrame, SID_SHlinkFrame, IID_PPV_ARG(IWebBrowser2, &pwb));
if (SUCCEEDED(hr))
{
hr = pwb->Navigate(bstr, PVAREMPTY, PVAREMPTY, PVAREMPTY, PVAREMPTY);
hr = pwb->put_Visible(VARIANT_TRUE);
pwb->Release();
}
}
SysFreeString(bstr);
}
return hr;
}
//
// Take a path or an URL and create a shorcut to navigare to it
//
STDAPI IENavigateIEProcess(LPCTSTR pszPath, BOOL fIsInternetShortcut)
{
IUnknown *punk;
HRESULT hr = CoCreateInstance(CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARG(IUnknown, &punk));
if (SUCCEEDED(hr))
{
hr = _NavigateFrame(punk, pszPath, fIsInternetShortcut);
punk->Release();
}
return hr;
}
// If this is an internet shortcut (.url file), we want it to
// navigate using using the file name so the frame frame
// can read data beyond out of that file. this includes frame set
// navigation and data that script on the page may have stored.
/*
Purpose : This function takes a path to a file. if that file is a .URL we try
to navigate with that file name. this is because .URL files have extra data stored
in them that we want to let script on the page get to. the exec we send here
lets the frame know the .URL file that this came from
Parameters : file name of .URL file (maybe) : In param
pUnk : Pointer to Object from which you can get the IOleCommandTarget
returns:
TRUE handled
FALSE not handled, file might not be a .URL
*/
STDAPI NavFrameWithFile(LPCTSTR pszPath, IUnknown *punk)
{
HRESULT hr = E_FAIL;
LPTSTR pszExt = PathFindExtension(pszPath);
// HACK: we hard code .URL. this should be a property of the file type
if (0 == StrCmpI(pszExt, TEXT(".url")))
{
#ifdef BROWSENEWPROCESS_STRICT // "Nav in new process" has become "Launch in new process", so this is no longer needed
if (IsBrowseNewProcessAndExplorer())
hr = IENavigateIEProcess(pszPath, TRUE);
else
#endif
hr = _NavigateFrame(punk, pszPath, TRUE);
}
return hr;
}
// get the win32 file system name (path) for the item
// and optional attributes
//
// pdwAttrib may be NULL
// in/out:
// pdwAttrib may be NULL, attributes to query on the item
STDAPI GetPathForItem(IShellFolder *psf, LPCITEMIDLIST pidl, LPTSTR pszPath, DWORD *pdwAttrib)
{
HRESULT hres = E_FAIL;
DWORD dwAttrib;
if (pdwAttrib == NULL)
{
pdwAttrib = &dwAttrib;
dwAttrib = SFGAO_FILESYSTEM;
}
else
*pdwAttrib |= SFGAO_FILESYSTEM;
if (SUCCEEDED(psf->GetAttributesOf(1, &pidl, pdwAttrib)) &&
(*pdwAttrib & SFGAO_FILESYSTEM))
{
STRRET str;
hres = psf->GetDisplayNameOf(pidl, SHGDN_FORPARSING, &str);
if (SUCCEEDED(hres))
StrRetToBuf(&str, pidl, pszPath, MAX_PATH);
}
return hres;
}
STDAPI EditBox_TranslateAcceleratorST(LPMSG lpmsg)
{
switch (lpmsg->message) {
case WM_KEYUP: // eat these (if we process corresponding WM_KEYDOWN)
case WM_KEYDOWN: // process these
if (lpmsg->wParam != VK_TAB)
{
// all keydown messages except for the tab key should go straight to
// the edit control -- unless the Ctrl key is down, in which case there
// are 9 messages that should go straight to the edit control
#ifdef DEBUG
if (lpmsg->wParam == VK_CONTROL)
return S_FALSE;
#endif
if (GetKeyState(VK_CONTROL) & 0x80000000)
{
switch (lpmsg->wParam)
{
case VK_RIGHT:
case VK_LEFT:
case VK_UP:
case VK_DOWN:
case VK_HOME:
case VK_END:
case VK_F4:
case VK_INSERT:
case VK_DELETE:
case 'C':
case 'X':
case 'V':
case 'A':
case 'Z':
// these Ctrl+key messages are used by the edit control
// send 'em straight there
break;
default:
return(S_FALSE);
}
}
else
{
switch(lpmsg->wParam)
{
case VK_F5: // for refresh
case VK_F6: // for cycle focus
return(S_FALSE);
}
}
// Note that we return S_OK.
goto TranslateDispatch;
}
break;
case WM_CHAR:
TranslateDispatch:
TranslateMessage(lpmsg);
DispatchMessage(lpmsg);
return(S_OK);
}
return S_FALSE;
}
// NOTE: dupe with shell32 util.cpp function
// like OLE GetClassFile(), but it only works on ProgID\CLSID type registration
// not real doc files or pattern matched files
//
STDAPI _CLSIDFromExtension(LPCTSTR pszExt, CLSID *pclsid)
{
TCHAR szProgID[80];
DWORD cb = SIZEOF(szProgID);
if (SHGetValue(HKEY_CLASSES_ROOT, pszExt, NULL, NULL, szProgID, &cb) == ERROR_SUCCESS)
{
TCHAR szCLSID[80];
StrCatBuff(szProgID, TEXT("\\CLSID"), ARRAYSIZE(szProgID));
cb = SIZEOF(szCLSID);
if (SHGetValue(HKEY_CLASSES_ROOT, szProgID, NULL, NULL, szCLSID, &cb) == ERROR_SUCCESS)
{
return GUIDFromString(szCLSID, pclsid) ? S_OK : E_FAIL;
}
}
return E_FAIL;
}
#if 0 // not used yet
// IShellLink is #defined to IShellLinkA or IShellLinkW depending on compile flags,
// bug Win95 did not support IShellLinkW. So call this function instead and you
// get the correct results regardless of what platform you are running on.
// REVIEW: In fact, we probably want these for ALL IShellLink functions...
//
LWSTDAPI IShellLink_GetPathA(IUnknown *punk, LPSTR pszBuf, UINT cchBuf, DWORD dwFlags)
{
HRESULT hres = E_INVALIDARG;
RIPMSG(cchBuf && pszBuf && IS_VALID_WRITE_BUFFER(pszBuf, char, cchBuf), "IShellLink_GetPathA: callre passed bad pszBuf/cchBuf");
DEBUGWhackPathBufferA(pszBuf, cchBuf);
if (cchBuf && pszBuf)
{
// In case of gross failure, NULL output buffer
*pszBuf = 0;
IShellLinkA * pslA;
hres = punk->QueryInterface(IID_IShellLinkA, (void**)&pslA);
if (SUCCEEDED(hres))
{
hres = pslA->GetPath(pszBuf, cchBuf, NULL, dwFlags);
pslA->Release();
}
else if (FAILED(hres))
{
#ifdef UNICODE
IShellLinkW *pslW;
hres = punk->QueryInterface(IID_IShellLinkW, (void**)&pslW);
if (SUCCEEDED(hres))
{
WCHAR wszPath[MAX_BUF];
LPWSTR pwszBuf = wszPath;
UINT cch = ARRAYSIZE(wszPath);
// Our stack buffer is too small, allocate one of the output buffer size
if (cchBuf > cch)
{
LPWSTR pwsz = LocalAlloc(LPTR, cchBuf * sizeof(WCHAR));
if (pwsz)
{
pwszBuf = pwsz;
cch = cchBuf;
}
}
hres = pslW->GetPath(pwszBuf, cch, NULL, dwFlags);
if (SUCCEEDED(hres))
{
SHUnicodeToAnsi(pwszBuf, pszBuf, cchBuf);
}
pslW->Release();
}
#endif
}
}
return hres;
}
LWSTDAPI IShellLink_GetPathW(IUnknown *punk, LPWSTR pwszBuf, UINT cchBuf, DWORD dwFlags)
{
HRESULT hres = E_INVALIDARG;
RIPMSG(cchBuf && pwszBuf && IS_VALID_WRITE_BUFFER(pwszBuf, WCHAR, cchBuf), "IShellLink_GetPathW: caller passed bad pwszBuf/cchBuf");
DEBUGWhackPathBufferW(pwszBuf, cchBuf);
if (cchBuf && pwszBuf)
{
// In case of gross failure, NULL output buffer
*pwszBuf = 0;
#ifdef UNICODE
IShellLinkW * pslW;
hres = punk->QueryInterface(IID_IShellLinkW, (void**)&pslW);
if (SUCCEEDED(hres))
{
hres = pslW->GetPath(pszBuf, cchBuf, NULL, dwFlags);
pslW->Release();
}
else if (FAILED(hres))
#endif
{
IShellLinkA *pslA;
hres = punk->QueryInterface(IID_IShellLinkA, (void**)&pslA);
if (SUCCEEDED(hres))
{
char szPath[MAX_BUF];
LPSTR pszBuf = szPath;
UINT cch = ARRAYSIZE(szPath);
// Our stack buffer is too small, allocate one of the output buffer size
if (cchBuf > cch)
{
LPSTR psz = LocalAlloc(LPTR, cchBuf * sizeof(char));
if (psz)
{
pszBuf = psz;
cch = cchBuf;
}
}
hres = pslA->GetPath(pszBuf, cch, NULL, dwFlags);
if (SUCCEEDED(hres))
{
SHAnsiToUnicode(pszBuf, pwszBuf, cchBuf);
}
pslA->Release();
}
}
}
return hres;
}
#endif // 0
HRESULT IShellLinkAorW_GetPath(IShellLinkA *pslA, LPTSTR pszBuf, UINT cchBuf, DWORD dwFlags)
{
HRESULT hres = E_FAIL;
// If we store the string unicode, we could be losing file information by asking
// through A version. Be unicode friendly and use the W version if it exists
//
#ifdef UNICODE
IShellLinkW *pslW;
hres = pslA->QueryInterface(IID_PPV_ARG(IShellLinkW, &pslW));
if (SUCCEEDED(hres))
{
hres = pslW->GetPath(pszBuf, cchBuf, NULL, dwFlags);
pslW->Release();
}
#endif
if (FAILED(hres))
{
char szBuf[MAX_URL_STRING]; // BOGUS, but this is a common size used, perhaps we should LocalAlloc...
cchBuf = ARRAYSIZE(szBuf);
hres = pslA->GetPath(szBuf, cchBuf, NULL, dwFlags);
SHAnsiToTChar(szBuf, pszBuf, cchBuf);
}
return hres;
}
STDAPI GetLinkTargetIDList(LPCTSTR pszPath, LPTSTR pszTarget, DWORD cchTarget, LPITEMIDLIST *ppidl)
{
IShellLinkA *psl;
CLSID clsid;
HRESULT hres;
*ppidl = NULL; // assume failure
// WARNING: we really should call GetClassFile() but this could
// slow this down a lot... so chicken out and just look in the registry
if (FAILED(_CLSIDFromExtension(PathFindExtension(pszPath), &clsid)))
clsid = CLSID_ShellLink; // assume it's a shell link
hres = CoCreateInstance(clsid, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellLinkA, &psl));
if (SUCCEEDED(hres))
{
IPersistFile *ppf;
hres = psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
if (SUCCEEDED(hres))
{
WCHAR wszPath[MAX_PATH];
SHTCharToUnicode(pszPath, wszPath, ARRAYSIZE(wszPath));
hres = ppf->Load(wszPath, 0);
if (SUCCEEDED(hres))
{
psl->GetIDList(ppidl);
if (*ppidl == NULL)
hres = E_FAIL; // NULL pidl is valid, but
// lets not return that to clients
if (pszTarget)
{
IShellLinkAorW_GetPath(psl, pszTarget, cchTarget, 0);
}
}
ppf->Release();
}
psl->Release();
}
// pszPath might == pszTarget so don't null out on entry always
if (FAILED(hres) && pszTarget)
*pszTarget = 0;
return hres;
}
STDAPI_(void) PathToDisplayNameW(LPCTSTR pszPath, LPTSTR pszDisplayName, UINT cchDisplayName)
{
SHFILEINFO sfi;
if (SHGetFileInfo(pszPath, 0, &sfi, SIZEOF(sfi), SHGFI_DISPLAYNAME))
{
StrCpyN(pszDisplayName, sfi.szDisplayName, cchDisplayName);
}
else
{
StrCpyN(pszDisplayName, PathFindFileName(pszPath), cchDisplayName);
PathRemoveExtension(pszDisplayName);
}
}
STDAPI_(void) PathToDisplayNameA(LPSTR pszPathA, LPSTR pszDisplayNameA, int cchDisplayName)
{
SHFILEINFOA sfi;
if (SHGetFileInfoA(pszPathA, 0, &sfi, SIZEOF(sfi), SHGFI_DISPLAYNAME))
{
StrCpyNA(pszDisplayNameA, sfi.szDisplayName, cchDisplayName);
}
else
{
pszPathA = PathFindFileNameA(pszPathA);
StrCpyNA(pszDisplayNameA, pszPathA, cchDisplayName);
PathRemoveExtensionA(pszDisplayNameA);
}
}
void* DataObj_GetDataOfType(IDataObject* pdtobj, UINT cfType, STGMEDIUM *pstg)
{
void * pret = NULL;
FORMATETC fmte = {(CLIPFORMAT)cfType, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
if (pdtobj->GetData(&fmte, pstg) == S_OK)
{
pret = GlobalLock(pstg->hGlobal);
if (!pret)
ReleaseStgMedium(pstg);
}
return pret;
}
void ReleaseStgMediumHGLOBAL(STGMEDIUM *pstg)
{
ASSERT(pstg->tymed == TYMED_HGLOBAL);
GlobalUnlock(pstg->hGlobal);
ReleaseStgMedium(pstg);
}
// this looks for the file descriptor format to get the display name of a data object
STDAPI DataObj_GetNameFromFileDescriptor(IDataObject *pdtobj, LPWSTR pszDisplayName, UINT cch)
{
HRESULT hres = E_FAIL;
STGMEDIUM mediumFGD;
InitClipboardFormats();
FILEGROUPDESCRIPTORW * pfgd = (FILEGROUPDESCRIPTORW *)DataObj_GetDataOfType(pdtobj, g_cfFileDescW, &mediumFGD);
if (pfgd)
{
if (pfgd->cItems > 0)
{
LPFILEDESCRIPTORW pfd = &(pfgd->fgd[0]);
SHUnicodeToTChar(pfd->cFileName, pszDisplayName, cch);
hres = S_OK;
}
ReleaseStgMediumHGLOBAL(&mediumFGD);
}
else
{
FILEGROUPDESCRIPTORA * pfgd = (FILEGROUPDESCRIPTORA *)DataObj_GetDataOfType(pdtobj, g_cfFileDescA, &mediumFGD);
if (pfgd)
{
if (pfgd->cItems > 0)
{
LPFILEDESCRIPTORA pfd = &(pfgd->fgd[0]);
SHAnsiToTChar(pfd->cFileName, pszDisplayName, cch);
hres = S_OK;
}
ReleaseStgMediumHGLOBAL(&mediumFGD);
}
}
return hres;
}
STDAPI SHPidlFromDataObject2(IDataObject *pdtobj, LPITEMIDLIST * ppidl)
{
HRESULT hres = E_FAIL;
STGMEDIUM medium;
InitClipboardFormats();
void *pdata = DataObj_GetDataOfType(pdtobj, g_cfHIDA, &medium);
if (pdata)
{
*ppidl = IDA_ILClone((LPIDA)pdata, 0);
if (*ppidl)
hres = S_OK;
else
hres = E_OUTOFMEMORY;
ReleaseStgMediumHGLOBAL(&medium);
}
return hres;
}
STDAPI SHPidlFromDataObject(IDataObject *pdtobj, LPITEMIDLIST *ppidl,
LPWSTR pszDisplayNameW, DWORD cchDisplayName)
{
FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
STGMEDIUM medium;
*ppidl = NULL;
HRESULT hres = pdtobj->GetData(&fmte, &medium);
if (hres == S_OK)
{
// This string is also used to store an URL in case it's an URL file
TCHAR szPath[MAX_URL_STRING];
hres = E_FAIL;
if (DragQueryFile((HDROP)medium.hGlobal, 0, szPath, ARRAYSIZE(szPath)))
{
SHFILEINFO sfi;
SHGetFileInfo(szPath, 0, &sfi, SIZEOF(sfi), SHGFI_ATTRIBUTES | SHGFI_DISPLAYNAME);
if (pszDisplayNameW)
SHTCharToUnicode(sfi.szDisplayName, pszDisplayNameW, MAX_PATH);
if (sfi.dwAttributes & SFGAO_LINK)
hres = GetLinkTargetIDList(szPath, szPath, ARRAYSIZE(szPath), ppidl);
if (FAILED(hres))
hres = IECreateFromPath(szPath, ppidl);
}
ReleaseStgMedium(&medium);
}
else
{
hres = SHPidlFromDataObject2(pdtobj, ppidl);
if (FAILED(hres))
{
void *pdata = DataObj_GetDataOfType(pdtobj, g_cfURL, &medium);
if (pdata)
{
LPSTR pszPath = (LPSTR)pdata;
if (pszDisplayNameW)
{
if (FAILED(DataObj_GetNameFromFileDescriptor(pdtobj, pszDisplayNameW, cchDisplayName)))
{
CHAR szDisplayNameA[MAX_URL_STRING];
ASSERT(cchDisplayName < MAX_URL_STRING);
SHUnicodeToAnsi(pszDisplayNameW, szDisplayNameA, cchDisplayName);
PathToDisplayNameA(pszPath, szDisplayNameA, cchDisplayName);
}
}
hres = IECreateFromPathA(pszPath, ppidl);
ReleaseStgMediumHGLOBAL(&medium);
}
}
}
return hres;
}
// BharatS - Perhaps all the stuff below here should be moved to shlwapi after beta 2 ?
typedef struct _broadcastmsgparams
{
BOOL fSendMessage; // If true - we call SendMessageTimeout
UINT uTimeout; // Only Matters if fSendMessage is set
UINT uMsg;
WPARAM wParam;
LPARAM lParam;
} BROADCAST_MSG_PARAMS;
BOOL CALLBACK EnumShellIEWindowsProc(
HWND hwnd, // handle to parent window
LPARAM lParam // application-defined value - this has the info needed for posting/sending the message
)
{
BROADCAST_MSG_PARAMS *pParams = (BROADCAST_MSG_PARAMS *)lParam;
BOOL fRet = TRUE;
if(IsExplorerWindow(hwnd) || IsFolderWindow(hwnd))
{
if(pParams->fSendMessage)
{
UINT uTimeout = (pParams->uTimeout < 4000) ? pParams->uTimeout : 4000;
LRESULT lResult;
DWORD_PTR dwpResult;
if (g_fRunningOnNT)
{
lResult = SendMessageTimeout(hwnd, pParams->uMsg, pParams->wParam, pParams->lParam, SMTO_ABORTIFHUNG | SMTO_NORMAL, uTimeout, &dwpResult);
}
else
{
lResult = SendMessageTimeoutA(hwnd, pParams->uMsg, pParams->wParam, pParams->lParam, SMTO_ABORTIFHUNG | SMTO_NORMAL, uTimeout, &dwpResult);
}
fRet = BOOLIFY(lResult);
}
else
{
fRet = PostMessage(hwnd, pParams->uMsg, pParams->wParam, pParams->lParam);
}
}
return fRet;
}
// PostShellIEBroadcastMessage is commented out since it is not used currentl
/*
STDAPI_(LRESULT) PostShellIEBroadcastMessage(UINT uMsg, WPARAM wParam, LPARAM lParam)
{
BROADCAST_MSG_PARAMS MsgParam;
MsgParam.uMsg = uMsg;
MsgParam.wParam = wParam;
MsgParam.lParam = lParam;
MsgParam.fSendMessage = FALSE;
return EnumWindows(EnumShellIEWindowsProc, (LPARAM)&MsgParam);
}
*/
//
// We can be hung if we use sendMessage, and you can not use pointers with asynchronous
// calls such as PostMessage or SendNotifyMessage. So we resort to using a timeout.
// This function should be used to broadcast notification messages, such as WM_SETTINGCHANGE,
// that pass pointers. (stevepro)
//
STDAPI_(LRESULT) SendShellIEBroadcastMessage(UINT uMsg, WPARAM wParam, LPARAM lParam, UINT uTimeout)
{
// Note that each this timeout is applied to each window that we broadcast to
BROADCAST_MSG_PARAMS MsgParam;
MsgParam.uMsg = uMsg;
MsgParam.wParam = wParam;
#ifdef UNICODE
CHAR szSection[MAX_PATH];
if (!g_fRunningOnNT && (uMsg == WM_WININICHANGE) && (0 != lParam))
{
SHUnicodeToAnsi((LPCWSTR)lParam, szSection, ARRAYSIZE(szSection));
lParam = (LPARAM)szSection;
}
#endif
MsgParam.lParam = lParam;
MsgParam.fSendMessage = TRUE;
MsgParam.uTimeout = uTimeout;
return EnumWindows(EnumShellIEWindowsProc, (LPARAM)&MsgParam);
}
// Return the parent psf and relative pidl given a pidl.
STDAPI IEBindToParentFolder(LPCITEMIDLIST pidl, IShellFolder** ppsfParent, LPCITEMIDLIST *ppidlChild)
{
HRESULT hres;
//
// if this is a rooted pidl and it is just the root
// then we can bind to the target pidl of the root instead
//
if (ILIsRooted(pidl) && ILIsEmpty(_ILNext(pidl)))
pidl = ILRootedFindIDList(pidl);
LPITEMIDLIST pidlParent = ILCloneParent(pidl);
if (pidlParent)
{
hres = IEBindToObject(pidlParent, ppsfParent);
ILFree(pidlParent);
}
else
hres = E_OUTOFMEMORY;
if (ppidlChild)
*ppidlChild = ILFindLastID(pidl);
return hres;
}
STDAPI GetDataObjectForPidl(LPCITEMIDLIST pidl, IDataObject ** ppdtobj)
{
HRESULT hres = E_FAIL;
if (pidl)
{
IShellFolder *psfParent;
LPCITEMIDLIST pidlChild;
hres = IEBindToParentFolder(pidl, &psfParent, &pidlChild);
if (SUCCEEDED(hres))
{
hres = psfParent->GetUIObjectOf(NULL, 1, &pidlChild, IID_PPV_ARG_NULL(IDataObject, ppdtobj));
psfParent->Release();
}
}
return hres;
}
// Is this pidl a Folder/Directory in the File System?
STDAPI_(BOOL) ILIsFileSysFolder(LPCITEMIDLIST pidl)
{
if (!pidl)
return FALSE;
DWORD dwAttributes = SFGAO_FOLDER | SFGAO_FILESYSTEM;
HRESULT hr = IEGetAttributesOf(pidl, &dwAttributes);
return SUCCEEDED(hr) && ((dwAttributes & (SFGAO_FOLDER | SFGAO_FILESYSTEM)) == (SFGAO_FOLDER | SFGAO_FILESYSTEM));
}
// HACKHACK HACKHACK
// the following functions are to work around the menu
// munging that happens in the shlwapi wrappers... when we're
// manipulating menus which are tracked by the system, the
// menu munging code in our shlwapi wrappers (necessary
// for xcp plugUI) trashes them since the system doesn't
// understand munged menus... hence the work arounds below.
// note that many of these functions are copies of the shlwapi
// *WrapW functions (minus the munging).
#undef LoadMenuW
// from winuser.h
EXTERN_C WINUSERAPI HMENU WINAPI LoadMenuW(HINSTANCE hInstance, LPCWSTR lpMenuName);
STDAPI_(HMENU)
LoadMenu_PrivateNoMungeW(HINSTANCE hInstance, LPCWSTR lpMenuName)
{
ASSERT(HIWORD64(lpMenuName) == 0);
if (g_fRunningOnNT)
{
return LoadMenuW(hInstance, lpMenuName);
}
return LoadMenuA(hInstance, (LPCSTR) lpMenuName);
}
#define CP_ATOM 0xFFFFFFFF /* not a string at all */
#undef InsertMenuW
// from winuser.h
EXTERN_C WINUSERAPI BOOL WINAPI InsertMenuW(IN HMENU hMenu, IN UINT uPosition, IN UINT uFlags, IN UINT_PTR uIDNewItem, IN LPCWSTR lpNewItem);
STDAPI_(BOOL) InsertMenu_PrivateNoMungeW(HMENU hMenu,
UINT uPosition,
UINT uFlags,
UINT_PTR uIDNewItem,
LPCWSTR lpNewItem)
{
if (g_fRunningOnNT)
{
return InsertMenuW(hMenu, uPosition, uFlags, uIDNewItem, lpNewItem);
}
char szMenuItem[CCH_MENUMAX];
SHUnicodeToAnsiCP((uFlags & MFT_NONSTRING) ? CP_ATOM : CP_ACP,
lpNewItem,
szMenuItem,
ARRAYSIZE(szMenuItem));
return InsertMenuA(hMenu, uPosition, uFlags, uIDNewItem, szMenuItem);
}
#ifndef NO_MLUI_IN_SHELL32
STDAPI_(HMENU) LoadMenuPopup_PrivateNoMungeW(UINT id)
{
HINSTANCE hinst = MLLoadShellLangResources();
HMENU hMenuSub = NULL;
HMENU hMenu = LoadMenu_PrivateNoMungeW(hinst, MAKEINTRESOURCEW(id));
if (hMenu)
{
hMenuSub = GetSubMenu(hMenu, 0);
if (hMenuSub)
{
RemoveMenu(hMenu, 0, MF_BYPOSITION);
}
// note this calls the shlwapi wrapper (that handles
// destroying munged menus) but it looks like
// it's safe to do so.
DestroyMenu(hMenu);
}
MLFreeLibrary(hinst);
return hMenuSub;
}
#endif // NO_MLUI_IN_SHELL32
// determine if a path is just a filespec (contains no path parts)
//
// REVIEW: we may want to count the # of elements, and make sure
// there are no illegal chars, but that is probably another routing
// PathIsValid()
//
// in:
// lpszPath path to look at
// returns:
// TRUE no ":" or "\" chars in this path
// FALSE there are path chars in there
//
//
BOOL PathIsFilePathA(LPCSTR lpszPath)
{
#ifdef UNIX
if (lpszPath[0] == '/')
#else
if ((lpszPath[0] == '\\') || (lpszPath[1] == ':'))
#endif
return TRUE;
return IsFileUrl(lpszPath);
}
//
// PrepareURLForDisplay
//
// Decodes without stripping file:// prefix
//
STDAPI_(BOOL) PrepareURLForDisplayA(LPCSTR psz, LPSTR pszOut, LPDWORD pcchOut)
{
if (PathIsFilePathA(psz))
{
if (IsFileUrl(psz))
return SUCCEEDED(PathCreateFromUrlA(psz, pszOut, pcchOut, 0));
StrCpyNA(pszOut, psz, *pcchOut);
*pcchOut = lstrlenA(pszOut);
return TRUE;
}
return SUCCEEDED(UrlUnescapeA((LPSTR)psz, pszOut, pcchOut, 0));
}
#undef InsertMenuW
#undef LoadMenuW
// from w95wraps.h
#define InsertMenuW InsertMenuWrapW
#define LoadMenuW LoadMenuWrapW
STDAPI SHTitleFromPidl(LPCITEMIDLIST pidl, LPTSTR psz, DWORD cch, BOOL fFullPath)
{
// Tries to get a system-displayable string from a pidl.
// (On Win9x and NT4, User32 doesn't support font-linking,
// so we can't display non-system language strings as window
// titles or menu items. In those cases, we call this function
// to grab the path/URL instead, which will likely be system-
// displayable).
UINT uType;
*psz = NULL;
TCHAR szName[MAX_URL_STRING];
if (fFullPath)
uType = SHGDN_FORPARSING;
else
uType = SHGDN_NORMAL;
uType |= SHGDN_FORADDRESSBAR;
DWORD dwAttrib = SFGAO_LINK;
HRESULT hr = IEGetNameAndFlags(pidl, uType, szName, SIZECHARS(szName), &dwAttrib);
if (SUCCEEDED(hr))
{
if ((uType & SHGDN_FORPARSING) && (dwAttrib & SFGAO_LINK))
{
// folder shortcut special case
IShellLinkA *psl; // Use A version for W95.
if (SUCCEEDED(SHGetUIObjectFromFullPIDL(pidl, NULL, IID_PPV_ARG(IShellLinkA, &psl))))
{
LPITEMIDLIST pidlTarget;
if (SUCCEEDED(psl->GetIDList(&pidlTarget)) && pidlTarget)
{
hr = IEGetNameAndFlags(pidlTarget, uType, szName, SIZECHARS(szName), NULL);
ILFree(pidlTarget);
}
}
}
}
else
{
// didn't work, try the reverse
uType ^= SHGDN_FORPARSING; // flip the for parsing bit
hr = IEGetNameAndFlags(pidl, uType, szName, SIZECHARS(szName), NULL);
// some old namespaces get confused by all our funny bits...
if (FAILED(hr))
{
hr = IEGetNameAndFlags(pidl, SHGDN_NORMAL, szName, SIZECHARS(szName), NULL);
}
}
if (SUCCEEDED(hr))
{
SHRemoveURLTurd(szName);
SHCleanupUrlForDisplay(szName);
// HTTP URLs are not escaped because they come from the
// user or web page which is required to create correctly
// escaped URLs. FTP creates then via results from the
// FTP session, so their pieces (username, password, path)
// need to be escaped when put in URL form. However,
// we are going to put that URL into the Caption Bar, and
// and we want to unescape it because it's assumed to be
// a DBCS name. All of this is done because unescaped URLs
// are pretty. (NT #1272882)
if (URL_SCHEME_FTP == GetUrlScheme(szName))
{
CHAR szUrlTemp[MAX_BROWSER_WINDOW_TITLE];
CHAR szUnEscaped[MAX_BROWSER_WINDOW_TITLE];
DWORD cchSizeTemp = ARRAYSIZE(szUnEscaped);
// This thunking stuff is necessary. Unescaping won't
// gell into DBCS chars unless it's in ansi.
SHTCharToAnsi(szName, szUrlTemp, ARRAYSIZE(szUrlTemp));
PrepareURLForDisplayA(szUrlTemp, szUnEscaped, &cchSizeTemp);
SHAnsiToTChar(szUnEscaped, psz, cch);
}
else
{
StrCpyN(psz, szName, cch);
}
}
return hr;
}
BOOL IsSpecialUrl(LPCWSTR pchURL)
{
UINT uProt = GetUrlSchemeW(pchURL);
return (URL_SCHEME_JAVASCRIPT == uProt ||
URL_SCHEME_VBSCRIPT == uProt ||
URL_SCHEME_ABOUT == uProt);
}
//encode any incoming %1 so that people can't spoof our domain security code
HRESULT WrapSpecialUrl(BSTR * pbstrUrl)
{
HRESULT hr = S_OK;
TCHAR *pchSafeUrl = NULL;
TCHAR *pch;
TCHAR achUrl[4096];
DWORD dwSize;
BSTR bstrURL = *pbstrUrl;
int cSize;
if (IsSpecialUrl(bstrURL))
{
//
// If this is javascript:, vbscript: or about:, append the
// url of this document so that on the other side we can
// decide whether or not to allow script execution.
//
// QFE 2735 (Georgi XDomain): [alanau]
//
// If the special URL contains an %00 sequence, then it will be converted to a Null char when
// encoded. This will effectively truncate the Security ID. For now, simply disallow this
// sequence, and display a "Permission Denied" script error.
//
if (StrStrW(bstrURL, L"%00"))
{
hr = E_ACCESSDENIED;
goto Cleanup;
}
// Copy the URL so we can munge it.
//
cSize = SysStringLen(bstrURL) + 1;
pchSafeUrl = new TCHAR[cSize];
if (!pchSafeUrl)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
StrCpyN(pchSafeUrl, bstrURL, cSize);
// someone could put in a string like this:
// %2501 OR %252501 OR %25252501
// which, depending on the number of decoding steps, will bypass security
// so, just keep decoding while there are %s and the string is getting shorter
int nPreviousLen = 0;
while ( (nPreviousLen != lstrlen(pchSafeUrl)) && (StrChrW(pchSafeUrl, L'%')))
{
nPreviousLen = lstrlen(pchSafeUrl);
int nNumPercents;
int nNumPrevPercents = 0;
// Reduce the URL
//
for (;;)
{
// Count the % signs.
//
nNumPercents = 0;
pch = pchSafeUrl;
while (pch = StrChrW(pch, L'%'))
{
pch++;
nNumPercents++;
}
// If the number of % signs has changed, we've reduced the URL one iteration.
//
if (nNumPercents != nNumPrevPercents)
{
// Encode the URL
hr = THR(CoInternetParseUrl(pchSafeUrl,
PARSE_ENCODE,
0,
achUrl,
ARRAYSIZE(achUrl),
&dwSize,
0));
StrCpyN(pchSafeUrl, achUrl, cSize);
nNumPrevPercents = nNumPercents;
}
else
{
// The URL is fully reduced. Break out of loop.
//
break;
}
}
}
// Now scan for '\1' characters.
//
if (StrChrW(pchSafeUrl, L'\1'))
{
// If there are '\1' characters, we can't guarantee the safety. Put up "Permission Denied".
//
hr = E_ACCESSDENIED;
goto Cleanup;
}
SysFreeString(*pbstrUrl);
*pbstrUrl = SysAllocString(pchSafeUrl);
if (!*pbstrUrl)
{
hr = E_OUTOFMEMORY;
goto Cleanup;
}
}
Cleanup:
delete [] pchSafeUrl;
return hr;
}
HRESULT WrapSpecialUrlFlat(LPWSTR pszUrl, DWORD cchUrl)
{
HRESULT hr = S_OK;
if (IsSpecialUrl(pszUrl))
{
//
// If this is javascript:, vbscript: or about:, append the
// url of this document so that on the other side we can
// decide whether or not to allow script execution.
//
// QFE 2735 (Georgi XDomain): [alanau]
//
// If the special URL contains an %00 sequence, then it will be converted to a Null char when
// encoded. This will effectively truncate the Security ID. For now, simply disallow this
// sequence, and display a "Permission Denied" script error.
//
if (StrStrW(pszUrl, L"%00"))
{
hr = E_ACCESSDENIED;
}
else
{
// munge the url in place
//
// someone could put in a string like this:
// %2501 OR %252501 OR %25252501
// which, depending on the number of decoding steps, will bypass security
// so, just keep decoding while there are %s and the string is getting shorter
int nPreviousLen = 0;
while ( (nPreviousLen != lstrlen(pszUrl)) && (StrChrW(pszUrl, L'%')))
{
nPreviousLen = lstrlen(pszUrl);
int nNumPercents;
int nNumPrevPercents = 0;
// Reduce the URL
//
for (;;)
{
// Count the % signs.
//
nNumPercents = 0;
WCHAR *pch = pszUrl;
while (pch = StrChrW(pch, L'%'))
{
pch++;
nNumPercents++;
}
// If the number of % signs has changed, we've reduced the URL one iteration.
//
if (nNumPercents != nNumPrevPercents)
{
WCHAR szBuf[MAX_URL_STRING];
DWORD dwSize;
// Encode the URL
hr = THR(CoInternetParseUrl(pszUrl,
PARSE_ENCODE,
0,
szBuf,
ARRAYSIZE(szBuf),
&dwSize,
0));
StrCpyN(pszUrl, szBuf, cchUrl);
nNumPrevPercents = nNumPercents;
}
else
{
// The URL is fully reduced. Break out of loop.
//
break;
}
}
}
// Now scan for '\1' characters.
//
if (StrChrW(pszUrl, L'\1'))
{
// If there are '\1' characters, we can't guarantee the safety. Put up "Permission Denied".
//
hr = E_ACCESSDENIED;
}
}
}
return hr;
}
STDAPI GetBrowserFrameOptions(IUnknown *punkFolder, IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions)
{
HRESULT hr = E_INVALIDARG;
*pdwOptions = BFO_NONE;
if (punkFolder)
{
IBrowserFrameOptions *pbfo;
hr = punkFolder->QueryInterface(IID_PPV_ARG(IBrowserFrameOptions, &pbfo));
if (SUCCEEDED(hr))
{
hr = pbfo->GetFrameOptions(dwMask, pdwOptions);
pbfo->Release();
}
}
return hr;
}
STDAPI GetBrowserFrameOptionsPidl(IN LPCITEMIDLIST pidl, IN BROWSERFRAMEOPTIONS dwMask, OUT BROWSERFRAMEOPTIONS * pdwOptions)
{
HRESULT hr = E_INVALIDARG;
*pdwOptions = BFO_NONE;
if (pidl)
{
IBrowserFrameOptions *pbfo;
hr = IEBindToObjectEx(pidl, NULL, IID_PPV_ARG(IBrowserFrameOptions, &pbfo));
if (SUCCEEDED(hr) && pbfo)
{
hr = pbfo->GetFrameOptions(dwMask, pdwOptions);
pbfo->Release();
}
}
return hr;
}
// Return TRUE only if all the bits in dwMask are set.
STDAPI_(BOOL) IsBrowserFrameOptionsSet(IN IShellFolder * psf, IN BROWSERFRAMEOPTIONS dwMask)
{
BOOL fSet = FALSE;
BROWSERFRAMEOPTIONS dwOptions = 0;
if (SUCCEEDED(GetBrowserFrameOptions(psf, dwMask, &dwOptions)) &&
(dwOptions == dwMask))
{
fSet = TRUE;
}
return fSet;
}
// Return TRUE only if all the bits in dwMask are set.
STDAPI_(BOOL) IsBrowserFrameOptionsPidlSet(IN LPCITEMIDLIST pidl, IN BROWSERFRAMEOPTIONS dwMask)
{
BOOL fSet = FALSE;
BROWSERFRAMEOPTIONS dwOptions = 0;
if (SUCCEEDED(GetBrowserFrameOptionsPidl(pidl, dwMask, &dwOptions)) &&
(dwOptions == dwMask))
{
fSet = TRUE;
}
return fSet;
}
STDAPI_(BOOL) IsFTPFolder(IShellFolder * psf)
{
BOOL fIsFTPFolder = FALSE;
CLSID clsid;
if (psf && SUCCEEDED(IUnknown_GetClassID(psf, &clsid)))
{
// Is this an FTP Folder?
if (IsEqualIID(clsid, CLSID_FtpFolder))
fIsFTPFolder = TRUE;
else
{
// Not directly, but let's see if it is an Folder Shortcut to
// an FTP Folder
if (IsEqualIID(clsid, CLSID_FolderShortcut))
{
IShellLinkA * psl;
HRESULT hr = psf->QueryInterface(IID_PPV_ARG(IShellLinkA, &psl));
if (SUCCEEDED(hr))
{
LPITEMIDLIST pidl;
hr = psl->GetIDList(&pidl);
if (SUCCEEDED(hr))
{
IShellFolder * psfTarget;
hr = IEBindToObject(pidl, &psfTarget);
if (SUCCEEDED(hr))
{
if (SUCCEEDED(IUnknown_GetClassID(psfTarget, &clsid)) &&
IsEqualIID(clsid, CLSID_FtpFolder))
{
fIsFTPFolder = TRUE;
}
psfTarget->Release();
}
ILFree(pidl);
}
psl->Release();
}
}
}
}
return fIsFTPFolder;
}
//+---------------------------------------------------------------------------
//
// Function: DrawFocusRectangle
//
// Synopsis: draws the focus rectangle for the edit control
//
//----------------------------------------------------------------------------
void DrawFocusRectangle (HWND hwnd, HDC hdc)
{
RECT rect;
BOOL fReleaseDC = FALSE;
if ( hdc == NULL )
{
hdc = GetDC(hwnd);
if ( hdc == NULL )
{
return;
}
fReleaseDC = TRUE;
}
GetClientRect(hwnd, &rect);
DrawFocusRect(hdc, &rect);
if ( fReleaseDC == TRUE )
{
ReleaseDC(hwnd, hdc);
}
}
typedef struct {
LPSTR psz;
LPCWSTR pwsz;
LONG byteoffset;
BOOL fStreamIn;
} STREAMIN_HELPER_STRUCT;
DWORD CALLBACK SetRicheditTextWCallback(
DWORD_PTR dwCookie, // application-defined value
LPBYTE pbBuff, // pointer to a buffer
LONG cb, // number of bytes to read or write
LONG *pcb // pointer to number of bytes transferred
)
{
STREAMIN_HELPER_STRUCT *pHelpStruct = (STREAMIN_HELPER_STRUCT *) dwCookie;
LONG lRemain = ((wcslen(pHelpStruct->pwsz) * sizeof(WCHAR)) - pHelpStruct->byteoffset);
if (pHelpStruct->fStreamIn)
{
//
// The whole string can be copied first time
//
if ((cb >= (LONG) (wcslen(pHelpStruct->pwsz) * sizeof(WCHAR))) && (pHelpStruct->byteoffset == 0))
{
memcpy(pbBuff, pHelpStruct->pwsz, wcslen(pHelpStruct->pwsz) * sizeof(WCHAR));
*pcb = wcslen(pHelpStruct->pwsz) * sizeof(WCHAR);
pHelpStruct->byteoffset = *pcb;
}
//
// The whole string has been copied, so terminate the streamin callbacks
// by setting the num bytes copied to 0
//
else if (((LONG)(wcslen(pHelpStruct->pwsz) * sizeof(WCHAR))) <= pHelpStruct->byteoffset)
{
*pcb = 0;
}
//
// The rest of the string will fit in this buffer
//
else if (cb >= (LONG) ((wcslen(pHelpStruct->pwsz) * sizeof(WCHAR)) - pHelpStruct->byteoffset))
{
memcpy(
pbBuff,
((BYTE *)pHelpStruct->pwsz) + pHelpStruct->byteoffset,
((wcslen(pHelpStruct->pwsz) * sizeof(WCHAR)) - pHelpStruct->byteoffset));
*pcb = ((wcslen(pHelpStruct->pwsz) * sizeof(WCHAR)) - pHelpStruct->byteoffset);
pHelpStruct->byteoffset += ((wcslen(pHelpStruct->pwsz) * sizeof(WCHAR)) - pHelpStruct->byteoffset);
}
//
// copy as much as possible
//
else
{
memcpy(
pbBuff,
((BYTE *)pHelpStruct->pwsz) + pHelpStruct->byteoffset,
cb);
*pcb = cb;
pHelpStruct->byteoffset += cb;
}
}
else
{
//
// This is the EM_STREAMOUT which is only used during the testing of
// the richedit2.0 functionality. (we know our buffer is 32 bytes)
//
if (cb <= 32)
{
memcpy(pHelpStruct->psz, pbBuff, cb);
}
*pcb = cb;
}
return 0;
}
void SetRicheditIMFOption(HWND hWndRichEdit)
{
DWORD dwOptions;
dwOptions = (DWORD)SendMessageW(hWndRichEdit, EM_GETLANGOPTIONS, 0, 0);
dwOptions |= IMF_UIFONTS;
SendMessageW(hWndRichEdit, EM_SETLANGOPTIONS, 0, dwOptions);
}
DWORD SetRicheditTextW(HWND hwndDlg, UINT id, LPCWSTR pwsz)
{
EDITSTREAM editStream;
STREAMIN_HELPER_STRUCT helpStruct;
SetRicheditIMFOption(GetDlgItem(hwndDlg, id));
//
// setup the edit stream struct since it is the same no matter what
//
editStream.dwCookie = (DWORD_PTR) &helpStruct;
editStream.dwError = 0;
editStream.pfnCallback = SetRicheditTextWCallback;
helpStruct.pwsz = pwsz;
helpStruct.byteoffset = 0;
helpStruct.fStreamIn = TRUE;
SendDlgItemMessageW(hwndDlg, id, EM_STREAMIN, SF_TEXT | SF_UNICODE, (LPARAM) &editStream);
return editStream.dwError;
}
//+---------------------------------------------------------------------------
//
// Function: RenderStringToEditControlW
//
// Synopsis: renders a string to the control given and if requested, gives
// it a link look and feel, subclassed to the wndproc given
//
// Arguments: [hwndDlg] -- dialog window handle
// [pwsz] -- string
// [wndproc] -- wndproc
// [uID] -- ID of control
//
//
// Notes:
//
//----------------------------------------------------------------------------
void RenderStringToEditControlW (
HWND hwndDlg,
LPCWSTR pwsz,
WNDPROC wndproc,
UINT uID)
{
HWND hControl;
//
// Get the control and set the text on it, make sure the background is right
//
hControl = GetDlgItem(hwndDlg, uID);
SetRicheditIMFOption(GetDlgItem(hwndDlg, uID));
SetRicheditTextW(hwndDlg, uID, L"");
SetRicheditTextW(hwndDlg, uID, pwsz);
SendMessage(
hControl,
EM_SETBKGNDCOLOR,
0,
(LPARAM)GetSysColor(COLOR_3DFACE)
);
//
// Update for the link look
//
CHARFORMAT cf;
memset(&cf, 0, sizeof(CHARFORMAT));
cf.cbSize = sizeof(CHARFORMAT);
cf.dwMask = CFM_COLOR | CFM_UNDERLINE | CFM_LINK;
cf.crTextColor = RGB(0, 0, 255);
cf.dwEffects |= CFE_UNDERLINE | CFE_LINK;
SendMessage(hControl, EM_SETCHARFORMAT, SCF_ALL, (LPARAM)&cf);
//subclass the window proc so we can handle the link specially
LONG_PTR PrevWndProc = GetWindowLongPtr(hControl, GWLP_WNDPROC);
SetWindowLongPtr(hControl, GWLP_USERDATA, (LONG_PTR)PrevWndProc);
SetWindowLongPtr(hControl, GWLP_WNDPROC, (LONG_PTR)wndproc);
}