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

Revision 2282, 11.0 KB checked in by lowjoel, 4 years ago (diff)

Prevent recursive folder definitions when users use Secure Move to move a folder to be a subfolder of the source directory.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2010 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        /// Makes the first path relative to the second.
94        /// </summary>
95        /// <remarks>Modified from:
96        /// http://mrpmorris.blogspot.com/2007/05/convert-absolute-path-to-relative-path.html</remarks>
97        /// <param name="absolutePath">The path to use as the root of the relative path.</param>
98        /// <param name="relativeTo">The path to make relative.</param>
99        /// <returns>The relative path to the provided path.</returns>
100        public static string MakeRelativeTo(FileSystemInfo absolutePath, string relativeTo)
101        {
102            string[] absoluteDirectories = absolutePath.FullName.Split(
103                Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
104            string[] relativeDirectories = relativeTo.Split(
105                Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
106
107            //Get the shortest of the two paths
108            int length = absoluteDirectories.Length < relativeDirectories.Length ?
109                absoluteDirectories.Length : relativeDirectories.Length;
110
111            //Use to determine where in the loop we exited
112            int lastCommonRoot = -1;
113            int index;
114
115            //Find common root
116            for (index = 0; index < length; index++)
117                if (absoluteDirectories[index] == relativeDirectories[index])
118                    lastCommonRoot = index;
119                else
120                    break;
121
122            //If we didn't find a common prefix then throw
123            if (lastCommonRoot == -1)
124                throw new ArgumentException("Paths do not have a common base");
125
126            //Build up the relative path
127            StringBuilder relativePath = new StringBuilder();
128
129            //Add on the ..
130            for (index = lastCommonRoot + 1; index < absoluteDirectories.Length; index++)
131                if (absoluteDirectories[index].Length > 0)
132                    relativePath.Append("..\\");
133
134            //Add on the folders
135            for (index = lastCommonRoot + 1; index < relativeDirectories.Length - 1; index++)
136                relativePath.Append(relativeDirectories[index] + "\\");
137            if (lastCommonRoot < relativeDirectories.Length - 1)
138                relativePath.Append(relativeDirectories[relativeDirectories.Length - 1]);
139
140            return relativePath.ToString();
141        }
142
143        /// <summary>
144        /// Verifies if the path given is rooted at the given absolute path.
145        /// </summary>
146        /// <param name="absolutePath">The root path.</param>
147        /// <param name="path">The path to verify.</param>
148        /// <returns>True if the path provided is a subfolder/sub-file of the provided root path.</returns>
149        public static bool IsRootedAt(FileSystemInfo absolutePath, string path)
150        {
151            //Convert the path in question to an absolute path
152            if (!Path.IsPathRooted(path))
153                path = Path.GetFullPath(path);
154
155            //Split the directory path to its component folders
156            string[] absoluteDirectories = absolutePath.FullName.Split(
157                Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
158            string[] relativeDirectories = path.Split(
159                Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
160
161            //Compare element by element; if the absolute path compares till the end, the
162            //provided path is a subdirectory
163            for (int i = 0; i < absoluteDirectories.Length; ++i)
164                if (absoluteDirectories[i] != relativeDirectories[i])
165                    return false;
166
167            return true;
168        }
169
170        /// <summary>
171        /// A List of known folder IDs in the shell namespace.
172        /// </summary>
173        public static class KnownFolderIDs
174        {
175            /// <summary>
176            /// The Known Folder ID of the Recycle Bin
177            /// </summary>
178            public static readonly Guid RecycleBin = 
179                new Guid(0xB7534046, 0x3ECB, 0x4C18, 0xBE, 0x4E, 0x64, 0xCD, 0x4C, 0xB7, 0xD6, 0xAC);
180
181            /// <summary>
182            /// Gets the PIDL for the given Known folder ID.
183            /// </summary>
184            /// <param name="folderId">The known folder ID to query.</param>
185            /// <returns>The PIDL for the given folder.</returns>
186            public static ShellItemIDList GetShellItemIdList(Guid folderId)
187            {
188                Guid guid = folderId;
189                IntPtr pidl = IntPtr.Zero;
190                NativeMethods.SHGetKnownFolderIDList(ref guid, 0, IntPtr.Zero, out pidl);
191
192                try
193                {
194                    return new ShellItemIDList(pidl);
195                }
196                finally
197                {
198                    NativeMethods.ILFree(pidl);
199                }
200            }
201        }
202    }
203
204    /// <summary>
205    /// Retrieves the path of a known folder as an ITEMIDLIST structure.
206    /// </summary>
207    public class ShellCIDA
208    {
209        /// <summary>
210        /// Parses the given buffer for CIDA elements
211        /// </summary>
212        /// <param name="buffer"></param>
213        public ShellCIDA(byte[] buffer)
214        {
215            int offset = 0;
216            cidl = BitConverter.ToUInt32(buffer, offset);
217            aoffset = new ShellItemIDList[cidl + 1];
218
219            for (int i = 0; i < aoffset.Length; ++i)
220            {
221                int pidlOffset = BitConverter.ToInt32(buffer, offset += sizeof(int));
222
223                //Read the size of the IDL
224                aoffset[i] = new ShellItemIDList(buffer.Skip(pidlOffset).ToArray());
225            }
226        }
227
228        /// <summary>
229        /// The number of PIDLs that are being transferred, not including the parent folder.
230        /// </summary>
231        public uint cidl
232        {
233            get;
234            private set;
235        }
236
237        /// <summary>
238        /// The first element of aoffset contains the fully-qualified PIDL of a parent folder.
239        /// If this PIDL is empty, the parent folder is the desktop. Each of the remaining
240        /// elements of the array contains an offset to one of the PIDLs to be transferred.
241        /// All of these PIDLs are relative to the PIDL of the parent folder.
242        /// </summary>
243        public ShellItemIDList[] aoffset
244        {
245            get;
246            private set;
247        }
248    }
249
250    /// <summary>
251    /// Contains a list of item identifiers.
252    /// </summary>
253    public class ShellItemIDList
254    {
255        public ShellItemIDList(byte[] buffer)
256        {
257            mkid = new ShellItemID(buffer);
258        }
259
260        public ShellItemIDList(IntPtr buffer)
261        {
262            mkid = new ShellItemID(buffer);
263        }
264
265        public ShellItemID mkid
266        {
267            get;
268            private set;
269        }
270
271        /// <summary>
272        /// The physical path to the object referenced by this IDL.
273        /// </summary>
274        /// <remarks>If this IDL references a virtual object, this will return
275        /// null.</remarks>
276        public string Path
277        {
278            get
279            {
280                IntPtr mkid = this.mkid.ToSHITEMID();
281                try
282                {
283                    StringBuilder result = new StringBuilder(NativeMethods.MaxPath);
284                    if (NativeMethods.SHGetPathFromIDList(mkid, result))
285                        return result.ToString();
286                }
287                finally
288                {
289                    Marshal.FreeHGlobal(mkid);
290                }
291
292                return null;
293            }
294        }
295
296        /// <summary>
297        /// The GUID of the virtual folder referenced by this IDL.
298        /// </summary>
299        /// <remarks>If this IDL references a physical object, this will return
300        /// <see cref="Guid.Empty"/></remarks>
301        public Guid Guid
302        {
303            get
304            {
305                Guid[] guids = new Guid[] {
306                    Shell.KnownFolderIDs.RecycleBin
307                };
308
309                foreach (Guid guid in guids)
310                {
311                    if (Shell.KnownFolderIDs.GetShellItemIdList(guid) == this)
312                        return guid;
313                }
314
315                return Guid.Empty;
316            }
317        }
318
319        public static bool operator==(ShellItemIDList lhs, ShellItemIDList rhs)
320        {
321            return lhs.mkid == rhs.mkid;
322        }
323
324        public static bool operator!=(ShellItemIDList lhs, ShellItemIDList rhs)
325        {
326            return lhs.mkid != rhs.mkid;
327        }
328
329        public override bool Equals(object obj)
330        {
331            if (obj is ShellItemIDList)
332                return this == (ShellItemIDList)obj;
333            return this.Equals(obj);
334        }
335
336        public override int GetHashCode()
337        {
338            return base.GetHashCode();
339        }
340    }
341
342    /// <summary>
343    /// Defines an item identifier. (native type: SHITEMID)
344    /// </summary>
345    public class ShellItemID
346    {
347        public ShellItemID(byte[] buffer)
348        {
349            short cb = BitConverter.ToInt16(buffer, 0);
350            abID = new byte[cb];
351            if (cb > 0)
352                Buffer.BlockCopy(buffer, sizeof(short), abID, 0, cb - sizeof(short));
353        }
354
355        public ShellItemID(IntPtr buffer)
356        {
357            short cb = Marshal.ReadInt16(buffer);
358            abID = new byte[cb];
359            if (cb > 0)
360                Marshal.Copy(new IntPtr(buffer.ToInt64() + sizeof(short)), abID, 0, cb - sizeof(short));
361        }
362
363        byte[] abID;
364
365        /// <summary>
366        /// Converts this ShellItemID to the native SHITEMID.
367        /// </summary>
368        /// <returns>A Pointer to an unmanaged block of memory which should be
369        /// freed by Marshal.FreeHGlobal upon completion.</returns>
370        internal IntPtr ToSHITEMID()
371        {
372            //Allocate the buffer
373            IntPtr result = Marshal.AllocHGlobal(abID.Length + (abID.Length == 0 ? 0 : sizeof(short)));
374
375            //Write the size of the identifier
376            Marshal.WriteInt16(result, (short)abID.Length);
377
378            //Then copy the block of memory
379            Marshal.Copy(abID, 0, new IntPtr(result.ToInt64() + 2), abID.Length);
380            return result;
381        }
382
383        public static bool operator==(ShellItemID lhs, ShellItemID rhs)
384        {
385            return lhs.abID.SequenceEqual(rhs.abID);
386        }
387
388        public static bool operator!=(ShellItemID lhs, ShellItemID rhs)
389        {
390            return !lhs.abID.SequenceEqual(rhs.abID);
391        }
392
393        public override bool Equals(object obj)
394        {
395            if (obj is ShellItemID)
396                return this == (ShellItemID)obj;
397            return this.Equals(obj);
398        }
399
400        public override int GetHashCode()
401        {
402            return base.GetHashCode();
403        }
404    }
405}
Note: See TracBrowser for help on using the repository browser.