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.

243 lines
7.4 KiB

  1. #include "stock.h"
  2. #pragma hdrstop
  3. #include <idhidden.h>
  4. // the last word of the pidl is where we store the hidden offset
  5. #define _ILHiddenOffset(pidl) (*((WORD UNALIGNED *)(((BYTE *)_ILNext(pidl)) - sizeof(WORD))))
  6. #define _ILSetHiddenOffset(pidl, cb) ((*((WORD UNALIGNED *)(((BYTE *)_ILNext(pidl)) - sizeof(WORD)))) = (WORD)cb)
  7. #define _ILIsHidden(pidhid) (HIWORD(pidhid->id) == HIWORD(IDLHID_EMPTY))
  8. STDAPI_(PCIDHIDDEN) _ILNextHidden(PCIDHIDDEN pidhid, LPCITEMIDLIST pidlLimit)
  9. {
  10. PCIDHIDDEN pidhidNext = (PCIDHIDDEN) _ILNext((LPCITEMIDLIST)pidhid);
  11. if ((BYTE *)pidhidNext < (BYTE *)pidlLimit && _ILIsHidden(pidhidNext))
  12. {
  13. return pidhidNext;
  14. }
  15. // if we ever go past the limit,
  16. // then this is not really a hidden id
  17. // or we have messed up on some calculation.
  18. ASSERT((BYTE *)pidhidNext == (BYTE *)pidlLimit);
  19. return NULL;
  20. }
  21. STDAPI_(PCIDHIDDEN) _ILFirstHidden(LPCITEMIDLIST pidl)
  22. {
  23. WORD cbHidden = _ILHiddenOffset(pidl);
  24. if (cbHidden && cbHidden + sizeof(HIDDENITEMID) < pidl->mkid.cb)
  25. {
  26. // this means it points to someplace inside the pidl
  27. // maybe this has hidden ids
  28. PCIDHIDDEN pidhid = (PCIDHIDDEN) (((BYTE *)pidl) + cbHidden);
  29. if (_ILIsHidden(pidhid)
  30. && (pidhid->cb + cbHidden <= pidl->mkid.cb))
  31. {
  32. // this is more than likely a hidden id
  33. // we could walk the chain and verify
  34. // that it adds up right...
  35. return pidhid;
  36. }
  37. }
  38. return NULL;
  39. }
  40. //
  41. // HIDDEN ids are sneakily hidden in the last ID in a pidl.
  42. // we append our data without changing the existing pidl,
  43. // (except it is now bigger) this works because the pidls
  44. // that we will apply this to are flexible in handling different
  45. // sized pidls. specifically this is used in FS pidls.
  46. //
  47. // WARNING - it is the callers responsibility to use hidden IDs
  48. // only on pidls that can handle it. most shell pidls, and
  49. // specifically FS pidls have no problem with this. however
  50. // some shell extensions might have fixed length ids,
  51. // which makes these unadvisable to append to everything.
  52. // possibly add an SFGAO_ bit to allow hidden, otherwise key
  53. // off FILESYSTEM bit.
  54. //
  55. STDAPI ILCloneWithHiddenID(LPCITEMIDLIST pidl, PCIDHIDDEN pidhid, LPITEMIDLIST *ppidl)
  56. {
  57. HRESULT hr;
  58. // If this ASSERT fires, then the caller did not set the pidhid->id
  59. // value properly. For example, the packing settings might be incorrect.
  60. ASSERT(_ILIsHidden(pidhid));
  61. if (ILIsEmpty(pidl))
  62. {
  63. *ppidl = NULL;
  64. hr = E_INVALIDARG;
  65. }
  66. else
  67. {
  68. UINT cbUsed = ILGetSize(pidl);
  69. UINT cbRequired = cbUsed + pidhid->cb + sizeof(pidhid->cb);
  70. *ppidl = (LPITEMIDLIST)SHAlloc(cbRequired);
  71. if (*ppidl)
  72. {
  73. hr = S_OK;
  74. CopyMemory(*ppidl, pidl, cbUsed);
  75. LPITEMIDLIST pidlLast = ILFindLastID(*ppidl);
  76. WORD cbHidden = _ILFirstHidden(pidlLast) ? _ILHiddenOffset(pidlLast) : pidlLast->mkid.cb;
  77. PIDHIDDEN pidhidCopy = (PIDHIDDEN)_ILSkip(*ppidl, cbUsed - sizeof((*ppidl)->mkid.cb));
  78. // Append it, overwriting the terminator
  79. MoveMemory(pidhidCopy, pidhid, pidhid->cb);
  80. // grow the copy to allow the hidden offset.
  81. pidhidCopy->cb += sizeof(pidhid->cb);
  82. // now we need to readjust pidlLast to encompass
  83. // the hidden bits and the hidden offset.
  84. pidlLast->mkid.cb += pidhidCopy->cb;
  85. // set the hidden offset so that we can find our hidden IDs later
  86. _ILSetHiddenOffset((LPITEMIDLIST)pidhidCopy, cbHidden);
  87. // We must put zero-terminator because of LMEM_ZEROINIT.
  88. _ILSkip(*ppidl, cbRequired - sizeof((*ppidl)->mkid.cb))->mkid.cb = 0;
  89. ASSERT(ILGetSize(*ppidl) == cbRequired);
  90. }
  91. else
  92. {
  93. hr = E_OUTOFMEMORY;
  94. }
  95. }
  96. return hr;
  97. }
  98. // lame API that consumes pidl as input (caller must not touch after callign this)
  99. STDAPI_(LPITEMIDLIST) ILAppendHiddenID(LPITEMIDLIST pidl, PCIDHIDDEN pidhid)
  100. {
  101. //
  102. // FEATURE - we dont handle collisions of multiple hidden ids
  103. // maybe remove IDs of the same IDLHID?
  104. //
  105. // Note: We do not remove IDLHID_EMPTY hidden ids.
  106. // Callers need to call ILExpungeRemovedHiddenIDs explicitly
  107. // if they want empty hidden ids to be compressed out.
  108. //
  109. RIP(pidl); // we require a pidl to attach the hidden id to
  110. if (!ILIsEmpty(pidl))
  111. {
  112. LPITEMIDLIST pidlSave = pidl;
  113. ILCloneWithHiddenID(pidl, pidhid, &pidl);
  114. ILFree(pidlSave);
  115. }
  116. return pidl;
  117. }
  118. STDAPI_(PCIDHIDDEN) ILFindHiddenIDOn(LPCITEMIDLIST pidl, IDLHID id, BOOL fOnLast)
  119. {
  120. RIP(pidl);
  121. if (!ILIsEmpty(pidl))
  122. {
  123. if (fOnLast)
  124. pidl = ILFindLastID(pidl);
  125. PCIDHIDDEN pidhid = _ILFirstHidden(pidl);
  126. // reuse pidl to become the limit.
  127. // so that we cant ever walk out of
  128. // the pidl.
  129. pidl = _ILNext(pidl);
  130. while (pidhid)
  131. {
  132. if (pidhid->id == id)
  133. break;
  134. pidhid = _ILNextHidden(pidhid, pidl);
  135. }
  136. return pidhid;
  137. }
  138. return NULL;
  139. }
  140. STDAPI_(LPITEMIDLIST) ILCreateWithHidden(UINT cbNonHidden, UINT cbHidden)
  141. {
  142. // alloc enough for the two ids plus term and hidden tail
  143. LPITEMIDLIST pidl;
  144. UINT cb = cbNonHidden + cbHidden + sizeof(pidl->mkid.cb);
  145. UINT cbAlloc = cb + sizeof(pidl->mkid.cb);
  146. pidl = (LPITEMIDLIST)SHAlloc(cbAlloc);
  147. if (pidl)
  148. {
  149. // zero-init for external task allocator
  150. memset(pidl, 0, cbAlloc);
  151. PIDHIDDEN pidhid = (PIDHIDDEN)_ILSkip(pidl, cbNonHidden);
  152. // grow the copy to allow the hidden offset.
  153. pidhid->cb = (USHORT) cbHidden + sizeof(pidhid->cb);
  154. // now we need to readjust pidlLast to encompass
  155. // the hidden bits and the hidden offset.
  156. pidl->mkid.cb = (USHORT) cb;
  157. // set the hidden offset so that we can find our hidden IDs later
  158. _ILSetHiddenOffset(pidl, cbNonHidden);
  159. ASSERT(ILGetSize(pidl) == cbAlloc);
  160. ASSERT(_ILNext(pidl) == _ILNext((LPCITEMIDLIST)pidhid));
  161. }
  162. return pidl;
  163. }
  164. // Note: The space occupied by the removed ID is not reclaimed.
  165. // Call ILExpungeRemovedHiddenIDs explicitly to reclaim the space.
  166. STDAPI_(BOOL) ILRemoveHiddenID(LPITEMIDLIST pidl, IDLHID id)
  167. {
  168. PIDHIDDEN pidhid = (PIDHIDDEN)ILFindHiddenID(pidl, id);
  169. if (pidhid)
  170. {
  171. pidhid->id = IDLHID_EMPTY;
  172. return TRUE;
  173. }
  174. return FALSE;
  175. }
  176. STDAPI_(void) ILExpungeRemovedHiddenIDs(LPITEMIDLIST pidl)
  177. {
  178. if (pidl)
  179. {
  180. pidl = ILFindLastID(pidl);
  181. // Note: Each IDHIDDEN has a WORD appended to it, equal to
  182. // _ILHiddenOffset, so we can just keep deleting IDHIDDENs
  183. // and if we delete them all, everything is cleaned up; if
  184. // there are still unremoved IDHIDDENs left, they will provide
  185. // the _ILHiddenOffset.
  186. PIDHIDDEN pidhid;
  187. BOOL fAnyDeleted = FALSE;
  188. while ((pidhid = (PIDHIDDEN)ILFindHiddenID(pidl, IDLHID_EMPTY)) != NULL)
  189. {
  190. fAnyDeleted = TRUE;
  191. LPBYTE pbAfter = (LPBYTE)pidhid + pidhid->cb;
  192. WORD cbDeleted = pidhid->cb;
  193. MoveMemory(pidhid, pbAfter,
  194. (LPBYTE)pidl + pidl->mkid.cb + sizeof(WORD) - pbAfter);
  195. pidl->mkid.cb -= cbDeleted;
  196. }
  197. }
  198. }