source: trunk/eraser/Eraser.Util/Shell.cs @ 2523

Revision 2523, 8.8 KB checked in by lowjoel, 3 years ago (diff)

Implement recycle bin erasures according to what Vista and 7 would detect as part of the Recycle Bin. This would erase recycle bins which are on UNC paths and mounted drives without a drive letter. Tested and works with Windows 7 and Windows XP SP3.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Rev URL
Line 
1/*
2 * $Id$
3 * Copyright 2008-2012 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Linq;
25using System.Text;
26
27using System.Runtime.InteropServices;
28using Microsoft.Win32;
29using System.IO;
30
31namespace Eraser.Util
32{
33    public static class Shell
34    {
35        /// <summary>
36        /// Gets or sets whether low disk space notifications are enabled for the
37        /// current user.
38        /// </summary>
39        public static bool LowDiskSpaceNotificationsEnabled
40        {
41            get
42            {
43                using (RegistryKey key = Registry.CurrentUser.OpenSubKey(
44                    "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer"))
45                {
46                    if (key == null)
47                        return true;
48                    return !Convert.ToBoolean(key.GetValue("NoLowDiskSpaceChecks", false));
49                }
50            }
51            set
52            {
53                RegistryKey key = null;
54                try
55                {
56                    key = Registry.CurrentUser.OpenSubKey(
57                        "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer", true);
58                    if (key == null)
59                        key = Registry.CurrentUser.CreateSubKey(
60                            "Software\\Microsoft\\Windows\\CurrentVersion\\Policies\\Explorer");
61                    key.SetValue("NoLowDiskSpaceChecks", !value);
62                }
63                finally
64                {
65                    if (key != null)
66                        key.Close();
67                }
68            }
69        }
70
71        /// <summary>
72        /// Parses the provided command line into its constituent arguments.
73        /// </summary>
74        /// <param name="commandLine">The command line to parse.</param>
75        /// <returns>The arguments specified in the command line</returns>
76        public static string[] ParseCommandLine(string commandLine)
77        {
78            int argc = 0;
79            IntPtr argv = NativeMethods.CommandLineToArgvW(commandLine, out argc);
80            string[] result = new string[argc];
81
82            //Get the pointers to the arguments, then read the string.
83            for (int i = 0; i < argc; ++i)
84                result[i] = Marshal.PtrToStringUni(Marshal.ReadIntPtr(argv, i * IntPtr.Size));
85
86            //Free the memory
87            NativeMethods.LocalFree(argv);
88
89            return result;
90        }
91
92        /// <summary>
93        /// A List of known folder IDs in the shell namespace.
94        /// </summary>
95        public static class KnownFolderIDs
96        {
97            /// <summary>
98            /// The Known Folder ID of the Recycle Bin
99            /// </summary>
100            public static readonly Guid RecycleBin = 
101                new Guid(0xB7534046, 0x3ECB, 0x4C18, 0xBE, 0x4E, 0x64, 0xCD, 0x4C, 0xB7, 0xD6, 0xAC);
102
103            /// <summary>
104            /// Gets the PIDL for the given Known folder ID.
105            /// </summary>
106            /// <param name="folderId">The known folder ID to query.</param>
107            /// <returns>The PIDL for the given folder.</returns>
108            public static ShellItemIDList GetShellItemIdList(Guid folderId)
109            {
110                Guid guid = folderId;
111                IntPtr pidl = IntPtr.Zero;
112                NativeMethods.SHGetKnownFolderIDList(ref guid, 0, IntPtr.Zero, out pidl);
113
114                try
115                {
116                    return new ShellItemIDList(pidl);
117                }
118                finally
119                {
120                    NativeMethods.ILFree(pidl);
121                }
122            }
123
124            /// <summary>
125            /// Retrieves the full path of a known folder identified by the folder's
126            /// KNOWNFOLDERID.
127            /// </summary>
128            /// <param name="guid">The KNOWNFOLDERID that identifies the folder.</param>
129            /// <returns>The DirectoryInfo for the given known folder path, or null if
130            /// an error occurred.</returns>
131            public static DirectoryInfo GetPath(Guid guid)
132            {
133                try
134                {
135                    IntPtr path = IntPtr.Zero;
136                    uint result = NativeMethods.SHGetKnownFolderPath(ref guid, 0, IntPtr.Zero,
137                        out path);
138
139                    if (result == 0)
140                    {
141                        string pathStr = Marshal.PtrToStringUni(path);
142                        Marshal.FreeCoTaskMem(path);
143
144                        return new DirectoryInfo(pathStr);
145                    }
146                    else
147                    {
148                        throw Marshal.GetExceptionForHR((int)result);
149                    }
150                }
151                catch (EntryPointNotFoundException)
152                {
153                    return null;
154                }
155            }
156        }
157    }
158
159    /// <summary>
160    /// Retrieves the path of a known folder as an ITEMIDLIST structure.
161    /// </summary>
162    public class ShellCIDA
163    {
164        /// <summary>
165        /// Parses the given buffer for CIDA elements
166        /// </summary>
167        /// <param name="buffer"></param>
168        public ShellCIDA(byte[] buffer)
169        {
170            int offset = 0;
171            cidl = BitConverter.ToUInt32(buffer, offset);
172            aoffset = new ShellItemIDList[cidl + 1];
173
174            for (int i = 0; i < aoffset.Length; ++i)
175            {
176                int pidlOffset = BitConverter.ToInt32(buffer, offset += sizeof(int));
177
178                //Read the size of the IDL
179                aoffset[i] = new ShellItemIDList(buffer.Skip(pidlOffset).ToArray());
180            }
181        }
182
183        /// <summary>
184        /// The number of PIDLs that are being transferred, not including the parent folder.
185        /// </summary>
186        public uint cidl
187        {
188            get;
189            private set;
190        }
191
192        /// <summary>
193        /// The first element of aoffset contains the fully-qualified PIDL of a parent folder.
194        /// If this PIDL is empty, the parent folder is the desktop. Each of the remaining
195        /// elements of the array contains an offset to one of the PIDLs to be transferred.
196        /// All of these PIDLs are relative to the PIDL of the parent folder.
197        /// </summary>
198        public ShellItemIDList[] aoffset
199        {
200            get;
201            private set;
202        }
203    }
204
205    /// <summary>
206    /// Contains a list of item identifiers.
207    /// </summary>
208    public class ShellItemIDList
209    {
210        public ShellItemIDList(byte[] buffer)
211        {
212            mkid = new ShellItemID(buffer);
213        }
214
215        public ShellItemIDList(IntPtr buffer)
216        {
217            mkid = new ShellItemID(buffer);
218        }
219
220        public ShellItemID mkid
221        {
222            get;
223            private set;
224        }
225
226        /// <summary>
227        /// The physical path to the object referenced by this IDL.
228        /// </summary>
229        /// <remarks>If this IDL references a virtual object, this will return
230        /// null.</remarks>
231        public string Path
232        {
233            get
234            {
235                IntPtr mkid = this.mkid.ToSHITEMID();
236                try
237                {
238                    StringBuilder result = new StringBuilder(NativeMethods.MaxPath);
239                    if (NativeMethods.SHGetPathFromIDList(mkid, result))
240                        return result.ToString();
241                }
242                finally
243                {
244                    Marshal.FreeHGlobal(mkid);
245                }
246
247                return null;
248            }
249        }
250
251        /// <summary>
252        /// The GUID of the virtual folder referenced by this IDL.
253        /// </summary>
254        /// <remarks>If this IDL references a physical object, this will return
255        /// <see cref="Guid.Empty"/></remarks>
256        public Guid Guid
257        {
258            get
259            {
260                Guid[] guids = new Guid[] {
261                    Shell.KnownFolderIDs.RecycleBin
262                };
263
264                foreach (Guid guid in guids)
265                {
266                    if (Shell.KnownFolderIDs.GetShellItemIdList(guid) == this)
267                        return guid;
268                }
269
270                return Guid.Empty;
271            }
272        }
273
274        public static bool operator==(ShellItemIDList lhs, ShellItemIDList rhs)
275        {
276            return lhs.mkid == rhs.mkid;
277        }
278
279        public static bool operator!=(ShellItemIDList lhs, ShellItemIDList rhs)
280        {
281            return lhs.mkid != rhs.mkid;
282        }
283
284        public override bool Equals(object obj)
285        {
286            if (obj is ShellItemIDList)
287                return this == (ShellItemIDList)obj;
288            return this.Equals(obj);
289        }
290
291        public override int GetHashCode()
292        {
293            return base.GetHashCode();
294        }
295    }
296
297    /// <summary>
298    /// Defines an item identifier. (native type: SHITEMID)
299    /// </summary>
300    public class ShellItemID
301    {
302        public ShellItemID(byte[] buffer)
303        {
304            short cb = BitConverter.ToInt16(buffer, 0);
305            abID = new byte[cb];
306            if (cb > 0)
307                Buffer.BlockCopy(buffer, sizeof(short), abID, 0, cb - sizeof(short));
308        }
309
310        public ShellItemID(IntPtr buffer)
311        {
312            short cb = Marshal.ReadInt16(buffer);
313            abID = new byte[cb];
314            if (cb > 0)
315                Marshal.Copy(new IntPtr(buffer.ToInt64() + sizeof(short)), abID, 0, cb - sizeof(short));
316        }
317
318        byte[] abID;
319
320        /// <summary>
321        /// Converts this ShellItemID to the native SHITEMID.
322        /// </summary>
323        /// <returns>A Pointer to an unmanaged block of memory which should be
324        /// freed by Marshal.FreeHGlobal upon completion.</returns>
325        internal IntPtr ToSHITEMID()
326        {
327            //Allocate the buffer
328            IntPtr result = Marshal.AllocHGlobal(abID.Length + (abID.Length == 0 ? 0 : sizeof(short)));
329
330            //Write the size of the identifier
331            Marshal.WriteInt16(result, (short)abID.Length);
332
333            //Then copy the block of memory
334            Marshal.Copy(abID, 0, new IntPtr(result.ToInt64() + 2), abID.Length);
335            return result;
336        }
337
338        public static bool operator==(ShellItemID lhs, ShellItemID rhs)
339        {
340            return lhs.abID.SequenceEqual(rhs.abID);
341        }
342
343        public static bool operator!=(ShellItemID lhs, ShellItemID rhs)
344        {
345            return !lhs.abID.SequenceEqual(rhs.abID);
346        }
347
348        public override bool Equals(object obj)
349        {
350            if (obj is ShellItemID)
351                return this == (ShellItemID)obj;
352            return this.Equals(obj);
353        }
354
355        public override int GetHashCode()
356        {
357            return base.GetHashCode();
358        }
359    }
360}
Note: See TracBrowser for help on using the repository browser.