229 lines
7.1 KiB
C++
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);
|
|
}
|