Welcome to mirror list, hosted at ThFree Co, Russian Federation.

DefineDosDevice.cs « IO « Library « Duplicati - github.com/duplicati/duplicati.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: fcd787c526268e67d007ba46688c1fd6b9691889 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
using System;
using System.Collections.Generic;
using System.Text;
using System.IO;

namespace Duplicati.Library.IO
{
    /// <summary>
    /// Implements a convenient wrapping for mapping a path to a drive letter
    /// </summary>
    public class DefineDosDevice : IDisposable
    {
        /// <summary>
        /// Encapsulation of Win32 calls
        /// </summary>
        private static class Win32API
        {
            /// <summary>
            /// Flags that can be used with DefineDosDevice
            /// </summary>
            [Flags]
            public enum DDD_Flags : uint
            {
                /// <summary>
                /// Uses the targetpath string as is. Otherwise, it is converted from an MS-DOS path to a path.
                /// </summary>
                DDD_RAW_TARGET_PATH = 0x1,
                /// <summary>
                /// Removes the specified definition for the specified device. To determine which definition to remove, the function walks the list of mappings for the device, looking for a match of targetpath against a prefix of each mapping associated with this device. The first mapping that matches is the one removed, and then the function returns.
                /// If targetpath is NULL or a pointer to a NULL string, the function will remove the first mapping associated with the device and pop the most recent one pushed. If there is nothing left to pop, the device name will be removed.
                /// If this value is not specified, the string pointed to by the targetpath parameter will become the new mapping for this device.
                /// </summary>
                DDD_REMOVE_DEFINITION = 0x2,
                /// <summary>
                /// If this value is specified along with DDD_REMOVE_DEFINITION, the function will use an exact match to determine which mapping to remove. Use this value to ensure that you do not delete something that you did not define.
                /// </summary>
                DDD_EXACT_MATCH_ON_REMOVE = 0x4,
                /// <summary>
                /// Do not broadcast the WM_SETTINGCHANGE message. By default, this message is broadcast to notify the shell and applications of the change.
                /// </summary>
                DDD_NO_BROADCAST_SYSTEM = 0x8
            }

            /// <summary>
            /// Defines, redefines, or deletes MS-DOS device names.
            /// </summary>
            /// <param name="flags">The controllable aspects of the DefineDosDevice function</param>
            /// <param name="devicename">A pointer to an MS-DOS device name string specifying the device the function is defining, redefining, or deleting. The device name string must not have a colon as the last character, unless a drive letter is being defined, redefined, or deleted. For example, drive C would be the string &quot;C:&quot;. In no case is a trailing backslash (&quot;\&quot;) allowed.</param>
            /// <param name="targetpath">A pointer to a path string that will implement this device. The string is an MS-DOS path string unless the DDD_RAW_TARGET_PATH flag is specified, in which case this string is a path string.</param>
            /// <returns>True on success, false otherwise</returns>
            [System.Runtime.InteropServices.DllImport("kernel32", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError  = true)]
            public static extern bool DefineDosDevice(DDD_Flags flags, string devicename, string targetpath);
        }

        /// <summary>
        /// The drive the path is mapped to
        /// </summary>
        private string m_drive;
        /// <summary>
        /// The path that is mapped to the drive
        /// </summary>
        private readonly string m_targetPath;
        /// <summary>
        /// A value indicating if the shell should be notified of changes
        /// </summary>
        private readonly bool m_shellBroadcast;

        /// <summary>
        /// Gets the drive that this mapping represents
        /// </summary>
        public string Drive { get { return m_drive; } }

        /// <summary>
        /// Gets the path that this mapping represents
        /// </summary>
        public string Targetpath { get { return m_targetPath; } }

        /// <summary>
        /// Creates a new mapping, using default settings
        /// </summary>
        /// <param name="path">The path to map</param>
        public DefineDosDevice(string path)
            : this(path, null, false)
        {
        }

        /// <summary>
        /// Creates a new mapping
        /// </summary>
        /// <param name="path">The path to map</param>
        /// <param name="drive">The drive to map to, use null to get a free drive letter</param>
        /// <param name="notifyShell">True to notify the shell of the change, false otherwise</param>
        public DefineDosDevice(string path, string drive, bool notifyShell)
        {
            if (string.IsNullOrEmpty(drive))
            {
                List<char> drives = new List<char>("DEFGHIJKLMNOPQRSTUVWXYZ".ToCharArray());
                foreach (DriveInfo di in DriveInfo.GetDrives())
                {
                    if ((di.RootDirectory.FullName.Length == 2 && di.RootDirectory.FullName[1] == ':') || ((di.RootDirectory.FullName.Length == 3 && di.RootDirectory.FullName.EndsWith(":\\", StringComparison.Ordinal))))
                    {
                        int i = drives.IndexOf(di.RootDirectory.FullName[0]);
                        if (i >= 0)
                            drives.RemoveAt(i);
                    }
                }

                if (drives.Count == 0)
                    throw new IOException("No drive letters available");
                drive = drives[0].ToString() + ':';
            }

            while (drive.EndsWith("\\", StringComparison.Ordinal))
                drive = drive.Substring(0, drive.Length - 1);

            if (!drive.EndsWith(":", StringComparison.Ordinal))
                throw new ArgumentException("The drive specification must end with a colon.", nameof(drive));

            Win32API.DDD_Flags flags = 0;
            if (!notifyShell)
                flags |= Win32API.DDD_Flags.DDD_NO_BROADCAST_SYSTEM;

            if (!Win32API.DefineDosDevice(flags, drive, path))
                throw new System.ComponentModel.Win32Exception();

            m_drive = drive;
            m_targetPath = path;
            m_shellBroadcast = notifyShell;
        }

        /// <summary>
        /// Disposes all resources held
        /// </summary>
        public void Dispose()
        {
            Dispose(true);
        }

        /// <summary>
        /// Disposes all resources
        /// </summary>
        /// <param name="disposing">True if called from Disposing, false otherwise</param>
        protected void Dispose(bool disposing)
        {
            if (m_drive != null)
            {
                Win32API.DDD_Flags flags = Win32API.DDD_Flags.DDD_REMOVE_DEFINITION | Win32API.DDD_Flags.DDD_EXACT_MATCH_ON_REMOVE;
                if (m_shellBroadcast)
                    flags |= Win32API.DDD_Flags.DDD_NO_BROADCAST_SYSTEM;
                Win32API.DefineDosDevice(flags, m_drive, m_targetPath);
                m_drive = null;
            }

            if (disposing)
                GC.SuppressFinalize(this);
        }

        /// <summary>
        /// Destroys the object and releases all held resources
        /// </summary>
        ~DefineDosDevice()
        {
            Dispose(false);
        }

    }
}