Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

228 lines
7.1 KiB

  1. //
  2. // Wrapper functions for shell interfaces
  3. //
  4. // Many ISVs mess up various IShellFolder methods, so we centralize the
  5. // workarounds so everybody wins.
  6. //
  7. // Someday, IExtractIcon and IShellLink wrappers may also be added, should
  8. // the need arise.
  9. //
  10. #include "priv.h"
  11. #include <shlobj.h>
  12. //----------------------------------------------------------------------------
  13. //
  14. // IShellFolder::GetDisplayNameOf was not very well documented. Lots of
  15. // people don't realize that the SHGDN values are flags, so they use
  16. // equality tests instead of bit tests. So whenever we add a new flag,
  17. // these people say "Huh? I don't understand." So we have to keep
  18. // retrying with fewer and fewer flags until finally they get something
  19. // they like. SHGDN_FORPARSING has the opposite problem: Some people
  20. // demand that the flag be set.
  21. //
  22. // This array lists the things we try to do to get the uFlags into a state
  23. // that the app will eventually like.
  24. //
  25. // We walk through the list and do this:
  26. //
  27. // uFlags = (uFlags & AND) | OR
  28. //
  29. // Most of the time, the entry will turn off a bit in the uFlags, but
  30. // SHGDN_FORPARSING is weird and it's a flag you actually want to turn on
  31. // instead of off.
  32. //
  33. typedef struct GDNCOMPAT {
  34. DWORD dwAnd;
  35. DWORD dwOr;
  36. DWORD dwAllow; // flag to allow this rule to fire
  37. } GDNCOMPAT;
  38. #define GDNADDFLAG(f) ~0, f // Add a flag to uFlags
  39. #define GDNDELFLAG(f) ~f, 0 // Remove a flag from uFlags
  40. #define ISHGDN2_CANREMOVEOTHERFLAGS 0x80000000
  41. GDNCOMPAT c_gdnc[] = {
  42. { GDNDELFLAG(SHGDN_FOREDITING), ISHGDN2_CANREMOVEOTHERFLAGS }, // Some apps don't like this flag
  43. { GDNDELFLAG(SHGDN_FORADDRESSBAR), ISHGDN2_CANREMOVEOTHERFLAGS }, // Some apps don't like this flag
  44. { GDNADDFLAG(SHGDN_FORPARSING), ISHGDN2_CANREMOVEOTHERFLAGS }, // Some apps require this flag
  45. { GDNDELFLAG(SHGDN_FORPARSING), ISHGDN2_CANREMOVEFORPARSING }, // And others don't like it
  46. { GDNDELFLAG(SHGDN_INFOLDER), ISHGDN2_CANREMOVEOTHERFLAGS }, // Desperation - remove this flag too
  47. };
  48. //
  49. // These are the return values we tend to get back when people see
  50. // flags they don't like.
  51. //
  52. BOOL __inline IsBogusHRESULT(HRESULT hres)
  53. {
  54. return hres == E_FAIL ||
  55. hres == E_INVALIDARG ||
  56. hres == E_NOTIMPL;
  57. }
  58. //
  59. // dwFlags2 controls how aggressively we try to find a working display name.
  60. //
  61. // ISHGDN2_CANREMOVEFORPARSING
  62. // Normally, we do not turn off the SHGDN_FORPARSING flag because
  63. // if a caller asks for the parse name, it probably really wants the
  64. // parse name. This flag indicates that we are allowed to turn off
  65. // SHGDN_FORPARSING if we think it'll help.
  66. //
  67. STDAPI IShellFolder_GetDisplayNameOf(
  68. IShellFolder *psf,
  69. LPCITEMIDLIST pidl,
  70. DWORD uFlags,
  71. LPSTRRET lpName,
  72. DWORD dwFlags2)
  73. {
  74. HRESULT hres;
  75. hres = psf->GetDisplayNameOf(pidl, uFlags, lpName);
  76. if (!IsBogusHRESULT(hres))
  77. return hres;
  78. int i;
  79. DWORD uFlagsOrig = uFlags;
  80. //
  81. // If the caller didn't pass SHGDN_FORPARSING, then clearly it's
  82. // safe to remove it.
  83. //
  84. if (!(uFlags & SHGDN_FORPARSING)) {
  85. dwFlags2 |= ISHGDN2_CANREMOVEFORPARSING;
  86. }
  87. // We can always remove other flags.
  88. dwFlags2 |= ISHGDN2_CANREMOVEOTHERFLAGS;
  89. for (i = 0; i < ARRAYSIZE(c_gdnc); i++)
  90. {
  91. if (c_gdnc[i].dwAllow & dwFlags2)
  92. {
  93. DWORD uFlagsNew = (uFlags & c_gdnc[i].dwAnd) | c_gdnc[i].dwOr;
  94. if (uFlagsNew != uFlags)
  95. {
  96. uFlags = uFlagsNew;
  97. hres = psf->GetDisplayNameOf(pidl, uFlags, lpName);
  98. if (!IsBogusHRESULT(hres))
  99. return hres;
  100. }
  101. }
  102. }
  103. // By now, we should've removed all the flags, except perhaps for
  104. // SHGDN_FORPARSING.
  105. if (dwFlags2 & ISHGDN2_CANREMOVEFORPARSING) {
  106. ASSERT(uFlags == SHGDN_NORMAL);
  107. } else {
  108. ASSERT(uFlags == SHGDN_NORMAL || uFlags == SHGDN_FORPARSING);
  109. }
  110. return hres;
  111. }
  112. //----------------------------------------------------------------------------
  113. //
  114. // The documentation on IShellFolder::ParseDisplayName wasn't clear that
  115. // pchEaten and pdwAttributes can be NULL, and some people dereference
  116. // them unconditionally. So make sure it's safe to dereference them.
  117. //
  118. // It is also popular to forget to set *ppidl=NULL on failure, so we null
  119. // it out here.
  120. //
  121. // We request no attributes, so people who aren't buggy won't go out of
  122. // their way trying to retrieve expensive attributes.
  123. //
  124. STDAPI IShellFolder_ParseDisplayName(
  125. IShellFolder *psf,
  126. HWND hwnd,
  127. LPBC pbc,
  128. LPOLESTR pszDisplayName,
  129. ULONG *pchEaten,
  130. LPITEMIDLIST *ppidl,
  131. ULONG *pdwAttributes)
  132. {
  133. ULONG cchEaten;
  134. ULONG dwAttributes = 0;
  135. if (pchEaten == NULL)
  136. pchEaten = &cchEaten;
  137. if (pdwAttributes == NULL)
  138. pdwAttributes = &dwAttributes;
  139. if (ppidl)
  140. *ppidl = NULL;
  141. return psf->ParseDisplayName(hwnd, pbc, pszDisplayName, pchEaten, ppidl, pdwAttributes);
  142. }
  143. STDAPI IShellFolder_CompareIDs(IShellFolder *psf, LPARAM lParam, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  144. {
  145. // We have new bits here...
  146. if (lParam & ~SHCIDS_COLUMNMASK)
  147. {
  148. IShellFolder2* psf2;
  149. if (SUCCEEDED(psf->QueryInterface(IID_PPV_ARG(IShellFolder2, &psf2))))
  150. {
  151. psf2->Release();
  152. }
  153. else
  154. {
  155. // But we can't send them to legacy IShellFolder implementations
  156. lParam &= SHCIDS_COLUMNMASK;
  157. }
  158. }
  159. return psf->CompareIDs(lParam, pidl1, pidl2);
  160. }
  161. //----------------------------------------------------------------------------
  162. //
  163. // IShellFolder::EnumObjects
  164. //
  165. CLSID CLSID_ZipFolder =
  166. { 0xe88dcce0, 0xb7b3, 0x11d1, { 0xa9, 0xf0, 0x00, 0xaa, 0x00, 0x60, 0xfa, 0x31 } };
  167. STDAPI IShellFolder_EnumObjects(
  168. IShellFolder *psf,
  169. HWND hwnd,
  170. DWORD grfFlags,
  171. IEnumIDList **ppenumIDList)
  172. {
  173. if (hwnd == NULL || hwnd == GetDesktopWindow())
  174. {
  175. // The first parameter to EnumObjects is supposed to be the window
  176. // on which to parent UI, or NULL for no UI, or GetDesktopWindow()
  177. // for "parentless UI".
  178. //
  179. // Win98 Plus! Zip Folders takes the hwnd and uses it as the basis
  180. // for a search for a rebar window, since they (for some bizarre
  181. // reason) want to hide the address bar when an enumeration starts.
  182. //
  183. // We used to pass NULL or GetDesktopWindow(), but this caused zip
  184. // folders to start searching from the desktop, which means that
  185. // it eventually finds the taskbar and tries to send it
  186. // inter-process rebar messages, which causes the shell to fault.
  187. //
  188. // When we discover we are about to pass NULL to Zip Folders,
  189. // we change it to HWND_BOTTOM. This is not a valid window handle,
  190. // which causes Zip Folders' search to bail out quickly and it ends
  191. // up not killing anyone.
  192. //
  193. CLSID clsid;
  194. if (SUCCEEDED(IUnknown_GetClassID(psf, &clsid)) &&
  195. IsEqualCLSID(clsid, CLSID_ZipFolder))
  196. hwnd = HWND_BOTTOM;
  197. }
  198. return psf->EnumObjects(hwnd, grfFlags, ppenumIDList);
  199. }