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

Revision 2112, 9.9 KB checked in by lowjoel, 4 years ago (diff)

Implemented secure move of files and folders.

  • 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        /// A List of known folder IDs in the shell namespace.
145        /// </summary>
146        public static class KnownFolderIDs
147        {
148            /// <summary>
149            /// The Known Folder ID of the Recycle Bin
150            /// </summary>
151            public static readonly Guid RecycleBin = 
152                new Guid(0xB7534046, 0x3ECB, 0x4C18, 0xBE, 0x4E, 0x64, 0xCD, 0x4C, 0xB7, 0xD6, 0xAC);
153
154            /// <summary>
155            /// Gets the PIDL for the given Known folder ID.
156            /// </summary>
157            /// <param name="folderId">The known folder ID to query.</param>
158            /// <returns>The PIDL for the given folder.</returns>
159            public static ShellItemIDList GetShellItemIdList(Guid folderId)
160            {
161                Guid guid = folderId;
162                IntPtr pidl = IntPtr.Zero;
163                NativeMethods.SHGetKnownFolderIDList(ref guid, 0, IntPtr.Zero, out pidl);
164
165                try
166                {
167                    return new ShellItemIDList(pidl);
168                }
169                finally
170                {
171                    NativeMethods.ILFree(pidl);
172                }
173            }
174        }
175    }
176
177    /// <summary>
178    /// Retrieves the path of a known folder as an ITEMIDLIST structure.
179    /// </summary>
180    public class ShellCIDA
181    {
182        /// <summary>
183        /// Parses the given buffer for CIDA elements
184        /// </summary>
185        /// <param name="buffer"></param>
186        public ShellCIDA(byte[] buffer)
187        {
188            int offset = 0;
189            cidl = BitConverter.ToUInt32(buffer, offset);
190            aoffset = new ShellItemIDList[cidl + 1];
191
192            for (int i = 0; i < aoffset.Length; ++i)
193            {
194                int pidlOffset = BitConverter.ToInt32(buffer, offset += sizeof(int));
195
196                //Read the size of the IDL
197                aoffset[i] = new ShellItemIDList(buffer.Skip(pidlOffset).ToArray());
198            }
199        }
200
201        /// <summary>
202        /// The number of PIDLs that are being transferred, not including the parent folder.
203        /// </summary>
204        public uint cidl
205        {
206            get;
207            private set;
208        }
209
210        /// <summary>
211        /// The first element of aoffset contains the fully-qualified PIDL of a parent folder.
212        /// If this PIDL is empty, the parent folder is the desktop. Each of the remaining
213        /// elements of the array contains an offset to one of the PIDLs to be transferred.
214        /// All of these PIDLs are relative to the PIDL of the parent folder.
215        /// </summary>
216        public ShellItemIDList[] aoffset
217        {
218            get;
219            private set;
220        }
221    }
222
223    /// <summary>
224    /// Contains a list of item identifiers.
225    /// </summary>
226    public class ShellItemIDList
227    {
228        public ShellItemIDList(byte[] buffer)
229        {
230            mkid = new ShellItemID(buffer);
231        }
232
233        public ShellItemIDList(IntPtr buffer)
234        {
235            mkid = new ShellItemID(buffer);
236        }
237
238        public ShellItemID mkid
239        {
240            get;
241            private set;
242        }
243
244        /// <summary>
245        /// The physical path to the object referenced by this IDL.
246        /// </summary>
247        /// <remarks>If this IDL references a virtual object, this will return
248        /// null.</remarks>
249        public string Path
250        {
251            get
252            {
253                IntPtr mkid = this.mkid.ToSHITEMID();
254                try
255                {
256                    StringBuilder result = new StringBuilder(NativeMethods.MaxPath);
257                    if (NativeMethods.SHGetPathFromIDList(mkid, result))
258                        return result.ToString();
259                }
260                finally
261                {
262                    Marshal.FreeHGlobal(mkid);
263                }
264
265                return null;
266            }
267        }
268
269        /// <summary>
270        /// The GUID of the virtual folder referenced by this IDL.
271        /// </summary>
272        /// <remarks>If this IDL references a physical object, this will return
273        /// <see cref="Guid.Empty"/></remarks>
274        public Guid Guid
275        {
276            get
277            {
278                Guid[] guids = new Guid[] {
279                    Shell.KnownFolderIDs.RecycleBin
280                };
281
282                foreach (Guid guid in guids)
283                {
284                    if (Shell.KnownFolderIDs.GetShellItemIdList(guid) == this)
285                        return guid;
286                }
287
288                return Guid.Empty;
289            }
290        }
291
292        public static bool operator==(ShellItemIDList lhs, ShellItemIDList rhs)
293        {
294            return lhs.mkid == rhs.mkid;
295        }
296
297        public static bool operator!=(ShellItemIDList lhs, ShellItemIDList rhs)
298        {
299            return lhs.mkid != rhs.mkid;
300        }
301
302        public override bool Equals(object obj)
303        {
304            if (obj is ShellItemIDList)
305                return this == (ShellItemIDList)obj;
306            return this.Equals(obj);
307        }
308
309        public override int GetHashCode()
310        {
311            return base.GetHashCode();
312        }
313    }
314
315    /// <summary>
316    /// Defines an item identifier. (native type: SHITEMID)
317    /// </summary>
318    public class ShellItemID
319    {
320        public ShellItemID(byte[] buffer)
321        {
322            short cb = BitConverter.ToInt16(buffer, 0);
323            abID = new byte[cb];
324            if (cb > 0)
325                Buffer.BlockCopy(buffer, sizeof(short), abID, 0, cb - sizeof(short));
326        }
327
328        public ShellItemID(IntPtr buffer)
329        {
330            short cb = Marshal.ReadInt16(buffer);
331            abID = new byte[cb];
332            if (cb > 0)
333                Marshal.Copy(new IntPtr(buffer.ToInt64() + sizeof(short)), abID, 0, cb - sizeof(short));
334        }
335
336        byte[] abID;
337
338        /// <summary>
339        /// Converts this ShellItemID to the native SHITEMID.
340        /// </summary>
341        /// <returns>A Pointer to an unmanaged block of memory which should be
342        /// freed by Marshal.FreeHGlobal upon completion.</returns>
343        internal IntPtr ToSHITEMID()
344        {
345            //Allocate the buffer
346            IntPtr result = Marshal.AllocHGlobal(abID.Length + (abID.Length == 0 ? 0 : sizeof(short)));
347
348            //Write the size of the identifier
349            Marshal.WriteInt16(result, (short)abID.Length);
350
351            //Then copy the block of memory
352            Marshal.Copy(abID, 0, new IntPtr(result.ToInt64() + 2), abID.Length);
353            return result;
354        }
355
356        public static bool operator==(ShellItemID lhs, ShellItemID rhs)
357        {
358            return lhs.abID.SequenceEqual(rhs.abID);
359        }
360
361        public static bool operator!=(ShellItemID lhs, ShellItemID rhs)
362        {
363            return !lhs.abID.SequenceEqual(rhs.abID);
364        }
365
366        public override bool Equals(object obj)
367        {
368            if (obj is ShellItemID)
369                return this == (ShellItemID)obj;
370            return this.Equals(obj);
371        }
372
373        public override int GetHashCode()
374        {
375            return base.GetHashCode();
376        }
377    }
378}
Note: See TracBrowser for help on using the repository browser.