PageRenderTime 32ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/animeplugin3/MyAnimePlugin3/ShellItem.cs

https://bitbucket.org/gibwar/jmm-test
C# | 2334 lines | 2317 code | 7 blank | 10 comment | 90 complexity | 9b807895a5af4736537348782ed636b8 MD5 | raw file
  1. /*
  2. * This class is converted VB.Net from the CScItem
  3. * from http://www.codeproject.com/KB/cpp/VbNetExpTree.aspx
  4. */
  5. using System.IO;
  6. using System.Runtime.InteropServices;
  7. using System.Text;
  8. using System;
  9. using System.Collections;
  10. using System.Windows.Forms;
  11. using System.Diagnostics;
  12. using System.Management;
  13. public class ShellItem : IDisposable, IComparable
  14. {
  15. #region " Shared Private Fields"
  16. //This class has occasion to refer to the TypeName as reported by
  17. // ShellDll.SHGetFileInfo. It needs to compare this to the string
  18. // (in English) "System Folder"
  19. //on non-English systems, we do not know, in the general case,
  20. // what the equivalent string is to compare against
  21. //The following variable is set by Sub New() to the string that
  22. // corresponds to "System Folder" on the current machine
  23. // Sub New() depends on the existance of My Computer(ShellDll.CSIDL.DRIVES),
  24. // to determine what the equivalent string is
  25. private static string m_strSystemFolder;
  26. //My Computer is also commonly used (though not internally),
  27. // so save & expose its name on the current machine
  28. private static string m_strMyComputer;
  29. //To get My Documents sorted first, we need to know the Locale
  30. //specific name of that folder.
  31. private static string m_strMyDocuments;
  32. // The DesktopBase is set up via Sub New() (one time only) and
  33. // disposed of only when DesktopBase is finally disposed of
  34. private static ShellItem DesktopBase;
  35. //We can avoid an extra ShellDll.SHGetFileInfo call once this is set up
  36. private static int OpenFolderIconIndex = -1;
  37. // It is also useful to know if the OS is XP or above.
  38. // Set up in Sub New() to avoid multiple calls to find this info
  39. private static bool XPorAbove;
  40. // Likewise if OS is Win2K or Above
  41. private static bool Win2KOrAbove;
  42. // DragDrop, possibly among others, needs to know the Path of
  43. // the DeskTopDirectory in addition to the Desktop itself
  44. // Also need the actual ShellItem for the DeskTopDirectory, so get it
  45. private static ShellItem m_DeskTopDirectory;
  46. #endregion
  47. #region " Instance Private Fields"
  48. //m_Folder and m_Pidl must be released/freed at Dispose time
  49. private ShellDll.IShellFolder m_Folder;
  50. //if item is a folder, contains the Folder interface for this instance
  51. private IntPtr m_Pidl;
  52. //The Absolute PIDL for this item (not retained for files)
  53. private string m_DisplayName = "";
  54. private string m_Path;
  55. private string m_TypeName;
  56. private ShellItem m_Parent;
  57. //= Nothing
  58. private int m_IconIndexNormal;
  59. //index into the System Image list for Normal icon
  60. private int m_IconIndexOpen;
  61. //index into the SystemImage list for Open icon
  62. private bool m_IsBrowsable;
  63. private bool m_IsFileSystem;
  64. private bool m_IsFolder;
  65. private bool m_HasSubFolders;
  66. private bool m_IsLink;
  67. private bool m_IsDisk;
  68. private bool m_IsShared;
  69. private bool m_IsHidden;
  70. private bool m_IsNetWorkDrive;
  71. //= False
  72. private bool m_IsRemovable;
  73. //= False
  74. private bool m_IsReadOnly;
  75. //= False
  76. //Properties of interest to Drag Operations
  77. private bool m_CanMove;
  78. //= False
  79. private bool m_CanCopy;
  80. //= False
  81. private bool m_CanDelete;
  82. //= False
  83. private bool m_CanLink;
  84. //= False
  85. private bool m_IsDropTarget;
  86. //= False
  87. private ShellDll.SFGAO m_Attributes;
  88. //the original, returned from GetAttributesOf
  89. private int m_SortFlag;
  90. //= 0 'Used in comparisons
  91. private ArrayList m_Directories;
  92. //The following elements are only filled in on demand
  93. private bool m_XtrInfo;
  94. //= False
  95. private DateTime m_LastWriteTime;
  96. private DateTime m_CreationTime;
  97. private DateTime m_LastAccessTime;
  98. private long m_Length;
  99. //Indicates whether DisplayName, TypeName, SortFlag have been set up
  100. private bool m_HasDispType;
  101. //= False
  102. //Indicates whether IsReadOnly has been set up
  103. private bool m_IsReadOnlySetup;
  104. //= False
  105. //Holds a byte() representation of m_PIDL -- filled when needed
  106. private cPidl m_cPidl;
  107. //Flags for Dispose state
  108. private bool m_Disposed;
  109. #endregion
  110. #region " Destructor"
  111. /// <summary>
  112. /// Summary of Dispose.
  113. /// </summary>
  114. ///
  115. public void Dispose()
  116. {
  117. Dispose(true);
  118. // Take yourself off of the finalization queue
  119. // to prevent finalization code for this object
  120. // from executing a second time.
  121. GC.SuppressFinalize(this);
  122. }
  123. /// <summary>
  124. /// Deallocates CoTaskMem contianing m_Pidl and removes reference to m_Folder
  125. /// </summary>
  126. /// <param name="disposing"></param>
  127. ///
  128. protected virtual void Dispose(bool disposing)
  129. {
  130. // Allow your Dispose method to be called multiple times,
  131. // but throw an exception if the object has been disposed.
  132. // Whenever you do something with this class,
  133. // check to see if it has been disposed.
  134. if (!(m_Disposed)) {
  135. // If disposing equals true, dispose all managed
  136. // and unmanaged resources.
  137. m_Disposed = true;
  138. if ((disposing)) {
  139. }
  140. // Release unmanaged resources. If disposing is false,
  141. // only the following code is executed.
  142. if ((m_Folder != null)) {
  143. Marshal.ReleaseComObject(m_Folder);
  144. }
  145. if (!m_Pidl.Equals(IntPtr.Zero)) {
  146. Marshal.FreeCoTaskMem(m_Pidl);
  147. }
  148. }
  149. else {
  150. throw new Exception("ShellItem Disposed more than once");
  151. }
  152. }
  153. // This Finalize method will run only if the
  154. // Dispose method does not get called.
  155. // By default, methods are NotOverridable.
  156. // This prevents a derived class from overriding this method.
  157. /// <summary>
  158. /// Summary of Finalize.
  159. /// </summary>
  160. ///
  161. ~ShellItem()
  162. {
  163. // Do not re-create Dispose clean-up code here.
  164. // Calling Dispose(false) is optimal in terms of
  165. // readability and maintainability.
  166. Dispose(false);
  167. }
  168. #endregion
  169. #region " Constructors"
  170. #region " Private Sub New(ByVal folder As ShellDll.IShellFolder, ByVal pidl As IntPtr, ByVal parent As ShellItem)"
  171. /// <summary>
  172. /// Private Constructor, creates new ShellItem from the item's parent folder and
  173. /// the item's PIDL relative to that folder.</summary>
  174. /// <param name="folder">the folder interface of the parent</param>
  175. /// <param name="pidl">the Relative PIDL of this item</param>
  176. /// <param name="parent">the CShitem of the parent</param>
  177. ///
  178. private ShellItem(ShellDll.IShellFolder folder, IntPtr pidl, ShellItem parent)
  179. {
  180. if ((DesktopBase == null)) {
  181. //This initializes the Desktop folder
  182. DesktopBase = new ShellItem();
  183. }
  184. m_Parent = parent;
  185. m_Pidl = concatPidls(parent.PIDL, pidl);
  186. //Get some attributes
  187. SetUpAttributes(folder, pidl);
  188. //Set unfetched value for IconIndex....
  189. m_IconIndexNormal = -1;
  190. m_IconIndexOpen = -1;
  191. //finally, set up my Folder
  192. if (m_IsFolder) {
  193. int HR = 0;
  194. HR = folder.BindToObject(pidl, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref m_Folder);
  195. if (HR != ShellDll.NOERROR) {
  196. Marshal.ThrowExceptionForHR(HR);
  197. }
  198. }
  199. }
  200. #endregion
  201. #region " Sub New()"
  202. /// <summary>
  203. /// Private Constructor. Creates ShellItem of the Desktop
  204. /// </summary>
  205. ///
  206. private ShellItem()
  207. {
  208. //only used when desktopfolder has not been intialized
  209. if ((DesktopBase != null)) {
  210. throw new Exception("Attempt to initialize ShellItem for second time");
  211. }
  212. int HR = 0;
  213. //firstly determine what the local machine calls a "System Folder" and "My Computer"
  214. IntPtr tmpPidl = default(IntPtr);
  215. HR = ShellDll.SHGetSpecialFolderLocation(0, (int)ShellDll.CSIDL.DRIVES, ref tmpPidl);
  216. ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
  217. int dwflag = (int)(ShellDll.SHGFI.DISPLAYNAME | ShellDll.SHGFI.TYPENAME | ShellDll.SHGFI.PIDL);
  218. int dwAttr = 0;
  219. ShellDll.SHGetFileInfo(tmpPidl, dwAttr, ref shfi, (int)ShellDll.cbFileInfo, dwflag);
  220. m_strSystemFolder = shfi.szTypeName;
  221. m_strMyComputer = shfi.szDisplayName;
  222. Marshal.FreeCoTaskMem(tmpPidl);
  223. //set OS version info
  224. XPorAbove = ShellDll.IsXpOrAbove();
  225. Win2KOrAbove = ShellDll.Is2KOrAbove();
  226. //With That done, now set up Desktop ShellItem
  227. m_Path = "::{" + ShellDll.DesktopGUID.ToString() + "}";
  228. m_IsFolder = true;
  229. m_HasSubFolders = true;
  230. m_IsBrowsable = false;
  231. HR = ShellDll.SHGetDesktopFolder(ref m_Folder);
  232. m_Pidl = ShellDll.GetSpecialFolderLocation(IntPtr.Zero, (int)ShellDll.CSIDL.DESKTOP);
  233. dwflag = (int)(ShellDll.SHGFI.DISPLAYNAME | ShellDll.SHGFI.TYPENAME | ShellDll.SHGFI.SYSICONINDEX | ShellDll.SHGFI.PIDL);
  234. dwAttr = 0;
  235. IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, dwAttr, ref shfi, (int)ShellDll.cbFileInfo, dwflag);
  236. m_DisplayName = shfi.szDisplayName;
  237. m_TypeName = strSystemFolder;
  238. //not returned correctly by ShellDll.SHGetFileInfo
  239. m_IconIndexNormal = shfi.iIcon;
  240. m_IconIndexOpen = shfi.iIcon;
  241. m_HasDispType = true;
  242. m_IsDropTarget = true;
  243. m_IsReadOnly = false;
  244. m_IsReadOnlySetup = true;
  245. //also get local name for "My Documents"
  246. int pchEaten = 0;
  247. tmpPidl = IntPtr.Zero;
  248. int tmp = 0;
  249. HR = m_Folder.ParseDisplayName(0, IntPtr.Zero, "::{450d8fba-ad25-11d0-98a8-0800361b1103}", ref pchEaten, ref tmpPidl, ref tmp);
  250. shfi = new ShellDll.SHFILEINFO();
  251. dwflag = (int)(ShellDll.SHGFI.DISPLAYNAME | ShellDll.SHGFI.TYPENAME | ShellDll.SHGFI.PIDL);
  252. dwAttr = 0;
  253. ShellDll.SHGetFileInfo(tmpPidl, dwAttr, ref shfi, ShellDll.cbFileInfo, dwflag);
  254. m_strMyDocuments = shfi.szDisplayName;
  255. Marshal.FreeCoTaskMem(tmpPidl);
  256. //this must be done after getting "My Documents" string
  257. m_SortFlag = ComputeSortFlag();
  258. //Set DesktopBase
  259. DesktopBase = this;
  260. // Lastly, get the Path and ShellItem of the DesktopDirectory -- useful for DragDrop
  261. m_DeskTopDirectory = new ShellItem(ShellDll.CSIDL.DESKTOPDIRECTORY);
  262. }
  263. #endregion
  264. #region " New(ByVal ID As ShellDll.CSIDL)"
  265. /// <summary>Create instance based on a non-desktop ShellDll.CSIDL.
  266. /// Will create based on any ShellDll.CSIDL Except the DeskTop ShellDll.CSIDL</summary>
  267. /// <param name="ID">Value from ShellDll.CSIDL enumeration denoting the folder to create this ShellItem of.</param>
  268. ///
  269. public ShellItem(ShellDll.CSIDL ID)
  270. {
  271. if ((DesktopBase == null)) {
  272. //This initializes the Desktop folder
  273. DesktopBase = new ShellItem();
  274. }
  275. int HR = 0;
  276. if (ID == ShellDll.CSIDL.MYDOCUMENTS) {
  277. int pchEaten = 0;
  278. int tmp = 0;
  279. HR = DesktopBase.m_Folder.ParseDisplayName(0, IntPtr.Zero, "::{450d8fba-ad25-11d0-98a8-0800361b1103}", ref pchEaten, ref m_Pidl, ref tmp);
  280. }
  281. else {
  282. HR = ShellDll.SHGetSpecialFolderLocation(0, (int)ID, ref m_Pidl);
  283. }
  284. if (HR == ShellDll.NOERROR) {
  285. ShellDll.IShellFolder pParent = default(ShellDll.IShellFolder);
  286. IntPtr relPidl = IntPtr.Zero;
  287. pParent = GetParentOf(m_Pidl, ref relPidl);
  288. //Get the Attributes
  289. SetUpAttributes(pParent, relPidl);
  290. //Set unfetched value for IconIndex....
  291. m_IconIndexNormal = -1;
  292. m_IconIndexOpen = -1;
  293. //finally, set up my Folder
  294. if (m_IsFolder) {
  295. HR = pParent.BindToObject(relPidl, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref m_Folder);
  296. if (HR != ShellDll.NOERROR) {
  297. Marshal.ThrowExceptionForHR(HR);
  298. }
  299. }
  300. Marshal.ReleaseComObject(pParent);
  301. //if PidlCount(m_Pidl) = 1 then relPidl is same as m_Pidl, don't release
  302. if (PidlCount(m_Pidl) > 1) Marshal.FreeCoTaskMem(relPidl);
  303. }
  304. else {
  305. Marshal.ThrowExceptionForHR(HR);
  306. }
  307. }
  308. #endregion
  309. #region " New(ByVal path As String)"
  310. /// <summary>Create a new ShellItem based on a Path Must be a valid FileSystem Path</summary>
  311. /// <param name="path"></param>
  312. ///
  313. public ShellItem(string path)
  314. {
  315. if ((DesktopBase == null)) {
  316. //This initializes the Desktop folder
  317. DesktopBase = new ShellItem();
  318. }
  319. //Removal of following code allows Path(GUID) of Special FOlders to serve
  320. // as a valid Path for ShellItem creation (part of Calum's refresh code needs this
  321. //If Not Directory.Exists(path) AndAlso Not File.Exists(path) Then
  322. // Throw New Exception("ShellItem -- Invalid Path specified")
  323. //End If
  324. int HR = 0;
  325. int tmp1 = 0, tmp2 = 0;
  326. HR = DesktopBase.m_Folder.ParseDisplayName(0, IntPtr.Zero, path, ref tmp1, ref m_Pidl, ref tmp2);
  327. if (!(HR == ShellDll.NOERROR)) Marshal.ThrowExceptionForHR(HR);
  328. ShellDll.IShellFolder pParent = default(ShellDll.IShellFolder);
  329. IntPtr relPidl = IntPtr.Zero;
  330. pParent = GetParentOf(m_Pidl, ref relPidl);
  331. //Get the Attributes
  332. SetUpAttributes(pParent, relPidl);
  333. //Set unfetched value for IconIndex....
  334. m_IconIndexNormal = -1;
  335. m_IconIndexOpen = -1;
  336. //finally, set up my Folder
  337. if (m_IsFolder) {
  338. HR = pParent.BindToObject(relPidl, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref m_Folder);
  339. if (HR != ShellDll.NOERROR) {
  340. Marshal.ThrowExceptionForHR(HR);
  341. }
  342. }
  343. Marshal.ReleaseComObject(pParent);
  344. //if PidlCount(m_Pidl) = 1 then relPidl is same as m_Pidl, don't release
  345. if (PidlCount(m_Pidl) > 1) {
  346. Marshal.FreeCoTaskMem(relPidl);
  347. }
  348. }
  349. #endregion
  350. #region " ShellItem(byte[] FoldBytes, byte[] ItemBytes)"
  351. ///<Summary>Given a Byte() containing the Pidl of the parent
  352. /// folder and another Byte() containing the Pidl of the Item,
  353. /// relative to the Folder, Create a ShellItem for the Item.
  354. /// This is of primary use in dealing with "Shell IDList Array"
  355. /// formatted info passed in a Drag Operation
  356. /// </Summary>
  357. public ShellItem(byte[] FoldBytes, byte[] ItemBytes)
  358. {
  359. if ((DesktopBase == null)) {
  360. //This initializes the Desktop folder
  361. DesktopBase = new ShellItem();
  362. }
  363. ShellDll.IShellFolder pParent = MakeFolderFromBytes(FoldBytes);
  364. IntPtr ipParent = cPidl.BytesToPidl(FoldBytes);
  365. IntPtr ipItem = cPidl.BytesToPidl(ItemBytes);
  366. if ((pParent == null)) {
  367. //m_PIDL will = IntPtr.Zero for really bad CShitem
  368. goto XIT;
  369. }
  370. if (ipParent.Equals(IntPtr.Zero) | ipItem.Equals(IntPtr.Zero)) {
  371. goto XIT;
  372. }
  373. // Now process just like sub new(folder,pidl,parent) version
  374. m_Pidl = concatPidls(ipParent, ipItem);
  375. //Get some attributes
  376. SetUpAttributes(pParent, ipItem);
  377. //Set unfetched value for IconIndex....
  378. m_IconIndexNormal = -1;
  379. m_IconIndexOpen = -1;
  380. //finally, set up my Folder
  381. if (m_IsFolder) {
  382. int HR = 0;
  383. HR = pParent.BindToObject(ipItem, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref m_Folder);
  384. #if Debug
  385. if (HR != NOERROR) {
  386. Marshal.ThrowExceptionForHR(HR);
  387. }
  388. #endif
  389. }
  390. XIT:
  391. //On any kind of exit, free the allocated memory
  392. #if Debug
  393. if (m_Pidl.Equals(IntPtr.Zero)) {
  394. Debug.WriteLine("CShItem.New(FoldBytes,ItemBytes) Failed");
  395. }
  396. else {
  397. Debug.WriteLine("CShItem.New(FoldBytes,ItemBytes) Created " + this.Path);
  398. }
  399. #endif
  400. if (!ipParent.Equals(IntPtr.Zero)) {
  401. Marshal.FreeCoTaskMem(ipParent);
  402. }
  403. if (!ipItem.Equals(IntPtr.Zero)) {
  404. Marshal.FreeCoTaskMem(ipItem);
  405. }
  406. }
  407. #endregion
  408. #region " Utility functions used in Constructors"
  409. #region " IsValidPidl"
  410. ///<Summary>It is impossible to validate a PIDL completely since its contents
  411. /// are arbitrarily defined by the creating Shell Namespace. However, it
  412. /// is possible to validate the structure of a PIDL.</Summary>
  413. public static bool IsValidPidl(byte[] b)
  414. {
  415. bool IsValidPidl = false;
  416. //assume failure
  417. int bMax = b.Length - 1;
  418. //max value that index can have
  419. if (bMax < 1) return IsValidPidl;
  420. //min Size is 2 bytes
  421. int cb = b[0] + (b[1] * 256);
  422. int indx = 0;
  423. while (cb > 0) {
  424. if ((indx + cb + 1) > bMax) return IsValidPidl;
  425. //an error
  426. indx += cb;
  427. cb = b[indx] + (b[indx + 1] * 256);
  428. }
  429. // on fall thru, it is ok as far as we can check
  430. return true;
  431. }
  432. #endregion
  433. #region " MakeFolderFromBytes"
  434. public static ShellDll.IShellFolder MakeFolderFromBytes(byte[] b)
  435. {
  436. ShellDll.IShellFolder functionReturnValue = default(ShellDll.IShellFolder);
  437. functionReturnValue = null;
  438. //get rid of VS2005 warning
  439. if (!IsValidPidl(b)) return null;
  440. if (b.Length == 2 && ((b[0] == 0) & (b[1] == 0))) {
  441. //this is the desktop
  442. return DesktopBase.Folder;
  443. }
  444. else if (b.Length == 0) {
  445. //Also indicates the desktop
  446. return DesktopBase.Folder;
  447. }
  448. else {
  449. IntPtr ptr = Marshal.AllocCoTaskMem(b.Length);
  450. if (ptr.Equals(IntPtr.Zero)) return null;
  451. Marshal.Copy(b, 0, ptr, b.Length);
  452. //the next statement assigns a IshellFolder object to the function return, or has an error
  453. int hr = DesktopBase.Folder.BindToObject(ptr, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref functionReturnValue);
  454. if (hr != 0) functionReturnValue = null;
  455. Marshal.FreeCoTaskMem(ptr);
  456. }
  457. return functionReturnValue;
  458. }
  459. #endregion
  460. #region " GetParentOf"
  461. ///<Summary>Returns both the ShellDll.IShellFolder interface of the parent folder
  462. /// and the relative pidl of the input PIDL</Summary>
  463. ///<remarks>Several internal functions need this information and do not have
  464. /// it readily available. GetParentOf serves those functions</remarks>
  465. private static ShellDll.IShellFolder GetParentOf(IntPtr pidl, ref IntPtr relPidl)
  466. {
  467. ShellDll.IShellFolder functionReturnValue = default(ShellDll.IShellFolder);
  468. functionReturnValue = null;
  469. //avoid VB2005 warning
  470. int HR = 0;
  471. int itemCnt = PidlCount(pidl);
  472. if (itemCnt == 1) {
  473. //parent is desktop
  474. HR = ShellDll.SHGetDesktopFolder(ref functionReturnValue);
  475. relPidl = pidl;
  476. }
  477. else {
  478. IntPtr tmpPidl = default(IntPtr);
  479. tmpPidl = TrimPidl(pidl, ref relPidl);
  480. HR = DesktopBase.m_Folder.BindToObject(tmpPidl, IntPtr.Zero, ref ShellDll.IID_IShellFolder, ref functionReturnValue);
  481. Marshal.FreeCoTaskMem(tmpPidl);
  482. }
  483. if (!(HR == ShellDll.NOERROR)) Marshal.ThrowExceptionForHR(HR);
  484. return functionReturnValue;
  485. }
  486. #endregion
  487. #region " SetUpAttributes"
  488. /// <summary>Get the base attributes of the folder/file that this ShellItem represents</summary>
  489. /// <param name="folder">Parent Folder of this Item</param>
  490. /// <param name="pidl">Relative Pidl of this Item.</param>
  491. ///
  492. private void SetUpAttributes(ShellDll.IShellFolder folder, IntPtr pidl)
  493. {
  494. ShellDll.SFGAO attrFlag = default(ShellDll.SFGAO);
  495. attrFlag = ShellDll.SFGAO.BROWSABLE;
  496. attrFlag = attrFlag | ShellDll.SFGAO.FILESYSTEM;
  497. attrFlag = attrFlag | ShellDll.SFGAO.HASSUBFOLDER;
  498. attrFlag = attrFlag | ShellDll.SFGAO.FOLDER;
  499. attrFlag = attrFlag | ShellDll.SFGAO.LINK;
  500. attrFlag = attrFlag | ShellDll.SFGAO.SHARE;
  501. attrFlag = attrFlag | ShellDll.SFGAO.HIDDEN;
  502. attrFlag = attrFlag | ShellDll.SFGAO.REMOVABLE;
  503. //attrFlag = attrFlag Or ShellDll.SFGAO.RDONLY 'made into an on-demand attribute
  504. attrFlag = attrFlag | ShellDll.SFGAO.CANCOPY;
  505. attrFlag = attrFlag | ShellDll.SFGAO.CANDELETE;
  506. attrFlag = attrFlag | ShellDll.SFGAO.CANLINK;
  507. attrFlag = attrFlag | ShellDll.SFGAO.CANMOVE;
  508. attrFlag = attrFlag | ShellDll.SFGAO.DROPTARGET;
  509. //Note: for GetAttributesOf, we must provide an array, in all cases with 1 element
  510. IntPtr[] aPidl = new IntPtr[1];
  511. aPidl[0] = pidl;
  512. folder.GetAttributesOf(1, aPidl, ref attrFlag);
  513. m_Attributes = attrFlag;
  514. m_IsBrowsable = ((attrFlag & ShellDll.SFGAO.BROWSABLE) != 0);
  515. m_IsFileSystem = ((attrFlag & ShellDll.SFGAO.FILESYSTEM) != 0);
  516. m_HasSubFolders = ((attrFlag & ShellDll.SFGAO.HASSUBFOLDER) != 0);
  517. m_IsFolder = ((attrFlag & ShellDll.SFGAO.FOLDER) != 0);
  518. m_IsLink = ((attrFlag & ShellDll.SFGAO.LINK) != 0);
  519. m_IsShared = ((attrFlag & ShellDll.SFGAO.SHARE) != 0);
  520. m_IsHidden = ((attrFlag & ShellDll.SFGAO.HIDDEN) != 0);
  521. m_IsRemovable = ((attrFlag & ShellDll.SFGAO.REMOVABLE) != 0);
  522. //m_IsReadOnly = CBool(attrFlag And ShellDll.SFGAO.RDONLY) 'made into an on-demand attribute
  523. m_CanCopy = ((attrFlag & ShellDll.SFGAO.CANCOPY) != 0);
  524. m_CanDelete = ((attrFlag & ShellDll.SFGAO.CANDELETE) != 0);
  525. m_CanLink = ((attrFlag & ShellDll.SFGAO.CANLINK) != 0);
  526. m_CanMove = ((attrFlag & ShellDll.SFGAO.CANMOVE) != 0);
  527. m_IsDropTarget = ((attrFlag & ShellDll.SFGAO.DROPTARGET) != 0);
  528. //Get the Path
  529. IntPtr strr = Marshal.AllocCoTaskMem(ShellDll.MAX_PATH * 2 + 4);
  530. Marshal.WriteInt32(strr, 0, 0);
  531. StringBuilder buf = new StringBuilder(ShellDll.MAX_PATH);
  532. ShellDll.SHGDN itemflags = ShellDll.SHGDN.FORPARSING;
  533. folder.GetDisplayNameOf(pidl, itemflags, strr);
  534. int HR = ShellDll.StrRetToBuf(strr, pidl, buf, ShellDll.MAX_PATH);
  535. Marshal.FreeCoTaskMem(strr);
  536. //now done with it
  537. if (HR == ShellDll.NOERROR) {
  538. m_Path = buf.ToString();
  539. //check for zip file = folder on xp, leave it a file
  540. if (m_IsFolder && m_IsFileSystem && XPorAbove) {
  541. //Note:meaning of ShellDll.SFGAO.STREAM changed between win2k and winXP
  542. //Version 20 code
  543. //If File.Exists(m_Path) Then
  544. // m_IsFolder = False
  545. //End If
  546. //Version 21 code
  547. aPidl[0] = pidl;
  548. attrFlag = ShellDll.SFGAO.STREAM;
  549. folder.GetAttributesOf(1, aPidl, ref attrFlag);
  550. if ((attrFlag & ShellDll.SFGAO.STREAM) != 0) {
  551. m_IsFolder = false;
  552. }
  553. }
  554. if (m_Path.Length == 3 && m_Path.Substring(1).Equals(":\\")) {
  555. m_IsDisk = true;
  556. }
  557. }
  558. else {
  559. Marshal.ThrowExceptionForHR(HR);
  560. }
  561. }
  562. #endregion
  563. #endregion
  564. #region " Public Shared Function GetCShItem(ByVal path As String) As ShellItem"
  565. public static ShellItem GetCShItem(string path)
  566. {
  567. ShellItem functionReturnValue = default(ShellItem);
  568. functionReturnValue = null;
  569. //assume failure
  570. int HR = 0;
  571. IntPtr tmpPidl = default(IntPtr);
  572. int tmp1 = 0, tmp2 = 0;
  573. HR = GetDeskTop().Folder.ParseDisplayName(0, IntPtr.Zero, path, ref tmp1, ref tmpPidl, ref tmp2);
  574. if (HR == 0) {
  575. functionReturnValue = FindCShItem(tmpPidl);
  576. if ((functionReturnValue == null))
  577. {
  578. try {
  579. functionReturnValue = new ShellItem(path);
  580. }
  581. catch {
  582. functionReturnValue = null;
  583. }
  584. }
  585. }
  586. if (!tmpPidl.Equals(IntPtr.Zero)) {
  587. Marshal.FreeCoTaskMem(tmpPidl);
  588. }
  589. return functionReturnValue;
  590. }
  591. #endregion
  592. #region " Public Shared Function GetCShItem(ByVal ID As ShellDll.CSIDL) As ShellItem"
  593. public static ShellItem GetCShItem(ShellDll.CSIDL ID)
  594. {
  595. ShellItem functionReturnValue = default(ShellItem);
  596. functionReturnValue = null;
  597. //avoid VB2005 Warning
  598. if (ID == ShellDll.CSIDL.DESKTOP) {
  599. return GetDeskTop();
  600. }
  601. int HR = 0;
  602. IntPtr tmpPidl = default(IntPtr);
  603. if (ID == ShellDll.CSIDL.MYDOCUMENTS) {
  604. int pchEaten = 0;
  605. int tmp = 0;
  606. HR = GetDeskTop().Folder.ParseDisplayName(0, IntPtr.Zero, "::{450d8fba-ad25-11d0-98a8-0800361b1103}", ref pchEaten, ref tmpPidl, ref tmp);
  607. }
  608. else {
  609. HR = ShellDll.SHGetSpecialFolderLocation(0, (int)ID, ref tmpPidl);
  610. }
  611. if (HR == ShellDll.NOERROR) {
  612. functionReturnValue = FindCShItem(tmpPidl);
  613. if ((functionReturnValue == null))
  614. {
  615. try {
  616. functionReturnValue = new ShellItem(ID);
  617. }
  618. catch {
  619. functionReturnValue = null;
  620. }
  621. }
  622. }
  623. if (!tmpPidl.Equals(IntPtr.Zero)) {
  624. Marshal.FreeCoTaskMem(tmpPidl);
  625. }
  626. return functionReturnValue;
  627. }
  628. #endregion
  629. #region " Public Shared Function GetCShItem(ByVal FoldBytes() As Byte, ByVal ItemBytes() As Byte) As ShellItem"
  630. public static ShellItem GetCShItem(byte[] FoldBytes, byte[] ItemBytes)
  631. {
  632. ShellItem functionReturnValue = default(ShellItem);
  633. functionReturnValue = null;
  634. //assume failure
  635. byte[] b = cPidl.JoinPidlBytes(FoldBytes, ItemBytes);
  636. if ((b == null)) return functionReturnValue;
  637. //can do no more with invalid pidls
  638. //otherwise do like below, skipping unnecessary validation check
  639. IntPtr thisPidl = Marshal.AllocCoTaskMem(b.Length);
  640. if (thisPidl.Equals(IntPtr.Zero)) return null;
  641. Marshal.Copy(b, 0, thisPidl, b.Length);
  642. functionReturnValue = FindCShItem(thisPidl);
  643. Marshal.FreeCoTaskMem(thisPidl);
  644. if ((functionReturnValue == null))
  645. {
  646. //didn't find it, make new
  647. try {
  648. functionReturnValue = new ShellItem(FoldBytes, ItemBytes);
  649. }
  650. catch {
  651. }
  652. }
  653. if (functionReturnValue.PIDL.Equals(IntPtr.Zero)) functionReturnValue = null;
  654. return functionReturnValue;
  655. }
  656. #endregion
  657. #region " Public Shared Function FindCShItem(ByVal b() As Byte) As ShellItem"
  658. public static ShellItem FindCShItem(byte[] b)
  659. {
  660. ShellItem functionReturnValue = default(ShellItem);
  661. if (!IsValidPidl(b)) return null;
  662. IntPtr thisPidl = Marshal.AllocCoTaskMem(b.Length);
  663. if (thisPidl.Equals(IntPtr.Zero)) return null;
  664. Marshal.Copy(b, 0, thisPidl, b.Length);
  665. functionReturnValue = FindCShItem(thisPidl);
  666. Marshal.FreeCoTaskMem(thisPidl);
  667. return functionReturnValue;
  668. }
  669. #endregion
  670. #region " Public Shared Function FindCShItem(ByVal ptr As IntPtr) As ShellItem"
  671. public static ShellItem FindCShItem(IntPtr ptr)
  672. {
  673. ShellItem functionReturnValue = default(ShellItem);
  674. functionReturnValue = null;
  675. //avoid VB2005 Warning
  676. ShellItem BaseItem = ShellItem.GetDeskTop();
  677. //ShellItem CSI = default(ShellItem);
  678. bool FoundIt = false;
  679. //True if we found item or an ancestor
  680. while (!(FoundIt)) {
  681. foreach (var obj in BaseItem.GetDirectories(true)) {
  682. ShellItem CSI = obj as ShellItem;
  683. if (IsAncestorOf(CSI.PIDL, ptr, false)) {
  684. if (ShellItem.IsEqual(CSI.PIDL, ptr)) {
  685. //we found the desired item
  686. return CSI;
  687. }
  688. else {
  689. BaseItem = CSI;
  690. FoundIt = true;
  691. break; // TODO: might not be correct. Was : Exit For
  692. }
  693. }
  694. }
  695. if (!FoundIt) return null;
  696. //didn't find an ancestor
  697. //The complication is that the desired item may not be a directory
  698. if (!IsAncestorOf(BaseItem.PIDL, ptr, true)) {
  699. //Don't have immediate ancestor
  700. //go around again
  701. FoundIt = false;
  702. }
  703. else {
  704. foreach (var obj in BaseItem.GetItems()) {
  705. ShellItem CSI = obj as ShellItem;
  706. if (ShellItem.IsEqual(CSI.PIDL, ptr))
  707. {
  708. return CSI;
  709. }
  710. }
  711. //fall thru here means it doesn't exist or we can't find it because of funny PIDL from SHParseDisplayName
  712. return null;
  713. }
  714. }
  715. return functionReturnValue;
  716. }
  717. #endregion
  718. #endregion
  719. #region " Icomparable -- for default Sorting"
  720. /// <summary>Computes the Sort key of this ShellItem, based on its attributes</summary>
  721. ///
  722. private int ComputeSortFlag()
  723. {
  724. int rVal = 0;
  725. if (m_IsDisk) rVal = 0x100000;
  726. if (m_TypeName.Equals(strSystemFolder)) {
  727. if (!m_IsBrowsable) {
  728. rVal = rVal | 0x10000;
  729. if (m_strMyDocuments.Equals(m_DisplayName)) {
  730. rVal = rVal | 0x1;
  731. }
  732. }
  733. else {
  734. rVal = rVal | 0x1000;
  735. }
  736. }
  737. if (m_IsFolder) rVal = rVal | 0x100;
  738. return rVal;
  739. }
  740. ///<Summary> CompareTo(obj as object)
  741. /// Compares obj to this instance based on SortFlag-- obj must be a ShellItem</Summary>
  742. ///<SortOrder> (low)Disks,non-browsable System Folders,
  743. /// browsable System Folders,
  744. /// Directories, Files, Nothing (high)</SortOrder>
  745. public virtual int CompareTo(object obj)
  746. {
  747. ShellItem Other = obj as ShellItem;
  748. if ((Other == null)) return 1;
  749. //non-existant is always low
  750. if (!m_HasDispType) SetDispType();
  751. int cmp = Other.SortFlag - m_SortFlag;
  752. //Note the reversal
  753. if (cmp != 0) {
  754. return cmp;
  755. }
  756. else {
  757. if (m_IsDisk) {
  758. //implies that both are
  759. return string.Compare(m_Path, Other.Path);
  760. }
  761. else {
  762. return string.Compare(m_DisplayName, Other.DisplayName);
  763. }
  764. }
  765. }
  766. #endregion
  767. #region " Properties"
  768. #region " Shared Properties"
  769. public static string strMyComputer {
  770. get { return m_strMyComputer; }
  771. }
  772. public static string strSystemFolder {
  773. get { return m_strSystemFolder; }
  774. }
  775. public static string DesktopDirectoryPath {
  776. get { return m_DeskTopDirectory.Path; }
  777. }
  778. #endregion
  779. #region " Normal Properties"
  780. public IntPtr PIDL {
  781. get { return m_Pidl; }
  782. }
  783. public ShellDll.IShellFolder Folder {
  784. get { return m_Folder; }
  785. }
  786. public string Path {
  787. get { return m_Path; }
  788. }
  789. public ShellItem Parent {
  790. get { return m_Parent; }
  791. }
  792. public ShellDll.SFGAO Attributes {
  793. get { return m_Attributes; }
  794. }
  795. public bool IsBrowsable {
  796. get { return m_IsBrowsable; }
  797. }
  798. public bool IsFileSystem {
  799. get { return m_IsFileSystem; }
  800. }
  801. public bool IsFolder {
  802. get { return m_IsFolder; }
  803. }
  804. public bool HasSubFolders {
  805. get { return m_HasSubFolders; }
  806. }
  807. public bool IsDisk {
  808. get { return m_IsDisk; }
  809. }
  810. public bool IsLink {
  811. get { return m_IsLink; }
  812. }
  813. public bool IsShared {
  814. get { return m_IsShared; }
  815. }
  816. public bool IsHidden {
  817. get { return m_IsHidden; }
  818. }
  819. public bool IsRemovable {
  820. get { return m_IsRemovable; }
  821. }
  822. #region " Drag Ops Properties"
  823. public bool CanMove {
  824. get { return m_CanMove; }
  825. }
  826. public bool CanCopy {
  827. get { return m_CanCopy; }
  828. }
  829. public bool CanDelete {
  830. get { return m_CanDelete; }
  831. }
  832. public bool CanLink {
  833. get { return m_CanLink; }
  834. }
  835. public bool IsDropTarget {
  836. get { return m_IsDropTarget; }
  837. }
  838. #endregion
  839. #endregion
  840. #region " Filled on Demand Properties"
  841. #region " Filled based on m_HasDispType"
  842. /// <summary>
  843. /// Set DisplayName, TypeName, and SortFlag when actually needed
  844. /// </summary>
  845. ///
  846. private void SetDispType()
  847. {
  848. //Get Displayname, TypeName
  849. ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
  850. int dwflag = (int)(ShellDll.SHGFI.DISPLAYNAME | ShellDll.SHGFI.TYPENAME | ShellDll.SHGFI.PIDL);
  851. int dwAttr = 0;
  852. if (m_IsFileSystem & !m_IsFolder) {
  853. dwflag = dwflag | (int)ShellDll.SHGFI.USEFILEATTRIBUTES;
  854. dwAttr = (int)ShellDll.FILE_ATTRIBUTE_NORMAL;
  855. }
  856. IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, dwAttr, ref shfi, ShellDll.cbFileInfo, dwflag);
  857. m_DisplayName = shfi.szDisplayName;
  858. m_TypeName = shfi.szTypeName;
  859. //fix DisplayName
  860. if (m_DisplayName.Equals("")) {
  861. m_DisplayName = m_Path;
  862. }
  863. //Fix TypeName
  864. //If m_IsFolder And m_TypeName.Equals("File") Then
  865. // m_TypeName = "File Folder"
  866. //End If
  867. m_SortFlag = ComputeSortFlag();
  868. m_HasDispType = true;
  869. }
  870. public string DisplayName {
  871. get {
  872. if (!m_HasDispType) SetDispType();
  873. return m_DisplayName;
  874. }
  875. }
  876. private int SortFlag {
  877. get {
  878. if (!m_HasDispType) SetDispType();
  879. return m_SortFlag;
  880. }
  881. }
  882. public string TypeName {
  883. get {
  884. if (!m_HasDispType) SetDispType();
  885. return m_TypeName;
  886. }
  887. }
  888. #endregion
  889. #region " IconIndex properties"
  890. public int IconIndexNormal {
  891. get {
  892. if (m_IconIndexNormal < 0) {
  893. if (!m_HasDispType) SetDispType();
  894. ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
  895. int dwflag = (int)(ShellDll.SHGFI.PIDL | ShellDll.SHGFI.SYSICONINDEX);
  896. int dwAttr = 0;
  897. if (m_IsFileSystem & !m_IsFolder) {
  898. dwflag = dwflag | (int)ShellDll.SHGFI.USEFILEATTRIBUTES;
  899. dwAttr = (int)ShellDll.FILE_ATTRIBUTE_NORMAL;
  900. }
  901. IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, dwAttr, ref shfi, ShellDll.cbFileInfo, dwflag);
  902. m_IconIndexNormal = shfi.iIcon;
  903. }
  904. return m_IconIndexNormal;
  905. }
  906. }
  907. // IconIndexOpen is Filled on demand
  908. public int IconIndexOpen {
  909. get {
  910. if (m_IconIndexOpen < 0) {
  911. if (!m_HasDispType) SetDispType();
  912. if (!m_IsDisk & m_IsFileSystem & m_IsFolder) {
  913. if (OpenFolderIconIndex < 0) {
  914. int dwflag = (int)(ShellDll.SHGFI.SYSICONINDEX | ShellDll.SHGFI.PIDL);
  915. ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
  916. IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, 0, ref shfi, ShellDll.cbFileInfo, dwflag | (int)ShellDll.SHGFI.OPENICON);
  917. m_IconIndexOpen = shfi.iIcon;
  918. }
  919. else {
  920. //If m_TypeName.Equals("File Folder") Then
  921. // OpenFolderIconIndex = shfi.iIcon
  922. //End If
  923. m_IconIndexOpen = OpenFolderIconIndex;
  924. }
  925. }
  926. else {
  927. m_IconIndexOpen = m_IconIndexNormal;
  928. }
  929. }
  930. return m_IconIndexOpen;
  931. }
  932. }
  933. #endregion
  934. #region " FileInfo type Information"
  935. /// <summary>
  936. /// Obtains information available from FileInfo.
  937. /// </summary>
  938. ///
  939. private void FillDemandInfo()
  940. {
  941. if (m_IsDisk) {
  942. try {
  943. //See if this is a network drive
  944. //NoRoot = 1
  945. //Removable = 2
  946. //LocalDisk = 3
  947. //Network = 4
  948. //CD = 5
  949. //RAMDrive = 6
  950. System.Management.ManagementObject disk = new System.Management.ManagementObject("win32_logicaldisk.deviceid=\"" + m_Path.Substring(0, 2) + "\"");
  951. m_Length = (long)(UInt64)disk["Size"];
  952. if ((UInt32)disk["DriveType"] == 4) {
  953. m_IsNetWorkDrive = true;
  954. }
  955. }
  956. catch (Exception) {
  957. //Disconnected Network Drives etc. will generate
  958. //an error here, just assume that it is a network
  959. //drive
  960. m_IsNetWorkDrive = true;
  961. }
  962. finally {
  963. m_XtrInfo = true;
  964. }
  965. }
  966. else if (!m_IsDisk & m_IsFileSystem & !m_IsFolder) {
  967. //in this case, it's a file
  968. if (File.Exists(m_Path)) {
  969. FileInfo fi = new FileInfo(m_Path);
  970. m_LastWriteTime = fi.LastWriteTime;
  971. m_LastAccessTime = fi.LastAccessTime;
  972. m_CreationTime = fi.CreationTime;
  973. m_Length = fi.Length;
  974. m_XtrInfo = true;
  975. }
  976. }
  977. else {
  978. if (m_IsFileSystem & m_IsFolder) {
  979. if (Directory.Exists(m_Path)) {
  980. DirectoryInfo di = new DirectoryInfo(m_Path);
  981. m_LastWriteTime = di.LastWriteTime;
  982. m_LastAccessTime = di.LastAccessTime;
  983. m_CreationTime = di.CreationTime;
  984. m_XtrInfo = true;
  985. }
  986. }
  987. }
  988. }
  989. public DateTime LastWriteTime {
  990. get {
  991. if (!m_XtrInfo) {
  992. FillDemandInfo();
  993. }
  994. return m_LastWriteTime;
  995. }
  996. }
  997. public DateTime LastAccessTime {
  998. get {
  999. if (!m_XtrInfo) {
  1000. FillDemandInfo();
  1001. }
  1002. return m_LastAccessTime;
  1003. }
  1004. }
  1005. public DateTime CreationTime {
  1006. get {
  1007. if (!m_XtrInfo) {
  1008. FillDemandInfo();
  1009. }
  1010. return m_CreationTime;
  1011. }
  1012. }
  1013. public long Length {
  1014. get {
  1015. if (!m_XtrInfo) {
  1016. FillDemandInfo();
  1017. }
  1018. return m_Length;
  1019. }
  1020. }
  1021. public bool IsNetworkDrive {
  1022. get {
  1023. if (!m_XtrInfo) {
  1024. FillDemandInfo();
  1025. }
  1026. return m_IsNetWorkDrive;
  1027. }
  1028. }
  1029. #endregion
  1030. #region " cPidl information"
  1031. public cPidl clsPidl {
  1032. get {
  1033. if ((m_cPidl == null)) {
  1034. m_cPidl = new cPidl(m_Pidl);
  1035. }
  1036. return m_cPidl;
  1037. }
  1038. }
  1039. #endregion
  1040. #region " IsReadOnly and IsSystem"
  1041. ///<Summary>The IsReadOnly attribute causes an annoying access to any floppy drives
  1042. /// on the system. To postpone this (or avoid, depending on user action),
  1043. /// the attribute is only queried when asked for</Summary>
  1044. public bool IsReadOnly {
  1045. get {
  1046. if (m_IsReadOnlySetup) {
  1047. return m_IsReadOnly;
  1048. }
  1049. else {
  1050. ShellDll.SHFILEINFO shfi = new ShellDll.SHFILEINFO();
  1051. shfi.dwAttributes = (int)ShellDll.SFGAO.RDONLY;
  1052. int dwflag = (int)(ShellDll.SHGFI.PIDL | ShellDll.SHGFI.ATTRIBUTES | ShellDll.SHGFI.ATTR_SPECIFIED);
  1053. int dwAttr = 0;
  1054. IntPtr H = ShellDll.SHGetFileInfo(m_Pidl, dwAttr, ref shfi, ShellDll.cbFileInfo, dwflag);
  1055. if (H.ToInt32() != ShellDll.NOERROR && H.ToInt32() != 1) {
  1056. Marshal.ThrowExceptionForHR(H.ToInt32());
  1057. }
  1058. m_IsReadOnly = ((shfi.dwAttributes & (int)ShellDll.SFGAO.RDONLY) != 0);
  1059. //If m_IsReadOnly Then Debug.WriteLine("IsReadOnly -- " & m_Path)
  1060. m_IsReadOnlySetup = true;
  1061. return m_IsReadOnly;
  1062. }
  1063. }
  1064. //If Not m_XtrInfo Then
  1065. // FillDemandInfo()
  1066. //End If
  1067. //Return m_Attributes And FileAttributes.ReadOnly = FileAttributes.ReadOnly
  1068. }
  1069. ///<Summary>The IsSystem attribute is seldom used, but required by DragDrop operations.
  1070. /// Since there is no way of getting ONLY the System attribute without getting
  1071. /// the RO attribute (which forces a reference to the floppy drive), we pay
  1072. /// the price of getting its own File/DirectoryInfo for this purpose alone.
  1073. ///</Summary>
  1074. public bool IsSystem {
  1075. get {
  1076. //true once we have gotten this attr
  1077. //the value of this attr once we have it
  1078. if (!static_IsSystem_HaveSysInfo) {
  1079. try {
  1080. static_IsSystem_m_IsSystem = (File.GetAttributes(m_Path) & FileAttributes.System) == FileAttributes.System;
  1081. static_IsSystem_HaveSysInfo = true;
  1082. }
  1083. catch (Exception) {
  1084. static_IsSystem_HaveSysInfo = true;
  1085. }
  1086. }
  1087. Debug.WriteLine("In IsSystem -- Path = " + m_Path + " IsSystem = " + static_IsSystem_m_IsSystem);
  1088. return static_IsSystem_m_IsSystem;
  1089. }
  1090. }
  1091. static bool static_IsSystem_m_IsSystem = false;
  1092. static bool static_IsSystem_HaveSysInfo = false;
  1093. #endregion
  1094. #endregion
  1095. #endregion
  1096. #region " Public Methods"
  1097. #region " Shared Public Methods"
  1098. #region " GetDeskTop"
  1099. /// <summary>
  1100. /// If not initialized, then build DesktopBase
  1101. /// once done, or if initialized already,
  1102. /// </summary>
  1103. ///<returns>The DesktopBase ShellItem representing the desktop</returns>
  1104. ///
  1105. public static ShellItem GetDeskTop()
  1106. {
  1107. if ((DesktopBase == null)) {
  1108. DesktopBase = new ShellItem();
  1109. }
  1110. return DesktopBase;
  1111. }
  1112. #endregion
  1113. #region " IsAncestorOf"
  1114. ///<Summary>IsAncestorOf returns True if ShellItem ancestor is an ancestor of ShellItem current
  1115. /// if OS is Win2K or above, uses the ILIsParent API, otherwise uses the
  1116. /// cPidl function StartsWith. This is necessary since ILIsParent in only available
  1117. /// in Win2K or above systems AND StartsWith fails on some folders on XP systems (most
  1118. /// obviously some Network Folder Shortcuts, but also Control Panel. Note, StartsWith
  1119. /// always works on systems prior to XP.
  1120. /// NOTE: if ancestor and current reference the same Item, both
  1121. /// methods return True</Summary>
  1122. public static bool IsAncestorOf(ShellItem ancestor, ShellItem current, [System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(false)] // ERROR: Optional parameters aren't supported in C#
  1123. bool fParent)
  1124. {
  1125. return IsAncestorOf(ancestor.PIDL, current.PIDL, fParent);
  1126. }
  1127. ///<Summary> Compares a candidate Ancestor PIDL with a Child PIDL and
  1128. /// returns True if Ancestor is an ancestor of the child.
  1129. /// if fParent is True, then only return True if Ancestor is the immediate
  1130. /// parent of the Child</Summary>
  1131. public static bool IsAncestorOf(IntPtr AncestorPidl, IntPtr ChildPidl, [System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(false)] // ERROR: Optional parameters aren't supported in C#
  1132. bool fParent)
  1133. {
  1134. bool functionReturnValue = false;
  1135. if (ShellDll.Is2KOrAbove()) {
  1136. return ShellDll.ILIsParent(AncestorPidl, ChildPidl, fParent);
  1137. }
  1138. else {
  1139. cPidl Child = new cPidl(ChildPidl);
  1140. cPidl Ancestor = new cPidl(AncestorPidl);
  1141. functionReturnValue = Child.StartsWith(Ancestor);
  1142. if (!functionReturnValue) return functionReturnValue;
  1143. if (fParent) {
  1144. // check for immediate ancestor, if desired
  1145. object[] oAncBytes = Ancestor.Decompose();
  1146. object[] oChildBytes = Child.Decompose();
  1147. if (oAncBytes.Length != (oChildBytes.Length - 1)) {
  1148. functionReturnValue = false;
  1149. }
  1150. }
  1151. }
  1152. return functionReturnValue;
  1153. }
  1154. #endregion
  1155. #region " AllFolderWalk"
  1156. ///<Summary>The WalkAllCallBack delegate defines the signature of
  1157. /// the routine to be passed to DirWalker
  1158. /// Usage: dim d1 as new CshItem.WalkAllCallBack(addressof yourroutine)
  1159. /// Callback function receives a ShellItem for each file and Directory in
  1160. /// Starting Directory and each sub-directory of this directory and
  1161. /// each sub-dir of each sub-dir ....
  1162. ///</Summary>
  1163. public delegate bool WalkAllCallBack(ShellItem info, int UserLevel, int Tag);
  1164. ///<Summary>
  1165. /// AllFolderWalk recursively walks down directories from cStart, calling its
  1166. /// callback routine, WalkAllCallBack, for each Directory and File encountered, including those in
  1167. /// cStart. UserLevel is incremented by 1 for each level of dirs that DirWalker
  1168. /// recurses thru. Tag in an Integer that is simply passed, unmodified to the
  1169. /// callback, with each ShellItem encountered, both File and Directory CShItems.
  1170. /// </Summary>
  1171. /// <param name="cStart"></param>
  1172. /// <param name="cback"></param>
  1173. /// <param name="UserLevel"></param>
  1174. /// <param name="Tag"></param>
  1175. ///
  1176. public static bool AllFolderWalk(ShellItem cStart, WalkAllCallBack cback, int UserLevel, int Tag)
  1177. {
  1178. if ((cStart != null) && cStart.IsFolder) {
  1179. //ShellItem cItem = default(ShellItem);
  1180. //first processes all files in this directory
  1181. foreach (var obj in cStart.GetFiles()) {
  1182. ShellItem cItem = obj as ShellItem;
  1183. if (!cback(cItem, UserLevel, Tag)) {
  1184. //user said stop
  1185. return false;
  1186. }
  1187. }
  1188. //then process all dirs in this directory, recursively
  1189. foreach (var obj in cStart.GetDirectories(true)) {
  1190. ShellItem cItem = obj as ShellItem;
  1191. if (!cback(cItem, UserLevel + 1, Tag)) {
  1192. //user said stop
  1193. return false;
  1194. }
  1195. else {
  1196. if (!AllFolderWalk(cItem, cback, UserLevel + 1, Tag)) {
  1197. return false;
  1198. }
  1199. }
  1200. }
  1201. return true;
  1202. }
  1203. else {
  1204. //Invalid call
  1205. throw new ApplicationException("AllFolderWalk -- Invalid Start Directory");
  1206. }
  1207. }
  1208. #endregion
  1209. #endregion
  1210. #region " Public Instance Methods"
  1211. #region " Equals"
  1212. public bool Equals(ShellItem other)
  1213. {
  1214. return this.Path.Equals(other.Path);
  1215. }
  1216. #endregion
  1217. #region " GetDirectories"
  1218. /// <summary>
  1219. /// Returns the Directories of this sub-folder as an ArrayList of CShitems
  1220. /// </summary>
  1221. /// <param name="doRefresh">Optional, default=True, Refresh the directories</param>
  1222. /// <returns>An ArrayList of CShItems. May return an empty ArrayList if there are none.</returns>
  1223. /// <remarks>revised to alway return an up-to-date list unless
  1224. /// specifically instructed not to (useful in constructs like:
  1225. /// if CSI.RefreshDirectories then
  1226. /// Dirs = CSI.GetDirectories(False) ' just did a Refresh </remarks>
  1227. public ArrayList GetDirectories([System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(true)] // ERROR: Optional parameters aren't supported in C#
  1228. bool doRefresh)
  1229. {
  1230. if (m_IsFolder) {
  1231. if (doRefresh) {
  1232. // return an up-to-date List
  1233. RefreshDirectories();
  1234. }
  1235. else if (m_Directories == null) {
  1236. RefreshDirectories();
  1237. }
  1238. return m_Directories;
  1239. }
  1240. else {
  1241. //if it is not a Folder, then return empty arraylist
  1242. return new ArrayList();
  1243. }
  1244. }
  1245. #endregion
  1246. #region " GetFiles"
  1247. /// <summary>
  1248. /// Returns the Files of this sub-folder as an
  1249. /// ArrayList of CShitems
  1250. /// Note: we do not keep the arraylist of files, Generate it each time
  1251. /// </summary>
  1252. /// <returns>An ArrayList of CShItems. May return an empty ArrayList if there are none.</returns>
  1253. ///
  1254. public ArrayList GetFiles()
  1255. {
  1256. if (m_IsFolder) {
  1257. return GetContents(ShellDll.SHCONTF.NONFOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, false);
  1258. }
  1259. else {
  1260. return new ArrayList();
  1261. }
  1262. }
  1263. /// <summary>
  1264. /// Returns the Files of this sub-folder, filtered by a filtering string, as an
  1265. /// ArrayList of CShitems
  1266. /// Note: we do not keep the arraylist of files, Generate it each time
  1267. /// </summary>
  1268. /// <param name="Filter">A filter string (for example: *.Doc)</param>
  1269. /// <returns>An ArrayList of CShItems. May return an empty ArrayList if there are none.</returns>
  1270. ///
  1271. public ArrayList GetFiles(string Filter)
  1272. {
  1273. if (m_IsFolder) {
  1274. ArrayList dummy = new ArrayList();
  1275. string[] fileentries = null;
  1276. fileentries = Directory.GetFiles(m_Path, Filter);
  1277. //string vFile = null;
  1278. foreach (var obj in fileentries) {
  1279. string vFile = obj as string;
  1280. dummy.Add(new ShellItem(vFile));
  1281. }
  1282. return dummy;
  1283. }
  1284. else {
  1285. return new ArrayList();
  1286. }
  1287. }
  1288. #endregion
  1289. #region " GetItems"
  1290. /// <summary>
  1291. /// Returns the Directories and Files of this sub-folder as an
  1292. /// ArrayList of CShitems
  1293. /// Note: we do not keep the arraylist of files, Generate it each time
  1294. /// </summary>
  1295. /// <returns>An ArrayList of CShItems. May return an empty ArrayList if there are none.</returns>
  1296. public ArrayList GetItems()
  1297. {
  1298. ArrayList rVal = new ArrayList();
  1299. if (m_IsFolder) {
  1300. rVal.AddRange(this.GetDirectories(true));
  1301. rVal.AddRange(this.GetContents(ShellDll.SHCONTF.NONFOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, false));
  1302. rVal.Sort();
  1303. return rVal;
  1304. }
  1305. else {
  1306. return rVal;
  1307. }
  1308. }
  1309. #endregion
  1310. #region " GetFileName"
  1311. ///<Summary>GetFileName returns the Full file name of this item.
  1312. /// Specifically, for a link file (xxx.txt.lnk for example) the
  1313. /// DisplayName property will return xxx.txt, this method will
  1314. /// return xxx.txt.lnk. In most cases this is equivalent of
  1315. /// System.IO.Path.GetFileName(m_Path). However, some m_Paths
  1316. /// actually are GUIDs. In that case, this routine returns the
  1317. /// DisplayName</Summary>
  1318. public string GetFileName()
  1319. {
  1320. if (m_Path.StartsWith("::{")) {
  1321. //Path is really a GUID
  1322. return this.DisplayName;
  1323. }
  1324. else {
  1325. if (m_IsDisk) {
  1326. return m_Path.Substring(0, 1);
  1327. }
  1328. else {
  1329. return System.IO.Path.GetFileName(m_Path);
  1330. }
  1331. }
  1332. }
  1333. #endregion
  1334. #region " ReFreshDirectories"
  1335. ///<Summary> A lower cost way of Refreshing the Directories of this ShellItem</Summary>
  1336. ///<returns> Returns True if there were any changes</returns>
  1337. public bool RefreshDirectories()
  1338. {
  1339. bool functionReturnValue = false;
  1340. functionReturnValue = false;
  1341. //value unless there were changes
  1342. if (m_IsFolder) {
  1343. //if not a folder, then return false
  1344. ArrayList InvalidDirs = new ArrayList();
  1345. //holds CShItems of not found dirs
  1346. if ((m_Directories == null)) {
  1347. m_Directories = GetContents(ShellDll.SHCONTF.FOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, false);
  1348. //changed from unexamined to examined
  1349. functionReturnValue = true;
  1350. }
  1351. else {
  1352. //Get relative PIDLs from current directory items
  1353. ArrayList curPidls = GetContents(ShellDll.SHCONTF.FOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, true);
  1354. //IntPtr iptr = default(IntPtr);
  1355. //used below
  1356. if (curPidls.Count < 1) {
  1357. if (m_Directories.Count > 0) {
  1358. m_Directories = new ArrayList();
  1359. //nothing there anymore
  1360. //Changed from had some to have none
  1361. functionReturnValue = true;
  1362. }
  1363. else {
  1364. //Empty before, Empty now, do nothing -- just a logic marker
  1365. }
  1366. }
  1367. else {
  1368. //still has some. Are they the same?
  1369. if (m_Directories.Count < 1) {
  1370. //didn't have any before, so different
  1371. m_Directories = GetContents(ShellDll.SHCONTF.FOLDERS | ShellDll.SHCONTF.INCLUDEHIDDEN, false);
  1372. //changed from had none to have some
  1373. functionReturnValue = true;
  1374. }
  1375. else {
  1376. //some before, some now. Same? This is the complicated part
  1377. //Firstly, build ArrayLists of Relative Pidls
  1378. ArrayList compList = new ArrayList(curPidls);
  1379. //Since we are only comparing relative PIDLs, build a list of
  1380. // the relative PIDLs of the old content -- saving repeated building
  1381. int iOld = 0;
  1382. IntPtr[] OldRel = new IntPtr[m_Directories.Count];
  1383. for (iOld = 0; iOld <= m_Directories.Count - 1; iOld++) {
  1384. //GetLastID returns a ptr into an EXISTING IDLIST -- never release that ptr
  1385. // and never release the EXISTING IDLIST before thru with OldRel
  1386. OldRel[iOld] = GetLastID(((ShellItem)m_Directories[iOld]).PIDL);
  1387. }
  1388. int iNew = 0;
  1389. for (iOld = 0; iOld <= m_Directories.Count - 1; iOld++) {
  1390. for (iNew = 0; iNew <= compList.Count - 1; iNew++) {
  1391. if (IsEqual((IntPtr)compList[iNew], OldRel[iOld])) {
  1392. compList.RemoveAt(iNew);
  1393. //Match, don't look at this one again
  1394. //content item exists in both
  1395. goto NXTOLD;
  1396. }
  1397. }
  1398. //falling thru here means couldn't find iOld entry
  1399. InvalidDirs.Add(m_Directories[iOld]);
  1400. //save off the unmatched ShellItem
  1401. functionReturnValue = true;
  1402. NXTOLD: functionReturnValue = !!functionReturnValue;
  1403. }
  1404. //any not found should be removed from m_Directories
  1405. //ShellItem csi = default(ShellItem);
  1406. foreach (var obj in InvalidDirs) {
  1407. ShellItem csi = obj as ShellItem;
  1408. m_Directories.Remove(csi);
  1409. }
  1410. //anything remaining in compList is a new entry
  1411. if (compList.Count > 0) {
  1412. functionReturnValue = true;
  1413. foreach (var obj in compList) {
  1414. IntPtr iptr = (IntPtr)obj;
  1415. //these are relative PIDLs
  1416. m_Directories.Add(new ShellItem(m_Folder, iptr, this));
  1417. }
  1418. }
  1419. if (functionReturnValue) {
  1420. //something added or removed, resort
  1421. m_Directories.Sort();
  1422. }
  1423. }
  1424. //we obtained some new relative PIDLs in curPidls, so free them
  1425. foreach (var obj in curPidls) {
  1426. IntPtr iptr = (IntPtr)obj;
  1427. Marshal.FreeCoTaskMem(iptr);
  1428. }
  1429. //end of content comparison
  1430. }
  1431. //end of IsNothing test
  1432. }
  1433. }
  1434. //end of IsFolder test
  1435. return functionReturnValue;
  1436. }
  1437. #endregion
  1438. #region " ToString"
  1439. /// <summary>
  1440. /// Returns the DisplayName as the normal ToString value
  1441. /// </summary>
  1442. ///
  1443. public override string ToString()
  1444. {
  1445. return m_DisplayName;
  1446. }
  1447. #endregion
  1448. #region " Debug Dumper"
  1449. /// <summary>
  1450. /// Summary of DebugDump.
  1451. /// </summary>
  1452. ///
  1453. public void DebugDump()
  1454. {
  1455. Debug.WriteLine("DisplayName = " + m_DisplayName);
  1456. Debug.WriteLine("PIDL = " + m_Pidl.ToString());
  1457. Debug.WriteLine("\tPath = " + m_Path);
  1458. Debug.WriteLine("\tTypeName = " + this.TypeName);
  1459. Debug.WriteLine("\tiIconNormal = " + m_IconIndexNormal);
  1460. Debug.WriteLine("\tiIconSelect = " + m_IconIndexOpen);
  1461. Debug.WriteLine("\tIsBrowsable = " + m_IsBrowsable);
  1462. Debug.WriteLine("\tIsFileSystem= " + m_IsFileSystem);
  1463. Debug.WriteLine("\tIsFolder = " + m_IsFolder);
  1464. Debug.WriteLine("\tIsLink = " + m_IsLink);
  1465. Debug.WriteLine("\tIsDropTarget = " + m_IsDropTarget);
  1466. Debug.WriteLine("\tIsReadOnly = " + this.IsReadOnly);
  1467. Debug.WriteLine("\tCanCopy = " + this.CanCopy);
  1468. Debug.WriteLine("\tCanLink = " + this.CanLink);
  1469. Debug.WriteLine("\tCanMove = " + this.CanMove);
  1470. Debug.WriteLine("\tCanDelete = " + this.CanDelete);
  1471. if (m_IsFolder) {
  1472. if ((m_Directories != null)) {
  1473. Debug.WriteLine("\tDirectory Count = " + m_Directories.Count);
  1474. }
  1475. else {
  1476. Debug.WriteLine("\tDirectory Count Not yet set");
  1477. }
  1478. }
  1479. }
  1480. #endregion
  1481. #region " GetDropTargetOf"
  1482. public IDropTarget GetDropTargetOf(Control tn)
  1483. {
  1484. //if ((m_Folder == null)) return null;
  1485. //IntPtr[] apidl = new IntPtr[1];
  1486. //int HR = 0;
  1487. IDropTarget theInterface = null;
  1488. //IntPtr tnH = tn.Handle;
  1489. //HR = m_Folder.CreateViewObject(tnH, ref ShellDll.IID_IDropTarget, ref theInterface);
  1490. //if (HR != 0) {
  1491. // Marshal.ThrowExceptionForHR(HR);
  1492. //}
  1493. return theInterface;
  1494. }
  1495. #endregion
  1496. #endregion
  1497. #endregion
  1498. #region " Private Instance Methods"
  1499. #region " GetContents"
  1500. ///<Summary>
  1501. /// Returns the requested Items of this Folder as an ArrayList of CShitems
  1502. /// unless the IntPtrOnly flag is set. If IntPtrOnly is True, return an
  1503. /// ArrayList of PIDLs.
  1504. ///</Summary>
  1505. /// <param name="flags">A set of one or more ShellDll.SHCONTF flags indicating which items to return</param>
  1506. /// <param name="IntPtrOnly">True to suppress generation of CShItems, returning only an
  1507. /// ArrayList of IntPtrs to RELATIVE PIDLs for the contents of this Folder</param>
  1508. private ArrayList GetContents(ShellDll.SHCONTF flags, [System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(false)] // ERROR: Optional parameters aren't supported in C#
  1509. bool IntPtrOnly)
  1510. {
  1511. ArrayList rVal = new ArrayList();
  1512. int HR = 0;
  1513. ShellDll.IEnumIDList IEnum = null;
  1514. HR = m_Folder.EnumObjects(0, flags, ref IEnum);
  1515. if (HR == ShellDll.NOERROR) {
  1516. IntPtr item = IntPtr.Zero;
  1517. int itemCnt = 0;
  1518. HR = IEnum.GetNext(1, ref item, ref itemCnt);
  1519. if (HR == ShellDll.NOERROR) {
  1520. while (itemCnt > 0 && !item.Equals(IntPtr.Zero)) {
  1521. //there is no setting to exclude folders from enumeration,
  1522. // just one to include non-folders
  1523. // so we have to screen the results to return only
  1524. // non-folders if folders are not wanted
  1525. if ((flags & ShellDll.SHCONTF.FOLDERS) == 0) {
  1526. //don't want folders. see if this is one
  1527. ShellDll.SFGAO attrFlag = default(ShellDll.SFGAO);
  1528. attrFlag = attrFlag | ShellDll.SFGAO.FOLDER | ShellDll.SFGAO.STREAM;
  1529. //Note: for GetAttributesOf, we must provide an array, in all cases with 1 element
  1530. IntPtr[] aPidl = new IntPtr[1];
  1531. aPidl[0] = item;
  1532. m_Folder.GetAttributesOf(1, aPidl, ref attrFlag);
  1533. if (!XPorAbove) {
  1534. if ((attrFlag & ShellDll.SFGAO.FOLDER) != 0) {
  1535. //Don't need it
  1536. goto SKIPONE;
  1537. }
  1538. }
  1539. else {
  1540. //XP or above
  1541. if ((attrFlag & ShellDll.SFGAO.FOLDER) != 0 && (attrFlag & ShellDll.SFGAO.STREAM) == 0) {
  1542. goto SKIPONE;
  1543. }
  1544. }
  1545. }
  1546. if (IntPtrOnly) {
  1547. //just relative pidls for fast look, no CShITem overhead
  1548. //caller must free
  1549. rVal.Add(PIDLClone(item));
  1550. }
  1551. else {
  1552. rVal.Add(new ShellItem(m_Folder, item, this));
  1553. }
  1554. SKIPONE:
  1555. Marshal.FreeCoTaskMem(item);
  1556. //if New kept it, it kept a copy
  1557. item = IntPtr.Zero;
  1558. itemCnt = 0;
  1559. // Application.DoEvents()
  1560. HR = IEnum.GetNext(1, ref item, ref itemCnt);
  1561. }
  1562. }
  1563. else {
  1564. //1 means no more
  1565. if (HR != 1) goto HRError;
  1566. }
  1567. }
  1568. else {
  1569. goto HRError;
  1570. }
  1571. NORMAL:
  1572. //Normal Exit
  1573. if ((IEnum != null)) {
  1574. Marshal.ReleaseComObject(IEnum);
  1575. }
  1576. rVal.TrimToSize();
  1577. return rVal;
  1578. HRError:
  1579. // Error Exit for all Com errors
  1580. //not ready disks will return the following error
  1581. //If HR = &HFFFFFFFF800704C7 Then
  1582. // GoTo NORMAL
  1583. //ElseIf HR = &HFFFFFFFF80070015 Then
  1584. // GoTo NORMAL
  1585. // 'unavailable net resources will return these
  1586. //ElseIf HR = &HFFFFFFFF80040E96 Or HR = &HFFFFFFFF80040E19 Then
  1587. // GoTo NORMAL
  1588. //ElseIf HR = &HFFFFFFFF80004001 Then 'Certain "Not Implemented" features will return this
  1589. // GoTo NORMAL
  1590. //ElseIf HR = &HFFFFFFFF80004005 Then
  1591. // GoTo NORMAL
  1592. //ElseIf HR = &HFFFFFFFF800704C6 Then
  1593. // GoTo NORMAL
  1594. //End If
  1595. if ((IEnum != null)) Marshal.ReleaseComObject(IEnum);
  1596. //#If Debug Then
  1597. // Marshal.ThrowExceptionForHR(HR)
  1598. //#End If
  1599. rVal = new ArrayList();
  1600. //sometimes it is a non-fatal error,ignored
  1601. goto NORMAL;
  1602. }
  1603. #endregion
  1604. #region " Really nasty Pidl manipulation"
  1605. /// <Summary>
  1606. /// Get FileSize in bytes of the first (possibly only)
  1607. /// SHItem in an ID list. Note: the full Size of
  1608. /// the item is the sum of the sizes of all SHItems
  1609. /// in the list!!
  1610. /// </Summary>
  1611. /// <param name="pidl"></param>
  1612. ///
  1613. private static int ItemIDSize(IntPtr pidl)
  1614. {
  1615. if (!pidl.Equals(IntPtr.Zero)) {
  1616. byte[] b = new byte[2];
  1617. Marshal.Copy(pidl, b, 0, 2);
  1618. return b[1] * 256 + b[0];
  1619. }
  1620. else {
  1621. return 0;
  1622. }
  1623. }
  1624. /// <summary>
  1625. /// computes the actual Size of the ItemIDList pointed to by pidl
  1626. /// </summary>
  1627. /// <param name="pidl">The pidl pointing to an ItemIDList</param>
  1628. ///<returns> Returns actual Size of the ItemIDList, less the terminating nulnul</returns>
  1629. public static int ItemIDListSize(IntPtr pidl)
  1630. {
  1631. if (!pidl.Equals(IntPtr.Zero)) {
  1632. int i = ItemIDSize(pidl);
  1633. int b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
  1634. while (b > 0) {
  1635. i += b;
  1636. b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
  1637. }
  1638. return i;
  1639. }
  1640. else {
  1641. return 0;
  1642. }
  1643. }
  1644. /// <summary>
  1645. /// Counts the total number of SHItems in input pidl
  1646. /// </summary>
  1647. /// <param name="pidl">The pidl to obtain the count for</param>
  1648. /// <returns> Returns the count of SHItems pointed to by pidl</returns>
  1649. public static int PidlCount(IntPtr pidl)
  1650. {
  1651. if (!pidl.Equals(IntPtr.Zero)) {
  1652. int cnt = 0;
  1653. int i = 0;
  1654. int b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
  1655. while (b > 0) {
  1656. cnt += 1;
  1657. i += b;
  1658. b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
  1659. }
  1660. return cnt;
  1661. }
  1662. else {
  1663. return 0;
  1664. }
  1665. }
  1666. ///<Summary>GetLastId -- returns a pointer to the last ITEMID in a valid
  1667. /// ITEMIDLIST. Returned pointer SHOULD NOT be released since it
  1668. /// points to place within the original PIDL</Summary>
  1669. ///<returns>IntPtr pointing to last ITEMID in ITEMIDLIST structure,
  1670. /// Returns IntPtr.Zero if given a null pointer.
  1671. /// If given a pointer to the Desktop, will return same pointer.</returns>
  1672. ///<remarks>This is what the API ILFindLastID does, however IL...
  1673. /// functions are not supported before Win2K.</remarks>
  1674. public static IntPtr GetLastID(IntPtr pidl)
  1675. {
  1676. if (!pidl.Equals(IntPtr.Zero)) {
  1677. int prev = 0;
  1678. int i = 0;
  1679. int b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
  1680. while (b > 0) {
  1681. prev = i;
  1682. i += b;
  1683. b = Marshal.ReadByte(pidl, i) + (Marshal.ReadByte(pidl, i + 1) * 256);
  1684. }
  1685. return new IntPtr(pidl.ToInt32() + prev);
  1686. }
  1687. else {
  1688. return IntPtr.Zero;
  1689. }
  1690. }
  1691. public static IntPtr[] DecomposePIDL(IntPtr pidl)
  1692. {
  1693. int lim = ItemIDListSize(pidl);
  1694. IntPtr[] PIDLs = new IntPtr[PidlCount(pidl)];
  1695. int i = 0;
  1696. int curB = 0;
  1697. int offSet = 0;
  1698. while (curB < lim) {
  1699. IntPtr thisPtr = new IntPtr(pidl.ToInt32() + curB);
  1700. offSet = Marshal.ReadByte(thisPtr) + (Marshal.ReadByte(thisPtr, 1) * 256);
  1701. PIDLs[i] = Marshal.AllocCoTaskMem(offSet + 2);
  1702. byte[] b = new byte[offSet + 2];
  1703. Marshal.Copy(thisPtr, b, 0, offSet);
  1704. b[offSet] = 0;
  1705. b[offSet + 1] = 0;
  1706. Marshal.Copy(b, 0, PIDLs[i], offSet + 2);
  1707. //DumpPidl(PIDLs(i))
  1708. curB += offSet;
  1709. i += 1;
  1710. }
  1711. return PIDLs;
  1712. }
  1713. private static IntPtr PIDLClone(IntPtr pidl)
  1714. {
  1715. IntPtr functionReturnValue = default(IntPtr);
  1716. int cb = ItemIDListSize(pidl);
  1717. byte[] b = new byte[cb + 2];
  1718. Marshal.Copy(pidl, b, 0, cb);
  1719. //not including terminating nulnul
  1720. b[cb] = 0;
  1721. b[cb + 1] = 0;
  1722. //force to nulnul
  1723. functionReturnValue = Marshal.AllocCoTaskMem(cb + 2);
  1724. Marshal.Copy(b, 0, functionReturnValue, cb + 2);
  1725. return functionReturnValue;
  1726. }
  1727. public static bool IsEqual(IntPtr Pidl1, IntPtr Pidl2)
  1728. {
  1729. if (Win2KOrAbove) {
  1730. return ShellDll.ILIsEqual(Pidl1, Pidl2);
  1731. }
  1732. else {
  1733. //do hard way, may not work for some folders on XP
  1734. int cb1 = 0;
  1735. int cb2 = 0;
  1736. cb1 = ItemIDListSize(Pidl1);
  1737. cb2 = ItemIDListSize(Pidl2);
  1738. if (cb1 != cb2) return false;
  1739. int lim32 = cb1 / 4;
  1740. int i = 0;
  1741. for (i = 0; i <= lim32 - 1; i++) {
  1742. if (Marshal.ReadInt32(Pidl1, i) != Marshal.ReadInt32(Pidl2, i)) {
  1743. return false;
  1744. }
  1745. }
  1746. int limB = cb1 % 4;
  1747. int offset = lim32 * 4;
  1748. for (i = 0; i <= limB - 1; i++) {
  1749. if (Marshal.ReadByte(Pidl1, offset + i) != Marshal.ReadByte(Pidl2, offset + i)) {
  1750. return false;
  1751. }
  1752. }
  1753. //made it to here, so they are equal
  1754. return true;
  1755. }
  1756. }
  1757. /// <summary>
  1758. /// Concatenates the contents of two pidls into a new Pidl (ended by 00)
  1759. /// allocating CoTaskMem to hold the result,
  1760. /// placing the concatenation (followed by 00) into the
  1761. /// allocated Memory,
  1762. /// and returning an IntPtr pointing to the allocated mem
  1763. /// </summary>
  1764. /// <param name="pidl1">IntPtr to a well formed SHItemIDList or IntPtr.Zero</param>
  1765. /// <param name="pidl2">IntPtr to a well formed SHItemIDList or IntPtr.Zero</param>
  1766. /// <returns>Returns a ptr to an ItemIDList containing the
  1767. /// concatenation of the two (followed by the req 2 zeros
  1768. /// Caller must Free this pidl when done with it</returns>
  1769. public static IntPtr concatPidls(IntPtr pidl1, IntPtr pidl2)
  1770. {
  1771. int cb1 = 0;
  1772. int cb2 = 0;
  1773. cb1 = ItemIDListSize(pidl1);
  1774. cb2 = ItemIDListSize(pidl2);
  1775. int rawCnt = cb1 + cb2;
  1776. if ((rawCnt) > 0) {
  1777. byte[] b = new byte[rawCnt + 2];
  1778. if (cb1 > 0) {
  1779. Marshal.Copy(pidl1, b, 0, cb1);
  1780. }
  1781. if (cb2 > 0) {
  1782. Marshal.Copy(pidl2, b, cb1, cb2);
  1783. }
  1784. IntPtr rVal = Marshal.AllocCoTaskMem(cb1 + cb2 + 2);
  1785. b[rawCnt] = 0;
  1786. b[rawCnt + 1] = 0;
  1787. Marshal.Copy(b, 0, rVal, rawCnt + 2);
  1788. return rVal;
  1789. }
  1790. else {
  1791. return IntPtr.Zero;
  1792. }
  1793. }
  1794. /// <summary>
  1795. /// Returns an ItemIDList with the last ItemID trimed off
  1796. /// This is necessary since I cannot get SHBindToParent to work
  1797. /// It's purpose is to generate an ItemIDList for the Parent of a
  1798. /// Special Folder which can then be processed with DesktopBase.BindToObject,
  1799. /// yeilding a Folder for the parent of the Special Folder
  1800. /// It also creates and passes back a RELATIVE pidl for this item
  1801. /// </summary>
  1802. /// <param name="pidl">A pointer to a well formed ItemIDList. The PIDL to trim</param>
  1803. /// <param name="relPidl">BYREF IntPtr which will point to a new relative pidl
  1804. /// containing the contents of the last ItemID in the ItemIDList
  1805. /// terminated by the required 2 nulls.</param>
  1806. /// <returns> an ItemIDList with the last element removed.
  1807. /// Caller must Free this item when through with it
  1808. /// Also returns the new relative pidl in the 2nd parameter
  1809. /// Caller must Free this pidl as well, when through with it
  1810. ///</returns>
  1811. public static IntPtr TrimPidl(IntPtr pidl, ref IntPtr relPidl)
  1812. {
  1813. int cb = ItemIDListSize(pidl);
  1814. byte[] b = new byte[cb + 2];
  1815. Marshal.Copy(pidl, b, 0, cb);
  1816. int prev = 0;
  1817. int i = b[0] + (b[1] * 256);
  1818. //Do While i < cb AndAlso b(i) <> 0
  1819. while (i > 0 && i < cb) {
  1820. //Changed code
  1821. prev = i;
  1822. i += b[i] + (b[i + 1] * 256);
  1823. }
  1824. if ((prev + 1) < cb) {
  1825. //first set up the relative pidl
  1826. b[cb] = 0;
  1827. b[cb + 1] = 0;
  1828. int cb1 = b[prev] + (b[prev + 1] * 256);
  1829. relPidl = Marshal.AllocCoTaskMem(cb1 + 2);
  1830. Marshal.Copy(b, prev, relPidl, cb1 + 2);
  1831. b[prev] = 0;
  1832. b[prev + 1] = 0;
  1833. IntPtr rVal = Marshal.AllocCoTaskMem(prev + 2);
  1834. Marshal.Copy(b, 0, rVal, prev + 2);
  1835. return rVal;
  1836. }
  1837. else {
  1838. return IntPtr.Zero;
  1839. }
  1840. }
  1841. #region " DumpPidl Routines"
  1842. /// <summary>
  1843. /// Dumps, to the Debug output, the contents of the mem block pointed to by
  1844. /// a PIDL. Depends on the internal structure of a PIDL
  1845. /// </summary>
  1846. /// <param name="pidl">The IntPtr(a PIDL) pointing to the block to dump</param>
  1847. ///
  1848. public static void DumpPidl(IntPtr pidl)
  1849. {
  1850. int cb = ItemIDListSize(pidl);
  1851. Debug.WriteLine("PIDL " + pidl.ToString() + " contains " + cb + " bytes");
  1852. if (cb > 0) {
  1853. byte[] b = new byte[cb + 2];
  1854. Marshal.Copy(pidl, b, 0, cb + 1);
  1855. int pidlCnt = 1;
  1856. int i = b[0] + (b[1] * 256);
  1857. int curB = 0;
  1858. while (i > 0) {
  1859. Debug.Write("ItemID #" + pidlCnt + " Length = " + i);
  1860. DumpHex(b, curB, curB + i - 1);
  1861. pidlCnt += 1;
  1862. curB += i;
  1863. i = b[curB] + (b[curB + 1] * 256);
  1864. }
  1865. }
  1866. }
  1867. ///<Summary>Dump a portion or all of a Byte Array to Debug output</Summary>
  1868. ///<param name = "b">A single dimension Byte Array</param>
  1869. ///<param name = "sPos">Optional start index of area to dump (default = 0)</param>
  1870. ///<param name = "epos">Optional last index position to dump (default = end of array)</param>
  1871. ///<Remarks>
  1872. ///</Remarks>
  1873. public static void DumpHex(byte[] b, [System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(0)] // ERROR: Optional parameters aren't supported in C#
  1874. int sPos, [System.Runtime.InteropServices.OptionalAttribute, System.Runtime.InteropServices.DefaultParameterValueAttribute(0)] // ERROR: Optional parameters aren't supported in C#
  1875. int ePos)
  1876. {
  1877. if (ePos == 0) ePos = b.Length - 1;
  1878. int j = 0;
  1879. int curB = sPos;
  1880. string sTmp = null;
  1881. char ch = '\0';
  1882. StringBuilder SBH = new StringBuilder();
  1883. StringBuilder SBT = new StringBuilder();
  1884. for (j = 0; j <= ePos - sPos; j++) {
  1885. if (j % 16 == 0) {
  1886. Debug.WriteLine(SBH.ToString() + SBT.ToString());
  1887. SBH = new StringBuilder();
  1888. SBT = new StringBuilder(" ");
  1889. SBH.Append(HexNum(j + sPos, 4) + "). ");
  1890. }
  1891. if (b[curB] < 16) {
  1892. sTmp = "0" + Convert.ToString(b[curB], 16);
  1893. }
  1894. else {
  1895. sTmp = Convert.ToString(b[curB], 16);
  1896. }
  1897. SBH.Append(sTmp);
  1898. SBH.Append(" ");
  1899. ch = (char)(b[curB]);
  1900. if (char.IsControl(ch)) {
  1901. SBT.Append(".");
  1902. }
  1903. else {
  1904. SBT.Append(ch);
  1905. }
  1906. curB += 1;
  1907. }
  1908. int fill = (j) % 16;
  1909. if (fill != 0) {
  1910. SBH.Append(' ', 48 - (3 * ((j) % 16)));
  1911. }
  1912. Debug.WriteLine(SBH.ToString() + SBT.ToString());
  1913. }
  1914. public static string HexNum(int num, int nrChrs)
  1915. {
  1916. string h = Convert.ToString(num, 16);
  1917. StringBuilder SB = new StringBuilder();
  1918. int i = 0;
  1919. for (i = 1; i <= nrChrs - h.Length; i++) {
  1920. SB.Append("0");
  1921. }
  1922. SB.Append(h);
  1923. return SB.ToString();
  1924. }
  1925. #endregion
  1926. #endregion
  1927. #endregion
  1928. #region " TagComparer Class"
  1929. ///<Summary> It is sometimes useful to sort a list of TreeNodes,
  1930. /// ListViewItems, or other objects in an order based on CShItems in their Tag
  1931. /// use this Icomparer for that Sort</Summary>
  1932. public class TagComparer : IComparer
  1933. {
  1934. public int Compare(object x, object y)
  1935. {
  1936. //ShellItem xTag = x.tag;
  1937. //ShellItem yTag = y.tag;
  1938. //return xTag.CompareTo(y.tag);
  1939. return 0;
  1940. }
  1941. }
  1942. #endregion
  1943. #region " cPidl Class"
  1944. ///<Summary>cPidl class contains a Byte() representation of a PIDL and
  1945. /// certain Methods and Properties for comparing one cPidl to another</Summary>
  1946. public class cPidl : IEnumerable
  1947. {
  1948. #region " Private Fields"
  1949. byte[] m_bytes = null;
  1950. //The local copy of the PIDL
  1951. int m_ItemCount = 0;
  1952. //the # of ItemIDs in this ItemIDList (PIDL)
  1953. #endregion
  1954. #region " Constructor"
  1955. public cPidl(IntPtr pidl)
  1956. {
  1957. int cb = ItemIDListSize(pidl);
  1958. if (cb > 0) {
  1959. m_bytes = new byte[cb + 2];
  1960. Marshal.Copy(pidl, m_bytes, 0, cb);
  1961. //DumpPidl(pidl)
  1962. }
  1963. else {
  1964. m_bytes = new byte[2]; //This is the DeskTop (we hope)
  1965. }
  1966. //ensure nulnul
  1967. m_bytes[m_bytes.Length - 2]= 0;
  1968. m_bytes[m_bytes.Length - 1] = 0;
  1969. m_ItemCount = PidlCount(pidl);
  1970. }
  1971. #endregion
  1972. #region " Public Properties"
  1973. public byte[] PidlBytes {
  1974. get { return m_bytes; }
  1975. }
  1976. public int Length {
  1977. get { return m_bytes.Length; }
  1978. }
  1979. public int ItemCount {
  1980. get { return m_ItemCount; }
  1981. }
  1982. #endregion
  1983. #region " Public Intstance Methods -- ToPIDL, Decompose, and IsEqual"
  1984. ///<Summary> Copy the contents of a byte() containing a pidl to
  1985. /// CoTaskMemory, returning an IntPtr that points to that mem block
  1986. /// Assumes that this cPidl is properly terminated, as all New
  1987. /// cPidls are.
  1988. /// Caller must Free the returned IntPtr when done with mem block.
  1989. ///</Summary>
  1990. public IntPtr ToPIDL()
  1991. {
  1992. return BytesToPidl(m_bytes);
  1993. }
  1994. ///<Summary>Returns an object containing a byte() for each of this cPidl's
  1995. /// ITEMIDs (individual PIDLS), in order such that obj(0) is
  1996. /// a byte() containing the bytes of the first ITEMID, etc.
  1997. /// Each ITEMID is properly terminated with a nulnul
  1998. ///</Summary>
  1999. public object[] Decompose()
  2000. {
  2001. object[] bArrays = new object[this.ItemCount];
  2002. ICPidlEnumerator eByte = this.GetEnumerator() as ICPidlEnumerator;
  2003. int i = 0;
  2004. while (eByte.MoveNext()) {
  2005. bArrays[i] = eByte.Current;
  2006. i += 1;
  2007. }
  2008. return bArrays;
  2009. }
  2010. ///<Summary>Returns True if input cPidl's content exactly match the
  2011. /// contents of this instance</Summary>
  2012. public bool IsEqual(cPidl other)
  2013. {
  2014. bool functionReturnValue = false;
  2015. functionReturnValue = false;
  2016. //assume not
  2017. if (other.Length != this.Length) return functionReturnValue;
  2018. byte[] ob = other.PidlBytes;
  2019. int i = 0;
  2020. for (i = 0; i <= this.Length - 1; i++) {
  2021. //note: we look at nulnul also
  2022. if (ob[i] != m_bytes[i]) return functionReturnValue;
  2023. }
  2024. //all equal on fall thru
  2025. return true;
  2026. }
  2027. #endregion
  2028. #region " Public Shared Methods"
  2029. #region " JoinPidlBytes"
  2030. ///<Summary> Join two byte arrays containing PIDLS, returning a
  2031. /// Byte() containing the resultant ITEMIDLIST. Both Byte() must
  2032. /// be properly terminated (nulnul)
  2033. /// Returns NOTHING if error
  2034. /// </Summary>
  2035. public static byte[] JoinPidlBytes(byte[] b1, byte[] b2)
  2036. {
  2037. if (IsValidPidl(b1) & IsValidPidl(b2)) {
  2038. byte[] b = new byte[b1.Length + b2.Length - 2];
  2039. //allow for leaving off first nulnul
  2040. Array.Copy(b1, b, b1.Length - 2);
  2041. Array.Copy(b2, 0, b, b1.Length - 2, b2.Length);
  2042. if (IsValidPidl(b)) {
  2043. return b;
  2044. }
  2045. else {
  2046. return null;
  2047. }
  2048. }
  2049. else {
  2050. return null;
  2051. }
  2052. }
  2053. #endregion
  2054. #region " BytesToPidl"
  2055. ///<Summary> Copy the contents of a byte() containing a pidl to
  2056. /// CoTaskMemory, returning an IntPtr that points to that mem block
  2057. /// Caller must free the IntPtr when done with it
  2058. ///</Summary>
  2059. public static IntPtr BytesToPidl(byte[] b)
  2060. {
  2061. IntPtr functionReturnValue = default(IntPtr);
  2062. functionReturnValue = IntPtr.Zero;
  2063. //assume failure
  2064. if (IsValidPidl(b)) {
  2065. int bLen = b.Length;
  2066. functionReturnValue = Marshal.AllocCoTaskMem(bLen);
  2067. if (functionReturnValue.Equals(IntPtr.Zero)) return functionReturnValue;
  2068. //another bad error
  2069. Marshal.Copy(b, 0, functionReturnValue, bLen);
  2070. }
  2071. return functionReturnValue;
  2072. }
  2073. #endregion
  2074. #region " StartsWith"
  2075. ///<Summary>returns True if the beginning of pidlA matches PidlB exactly for pidlB's entire length</Summary>
  2076. public static bool StartsWith(IntPtr pidlA, IntPtr pidlB)
  2077. {
  2078. return cPidl.StartsWith(new cPidl(pidlA), new cPidl(pidlB));
  2079. }
  2080. ///<Summary>returns True if the beginning of A matches B exactly for B's entire length</Summary>
  2081. public static bool StartsWith(cPidl A, cPidl B)
  2082. {
  2083. return A.StartsWith(B);
  2084. }
  2085. ///<Summary>Returns true if the CPidl input parameter exactly matches the
  2086. /// beginning of this instance of CPidl</Summary>
  2087. public bool StartsWith(cPidl cp)
  2088. {
  2089. byte[] b = cp.PidlBytes;
  2090. if (b.Length > m_bytes.Length) return false;
  2091. //input is longer
  2092. int i = 0;
  2093. for (i = 0; i <= b.Length - 3; i++) {
  2094. //allow for nulnul at end of cp.PidlBytes
  2095. if (b[i] != m_bytes[i]) return false;
  2096. }
  2097. return true;
  2098. }
  2099. #endregion
  2100. #endregion
  2101. #region " GetEnumerator"
  2102. public System.Collections.IEnumerator GetEnumerator()
  2103. {
  2104. return new ICPidlEnumerator(m_bytes);
  2105. }
  2106. #endregion
  2107. #region " PIDL enumerator Class"
  2108. private class ICPidlEnumerator : IEnumerator
  2109. {
  2110. private int m_sPos;
  2111. //the first index in the current PIDL
  2112. private int m_ePos;
  2113. //the last index in the current PIDL
  2114. private byte[] m_bytes;
  2115. //the local copy of the PIDL
  2116. private bool m_NotEmpty = false;
  2117. //the desktop PIDL is zero length
  2118. public ICPidlEnumerator(byte[] b)
  2119. {
  2120. m_bytes = b;
  2121. if (b.Length > 0) m_NotEmpty = true;
  2122. m_sPos = -1;
  2123. m_ePos = -1;
  2124. }
  2125. public object Current {
  2126. get {
  2127. if (m_sPos < 0) throw new InvalidOperationException("ICPidlEnumerator --- attempt to get Current with invalidated list");
  2128. byte[] b = new byte[(m_ePos - m_sPos) + 3];
  2129. //room for nulnul
  2130. Array.Copy(m_bytes, m_sPos, b, 0, b.Length - 2);
  2131. b[b.Length - 2] = 0;
  2132. b[b.Length - 1] = 0;
  2133. //add nulnul
  2134. return b;
  2135. }
  2136. }
  2137. public bool MoveNext()
  2138. {
  2139. if (m_NotEmpty) {
  2140. if (m_sPos < 0) {
  2141. m_sPos = 0;
  2142. m_ePos = -1;
  2143. }
  2144. else {
  2145. m_sPos = m_ePos + 1;
  2146. }
  2147. if (m_bytes.Length < m_sPos + 1) throw new InvalidCastException("Malformed PIDL");
  2148. int cb = m_bytes[m_sPos] + m_bytes[m_sPos + 1] * 256;
  2149. if (cb == 0) {
  2150. //have passed all back
  2151. return false;
  2152. }
  2153. else {
  2154. m_ePos += cb;
  2155. }
  2156. }
  2157. else {
  2158. m_sPos = 0;
  2159. m_ePos = 0;
  2160. //in this case, we have exhausted the list of 0 ITEMIDs
  2161. return false;
  2162. }
  2163. return true;
  2164. }
  2165. public void Reset()
  2166. {
  2167. m_sPos = -1;
  2168. m_ePos = -1;
  2169. }
  2170. }
  2171. #endregion
  2172. }
  2173. #endregion
  2174. }