2 // Microsoft.Win32/IRegistryApi.cs
5 // Miguel de Icaza (miguel@gnome.org)
7 // (C) 2005 Novell, Inc (http://www.novell.com)
10 // Someone could the same subkey twice: once read/write once readonly,
11 // currently since we use a unique hash based on the file name, we are unable
12 // to have two versions of the same key and hence unable to throw an exception
13 // if the user tries to write to a read-only key.
15 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
17 // Permission is hereby granted, free of charge, to any person obtaining
18 // a copy of this software and associated documentation files (the
19 // "Software"), to deal in the Software without restriction, including
20 // without limitation the rights to use, copy, modify, merge, publish,
21 // distribute, sublicense, and/or sell copies of the Software, and to
22 // permit persons to whom the Software is furnished to do so, subject to
23 // the following conditions:
25 // The above copyright notice and this permission notice shall be
26 // included in all copies or substantial portions of the Software.
28 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
29 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
30 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
31 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
32 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
33 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
34 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 using System.Collections;
39 using System.Globalization;
42 using System.Runtime.InteropServices;
43 using System.Reflection;
44 using System.Security;
45 using System.Threading;
47 namespace Microsoft.Win32 {
50 static Hashtable key_to_handler = new Hashtable ();
51 static Hashtable dir_to_key = new Hashtable ();
55 public Hashtable values;
59 KeyHandler (RegistryKey rkey, string basedir)
61 if (!Directory.Exists (basedir)){
63 Directory.CreateDirectory (basedir);
64 } catch (Exception e){
65 Console.Error.WriteLine ("KeyHandler error while creating directory {0}:\n{1}", basedir, e);
69 file = Path.Combine (Dir, "values.xml");
75 values = new Hashtable ();
76 if (!File.Exists (file))
80 using (FileStream fs = File.OpenRead (file)){
81 StreamReader r = new StreamReader (fs);
82 string xml = r.ReadToEnd ();
86 SecurityElement tree = SecurityElement.FromString (xml);
87 if (tree.Tag == "values" && tree.Children != null){
88 foreach (SecurityElement value in tree.Children){
89 if (value.Tag == "value"){
95 } catch (Exception e){
96 Console.Error.WriteLine ("While loading registry key at {0}: {1}", file, e);
101 void LoadKey (SecurityElement se)
103 Hashtable h = se.Attributes;
105 string name = (string) h ["name"];
108 string type = (string) h ["type"];
114 values [name] = Int32.Parse (se.Text);
117 Convert.FromBase64String (se.Text);
120 values [name] = se.Text;
123 ArrayList sa = new ArrayList ();
124 if (se.Children != null){
125 foreach (SecurityElement stre in se.Children){
129 values [name] = sa.ToArray (typeof (string));
133 // We ignore individual errors in the file.
137 public RegistryKey Ensure (RegistryKey rkey, string extra)
139 lock (typeof (KeyHandler)){
140 string f = Path.Combine (Dir, extra);
141 if (dir_to_key.Contains (f))
142 return (RegistryKey) dir_to_key [f];
144 KeyHandler kh = new KeyHandler (rkey, f);
145 RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra));
146 key_to_handler [rk] = kh;
152 public RegistryKey Probe (RegistryKey rkey, string extra, bool write)
154 lock (typeof (KeyHandler)){
155 string f = Path.Combine (Dir, extra);
156 if (dir_to_key.Contains (f))
157 return (RegistryKey) dir_to_key [f];
158 if (Directory.Exists (f)){
159 KeyHandler kh = new KeyHandler (rkey, f);
160 RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra));
162 key_to_handler [rk] = kh;
169 static string CombineName (RegistryKey rkey, string extra)
171 if (extra.IndexOf ('/') != -1)
172 extra = extra.Replace ('/', '\\');
174 return String.Concat (rkey.Name, "\\", extra);
177 public static KeyHandler Lookup (RegistryKey rkey)
179 lock (typeof (KeyHandler)){
180 KeyHandler k = (KeyHandler) key_to_handler [rkey];
184 RegistryHive x = (RegistryHive) rkey.Data;
186 case RegistryHive.ClassesRoot:
187 case RegistryHive.CurrentConfig:
188 case RegistryHive.CurrentUser:
189 case RegistryHive.DynData:
190 case RegistryHive.LocalMachine:
191 case RegistryHive.PerformanceData:
192 case RegistryHive.Users:
193 string d = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal), ".mono/registry");
194 d = Path.Combine (d, x.ToString ());
196 k = new KeyHandler (rkey, d);
197 key_to_handler [rkey] = k;
200 throw new Exception ("Unknown RegistryHive");
202 key_to_handler [rkey] = k;
207 public static void Drop (RegistryKey rkey)
209 KeyHandler k = (KeyHandler) key_to_handler [rkey];
212 dir_to_key.Remove (k.Dir);
213 key_to_handler.Remove (rkey);
216 public static void Drop (string dir)
218 if (dir_to_key.Contains (dir)){
219 key_to_handler.Remove (dir_to_key [dir]);
220 dir_to_key.Remove (dir);
224 public void SetValue (string name, object value)
226 // immediately convert non-native registry values to string to avoid
227 // returning it unmodified in calls to UnixRegistryApi.GetValue
228 if (value is int || value is string || value is byte[] || value is string[])
229 values[name] = value;
231 values[name] = value.ToString ();
237 lock (typeof (KeyHandler)){
241 new Timer (DirtyTimeout, null, 3000, Timeout.Infinite);
245 public void DirtyTimeout (object state)
252 lock (typeof (KeyHandler)){
265 if (!File.Exists (file) && values.Count == 0)
268 SecurityElement se = new SecurityElement ("values");
270 foreach (DictionaryEntry de in values){
271 object val = de.Value;
272 SecurityElement value = new SecurityElement ("value");
273 value.AddAttribute ("name", (string) de.Key);
276 value.AddAttribute ("type", "string");
277 value.Text = (string) val;
278 } else if (val is int){
279 value.AddAttribute ("type", "int");
280 value.Text = val.ToString ();
281 } else if (val is byte []){
282 value.AddAttribute ("type", "bytearray");
283 value.Text = Convert.ToBase64String ((byte[]) val);
284 } else if (val is string []){
285 value.AddAttribute ("type", "string-array");
287 foreach (string ss in (string[]) val){
288 SecurityElement str = new SecurityElement ("string");
290 value.AddChild (str);
297 using (FileStream fs = File.Create (file)){
298 StreamWriter sw = new StreamWriter (fs);
300 sw.Write (se.ToString ());
303 } catch (Exception e){
304 Console.Error.WriteLine ("When saving {0} got {1}", file, e);
309 internal class UnixRegistryApi : IRegistryApi {
311 static string ToUnix (string keyname)
313 if (keyname.IndexOf ('\\') != -1)
314 keyname = keyname.Replace ('\\', '/');
315 return keyname.ToLower ();
318 static bool IsWellKnownKey (string parentKeyName, string keyname)
320 // FIXME: Add more keys if needed
321 if (parentKeyName == Registry.CurrentUser.Name ||
322 parentKeyName == Registry.LocalMachine.Name)
323 return (0 == String.Compare ("software", keyname, true, CultureInfo.InvariantCulture));
328 public RegistryKey CreateSubKey (RegistryKey rkey, string keyname)
330 KeyHandler self = KeyHandler.Lookup (rkey);
331 return self.Ensure (rkey, ToUnix (keyname));
334 public RegistryKey OpenSubKey (RegistryKey rkey, string keyname, bool writtable)
336 KeyHandler self = KeyHandler.Lookup (rkey);
337 RegistryKey result = self.Probe (rkey, ToUnix (keyname), writtable);
338 if (result == null && IsWellKnownKey (rkey.Name, keyname)) {
339 result = CreateSubKey (rkey, keyname);
345 public void Flush (RegistryKey rkey)
347 KeyHandler self = KeyHandler.Lookup (rkey);
351 public void Close (RegistryKey rkey)
353 KeyHandler.Drop (rkey);
356 public object GetValue (RegistryKey rkey, string name, bool return_default_value, object default_value)
358 KeyHandler self = KeyHandler.Lookup (rkey);
360 if (self.values.Contains (name))
361 return self.values [name];
362 if (return_default_value)
363 return default_value;
367 public void SetValue (RegistryKey rkey, string name, object value)
369 KeyHandler self = KeyHandler.Lookup (rkey);
370 self.SetValue (name, value);
373 public int SubKeyCount (RegistryKey rkey)
375 KeyHandler self = KeyHandler.Lookup (rkey);
377 return Directory.GetDirectories (self.Dir).Length;
380 public int ValueCount (RegistryKey rkey)
382 KeyHandler self = KeyHandler.Lookup (rkey);
384 return self.values.Keys.Count;
387 public void DeleteValue (RegistryKey rkey, string name, bool throw_if_missing)
389 KeyHandler self = KeyHandler.Lookup (rkey);
391 if (throw_if_missing && !self.values.Contains (name))
392 throw new ArgumentException ("the given value does not exist", "name");
394 self.values.Remove (name);
397 public void DeleteKey (RegistryKey rkey, string keyname, bool throw_if_missing)
399 KeyHandler self = KeyHandler.Lookup (rkey);
400 string dir = Path.Combine (self.Dir, keyname);
402 if (Directory.Exists (dir)){
403 Directory.Delete (dir, true);
404 KeyHandler.Drop (dir);
405 } else if (throw_if_missing)
406 throw new ArgumentException ("the given value does not exist", "value");
409 public string [] GetSubKeyNames (RegistryKey rkey)
411 KeyHandler self = KeyHandler.Lookup (rkey);
412 DirectoryInfo selfDir = new DirectoryInfo (self.Dir);
413 DirectoryInfo[] subDirs = selfDir.GetDirectories ();
414 string[] subKeyNames = new string[subDirs.Length];
415 for (int i = 0; i < subDirs.Length; i++) {
416 DirectoryInfo subDir = subDirs[i];
417 subKeyNames[i] = subDir.Name;
422 public string [] GetValueNames (RegistryKey rkey)
424 KeyHandler self = KeyHandler.Lookup (rkey);
425 ICollection keys = self.values.Keys;
427 string [] vals = new string [keys.Count];
428 keys.CopyTo (vals, 0);
432 public string ToString (RegistryKey rkey)