// // 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 //---------------------------------------------------------------------------- // // 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); }