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

229 lines
7.1 KiB
C++

//
// Wrapper functions for shell interfaces
//
// Many ISVs mess up various IShellFolder methods, so we centralize the
// workarounds so everybody wins.
//
// Someday, IExtractIcon and IShellLink wrappers may also be added, should
// the need arise.
//
#include "priv.h"
#include <shlobj.h>
//----------------------------------------------------------------------------
//
// IShellFolder::GetDisplayNameOf was not very well documented. Lots of
// people don't realize that the SHGDN values are flags, so they use
// equality tests instead of bit tests. So whenever we add a new flag,
// these people say "Huh? I don't understand." So we have to keep
// retrying with fewer and fewer flags until finally they get something
// they like. SHGDN_FORPARSING has the opposite problem: Some people
// demand that the flag be set.
//
// This array lists the things we try to do to get the uFlags into a state
// that the app will eventually like.
//
// We walk through the list and do this:
//
// uFlags = (uFlags & AND) | OR
//
// Most of the time, the entry will turn off a bit in the uFlags, but
// SHGDN_FORPARSING is weird and it's a flag you actually want to turn on
// instead of off.
//
typedef struct GDNCOMPAT {
DWORD dwAnd;
DWORD dwOr;
DWORD dwAllow; // flag to allow this rule to fire
} GDNCOMPAT;
#define GDNADDFLAG(f) ~0, f // Add a flag to uFlags
#define GDNDELFLAG(f) ~f, 0 // Remove a flag from uFlags
#define ISHGDN2_CANREMOVEOTHERFLAGS 0x80000000
GDNCOMPAT c_gdnc[] = {
{ GDNDELFLAG(SHGDN_FOREDITING), ISHGDN2_CANREMOVEOTHERFLAGS }, // Some apps don't like this flag
{ GDNDELFLAG(SHGDN_FORADDRESSBAR), ISHGDN2_CANREMOVEOTHERFLAGS }, // Some apps don't like this flag
{ GDNADDFLAG(SHGDN_FORPARSING), ISHGDN2_CANREMOVEOTHERFLAGS }, // Some apps require this flag
{ GDNDELFLAG(SHGDN_FORPARSING), ISHGDN2_CANREMOVEFORPARSING }, // And others don't like it
{ GDNDELFLAG(SHGDN_INFOLDER), ISHGDN2_CANREMOVEOTHERFLAGS }, // Desperation - remove this flag too
};
//
// These are the return values we tend to get back when people see
// flags they don't like.
//
BOOL __inline IsBogusHRESULT(HRESULT hres)
{
return hres == E_FAIL ||
hres == E_INVALIDARG ||
hres == E_NOTIMPL;
}
//
// dwFlags2 controls how aggressively we try to find a working display name.
//
// ISHGDN2_CANREMOVEFORPARSING
// Normally, we do not turn off the SHGDN_FORPARSING flag because
// if a caller asks for the parse name, it probably really wants the
// parse name. This flag indicates that we are allowed to turn off
// SHGDN_FORPARSING if we think it'll help.
//
STDAPI IShellFolder_GetDisplayNameOf(
IShellFolder *psf,
LPCITEMIDLIST pidl,
DWORD uFlags,
LPSTRRET lpName,
DWORD dwFlags2)
{
HRESULT hres;
hres = psf->GetDisplayNameOf(pidl, uFlags, lpName);
if (!IsBogusHRESULT(hres))
return hres;
int i;
DWORD uFlagsOrig = uFlags;
//
// If the caller didn't pass SHGDN_FORPARSING, then clearly it's
// safe to remove it.
//
if (!(uFlags & SHGDN_FORPARSING)) {
dwFlags2 |= ISHGDN2_CANREMOVEFORPARSING;
}
// We can always remove other flags.
dwFlags2 |= ISHGDN2_CANREMOVEOTHERFLAGS;
for (i = 0; i < ARRAYSIZE(c_gdnc); i++)
{
if (c_gdnc[i].dwAllow & dwFlags2)
{
DWORD uFlagsNew = (uFlags & c_gdnc[i].dwAnd) | c_gdnc[i].dwOr;
if (uFlagsNew != uFlags)
{
uFlags = uFlagsNew;
hres = psf->GetDisplayNameOf(pidl, uFlags, lpName);
if (!IsBogusHRESULT(hres))
return hres;
}
}
}
// By now, we should've removed all the flags, except perhaps for
// SHGDN_FORPARSING.
if (dwFlags2 & ISHGDN2_CANREMOVEFORPARSING) {
ASSERT(uFlags == SHGDN_NORMAL);
} else {
ASSERT(uFlags == SHGDN_NORMAL || uFlags == SHGDN_FORPARSING);
}
return hres;
}
//----------------------------------------------------------------------------
//
// The documentation on IShellFolder::ParseDisplayName wasn't clear that
// pchEaten and pdwAttributes can be NULL, and some people dereference
// them unconditionally. So make sure it's safe to dereference them.
//
// It is also popular to forget to set *ppidl=NULL on failure, so we null
// it out here.
//
// We request no attributes, so people who aren't buggy won't go out of
// their way trying to retrieve expensive attributes.
//
STDAPI IShellFolder_ParseDisplayName(
IShellFolder *psf,
HWND hwnd,
LPBC pbc,
LPOLESTR pszDisplayName,
ULONG *pchEaten,
LPITEMIDLIST *ppidl,
ULONG *pdwAttributes)
{
ULONG cchEaten;
ULONG dwAttributes = 0;
if (pchEaten == NULL)
pchEaten = &cchEaten;
if (pdwAttributes == NULL)
pdwAttributes = &dwAttributes;
if (ppidl)
*ppidl = NULL;
return psf->ParseDisplayName(hwnd, pbc, pszDisplayName, pchEaten, ppidl, pdwAttributes);
}
STDAPI IShellFolder_CompareIDs(IShellFolder *psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
{
// We have new bits here...
if (lParam & ~SHCIDS_COLUMNMASK)
{
IShellFolder2* psf2;
if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
{
psf2->Release();
}
else
{
// But we can't send them to legacy IShellFolder implementations
lParam &= SHCIDS_COLUMNMASK;
}
}
return psf->CompareIDs(lParam, pidl1, pidl2);
}
//----------------------------------------------------------------------------
//
// IShellFolder::EnumObjects
//
CLSID CLSID_ZipFolder =
{ 0xe88dcce0, 0xb7b3, 0x11d1, { 0xa9, 0xf0, 0x00, 0xaa, 0x00, 0x60, 0xfa, 0x31 } };
STDAPI IShellFolder_EnumObjects(
IShellFolder *psf,
HWND hwnd,
DWORD grfFlags,
IEnumIDList **ppenumIDList)
{
if (hwnd == NULL || hwnd == GetDesktopWindow())
{
// The first parameter to EnumObjects is supposed to be the window
// on which to parent UI, or NULL for no UI, or GetDesktopWindow()
// for "parentless UI".
//
// Win98 Plus! Zip Folders takes the hwnd and uses it as the basis
// for a search for a rebar window, since they (for some bizarre
// reason) want to hide the address bar when an enumeration starts.
//
// We used to pass NULL or GetDesktopWindow(), but this caused zip
// folders to start searching from the desktop, which means that
// it eventually finds the taskbar and tries to send it
// inter-process rebar messages, which causes the shell to fault.
//
// When we discover we are about to pass NULL to Zip Folders,
// we change it to HWND_BOTTOM. This is not a valid window handle,
// which causes Zip Folders' search to bail out quickly and it ends
// up not killing anyone.
//
CLSID clsid;
if (SUCCEEDED(IUnknown_GetClassID(psf, &clsid)) &&
IsEqualCLSID(clsid, CLSID_ZipFolder))
hwnd = HWND_BOTTOM;
}
return psf->EnumObjects(hwnd, grfFlags, ppenumIDList);
}