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 {
52 public ExpandString (string s)
57 public override string ToString ()
62 public string Expand ()
64 StringBuilder sb = new StringBuilder ();
66 for (int i = 0; i < value.Length; i++){
67 if (value [i] == '%'){
69 for (; j < value.Length; j++){
70 if (value [j] == '%'){
71 string key = value.Substring (i + 1, j - i - 1);
73 sb.Append (Environment.GetEnvironmentVariable (key));
78 if (j == value.Length){
82 sb.Append (value [i]);
85 return sb.ToString ();
89 static Hashtable key_to_handler = new Hashtable ();
90 static Hashtable dir_to_key = new Hashtable ();
94 public Hashtable values;
99 KeyHandler (RegistryKey rkey, string basedir)
101 if (!Directory.Exists (basedir)){
103 Directory.CreateDirectory (basedir);
104 } catch (Exception e){
105 Console.Error.WriteLine ("KeyHandler error while creating directory {0}:\n{1}", basedir, e);
109 file = Path.Combine (Dir, "values.xml");
115 values = new Hashtable ();
116 if (!File.Exists (file))
120 using (FileStream fs = File.OpenRead (file)){
121 StreamReader r = new StreamReader (fs);
122 string xml = r.ReadToEnd ();
126 SecurityElement tree = SecurityElement.FromString (xml);
127 if (tree.Tag == "values" && tree.Children != null){
128 foreach (SecurityElement value in tree.Children){
129 if (value.Tag == "value"){
135 } catch (Exception e){
136 Console.Error.WriteLine ("While loading registry key at {0}: {1}", file, e);
141 void LoadKey (SecurityElement se)
143 Hashtable h = se.Attributes;
145 string name = (string) h ["name"];
148 string type = (string) h ["type"];
154 values [name] = Int32.Parse (se.Text);
157 Convert.FromBase64String (se.Text);
160 values [name] = se.Text;
163 values [name] = new ExpandString (se.Text);
166 values [name] = Int64.Parse (se.Text);
169 ArrayList sa = new ArrayList ();
170 if (se.Children != null){
171 foreach (SecurityElement stre in se.Children){
175 values [name] = sa.ToArray (typeof (string));
179 // We ignore individual errors in the file.
183 public RegistryKey Ensure (RegistryKey rkey, string extra)
185 lock (typeof (KeyHandler)){
186 string f = Path.Combine (Dir, extra);
187 if (dir_to_key.Contains (f))
188 return (RegistryKey) dir_to_key [f];
190 KeyHandler kh = new KeyHandler (rkey, f);
191 RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra));
192 key_to_handler [rk] = kh;
198 public RegistryKey Probe (RegistryKey rkey, string extra, bool write)
200 lock (typeof (KeyHandler)){
201 string f = Path.Combine (Dir, extra);
202 if (dir_to_key.Contains (f))
203 return (RegistryKey) dir_to_key [f];
204 if (Directory.Exists (f)){
205 KeyHandler kh = new KeyHandler (rkey, f);
206 RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra));
208 key_to_handler [rk] = kh;
215 static string CombineName (RegistryKey rkey, string extra)
217 if (extra.IndexOf ('/') != -1)
218 extra = extra.Replace ('/', '\\');
220 return String.Concat (rkey.Name, "\\", extra);
223 public static KeyHandler Lookup (RegistryKey rkey)
225 lock (typeof (KeyHandler)){
226 KeyHandler k = (KeyHandler) key_to_handler [rkey];
230 RegistryHive x = (RegistryHive) rkey.Data;
232 case RegistryHive.ClassesRoot:
233 case RegistryHive.CurrentConfig:
234 case RegistryHive.CurrentUser:
235 case RegistryHive.DynData:
236 case RegistryHive.LocalMachine:
237 case RegistryHive.PerformanceData:
238 case RegistryHive.Users:
239 string d = Path.Combine (RegistryStore, x.ToString ());
240 k = new KeyHandler (rkey, d);
243 throw new Exception ("Unknown RegistryHive");
245 key_to_handler [rkey] = k;
250 public static void Drop (RegistryKey rkey)
252 KeyHandler k = (KeyHandler) key_to_handler [rkey];
256 dir_to_key.Remove (k.Dir);
257 key_to_handler.Remove (rkey);
260 public static void Drop (string dir)
262 if (dir_to_key.Contains (dir)){
263 RegistryKey rkey = (RegistryKey) dir_to_key [dir];
268 public void SetValue (string name, object value)
270 // immediately convert non-native registry values to string to avoid
271 // returning it unmodified in calls to UnixRegistryApi.GetValue
272 if (value is int || value is string || value is byte[] || value is string[])
273 values[name] = value;
275 values[name] = value.ToString ();
281 // This version has to do argument validation based on the valueKind
283 public void SetValue (string name, object value, RegistryValueKind valueKind)
287 case RegistryValueKind.String:
288 if (value is string){
289 values [name] = value;
293 case RegistryValueKind.ExpandString:
294 if (value is string){
295 Console.WriteLine ("SETTING THIS BAD BOY {0} to {1}", name, "Exp");
296 values [name] = new ExpandString ((string)value);
301 case RegistryValueKind.Binary:
302 if (value is byte []){
303 values [name] = value;
308 case RegistryValueKind.DWord:
310 (((long) value) < Int32.MaxValue) &&
311 (((long) value) > Int32.MinValue)){
312 values [name] = (int) ((long)value);
316 values [name] = value;
321 case RegistryValueKind.MultiString:
322 if (value is string []){
323 values [name] = value;
328 case RegistryValueKind.QWord:
330 values [name] = (long) ((int) value);
334 values [name] = value;
339 throw new ArgumentException ("unknown value", "valueKind");
341 throw new ArgumentException ("Value could not be converted to specified type", "valueKind");
347 lock (typeof (KeyHandler)){
351 new Timer (DirtyTimeout, null, 3000, Timeout.Infinite);
355 public void DirtyTimeout (object state)
362 lock (typeof (KeyHandler)){
378 if (!File.Exists (file) && values.Count == 0)
381 SecurityElement se = new SecurityElement ("values");
383 foreach (DictionaryEntry de in values){
384 object val = de.Value;
385 SecurityElement value = new SecurityElement ("value");
386 value.AddAttribute ("name", (string) de.Key);
389 value.AddAttribute ("type", "string");
390 value.Text = (string) val;
391 } else if (val is int){
392 value.AddAttribute ("type", "int");
393 value.Text = val.ToString ();
394 } else if (val is long){
395 value.AddAttribute ("type", "qword");
396 value.Text = val.ToString ();
397 } else if (val is byte []){
398 value.AddAttribute ("type", "bytearray");
399 value.Text = Convert.ToBase64String ((byte[]) val);
400 } else if (val is ExpandString){
401 value.AddAttribute ("type", "expand");
402 value.Text = val.ToString ();
403 } else if (val is string []){
404 value.AddAttribute ("type", "string-array");
406 foreach (string ss in (string[]) val){
407 SecurityElement str = new SecurityElement ("string");
409 value.AddChild (str);
416 using (FileStream fs = File.Create (file)){
417 StreamWriter sw = new StreamWriter (fs);
419 sw.Write (se.ToString ());
422 } catch (Exception e){
423 Console.Error.WriteLine ("When saving {0} got {1}", file, e);
427 public static string RegistryStore {
429 return Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal),
436 internal class UnixRegistryApi : IRegistryApi {
438 static string ToUnix (string keyname)
440 if (keyname.IndexOf ('\\') != -1)
441 keyname = keyname.Replace ('\\', '/');
442 return keyname.ToLower ();
445 static bool IsWellKnownKey (string parentKeyName, string keyname)
447 if (string.Compare ("software", keyname, true, CultureInfo.InvariantCulture) == 0)
448 return (parentKeyName == Registry.CurrentUser.Name ||
449 parentKeyName == Registry.LocalMachine.Name);
451 // required for event log support
452 if (string.Compare (@"SYSTEM\CurrentControlSet\Services\EventLog", keyname, true, CultureInfo.InvariantCulture) == 0)
453 return (parentKeyName == Registry.LocalMachine.Name);
458 public RegistryKey CreateSubKey (RegistryKey rkey, string keyname)
460 KeyHandler self = KeyHandler.Lookup (rkey);
461 return self.Ensure (rkey, ToUnix (keyname));
464 public RegistryKey OpenSubKey (RegistryKey rkey, string keyname, bool writtable)
466 KeyHandler self = KeyHandler.Lookup (rkey);
467 RegistryKey result = self.Probe (rkey, ToUnix (keyname), writtable);
468 if (result == null && IsWellKnownKey (rkey.Name, keyname)) {
469 result = CreateSubKey (rkey, keyname);
475 public void Flush (RegistryKey rkey)
477 KeyHandler self = KeyHandler.Lookup (rkey);
481 public void Close (RegistryKey rkey)
483 KeyHandler.Drop (rkey);
486 public object GetValue (RegistryKey rkey, string name, bool return_default_value, object default_value)
488 KeyHandler self = KeyHandler.Lookup (rkey);
490 if (self.values.Contains (name)){
491 object r = self.values [name];
493 if (r is ExpandString){
494 return ((ExpandString)r).Expand ();
499 if (return_default_value)
500 return default_value;
504 public void SetValue (RegistryKey rkey, string name, object value)
506 KeyHandler self = KeyHandler.Lookup (rkey);
507 self.SetValue (name, value);
511 public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
513 KeyHandler self = KeyHandler.Lookup (rkey);
514 self.SetValue (name, value, valueKind);
518 public int SubKeyCount (RegistryKey rkey)
520 KeyHandler self = KeyHandler.Lookup (rkey);
522 return Directory.GetDirectories (self.Dir).Length;
525 public int ValueCount (RegistryKey rkey)
527 KeyHandler self = KeyHandler.Lookup (rkey);
529 return self.values.Keys.Count;
532 public void DeleteValue (RegistryKey rkey, string name, bool throw_if_missing)
534 KeyHandler self = KeyHandler.Lookup (rkey);
536 if (throw_if_missing && !self.values.Contains (name))
537 throw new ArgumentException ("the given value does not exist", "name");
539 self.values.Remove (name);
542 public void DeleteKey (RegistryKey rkey, string keyname, bool throw_if_missing)
544 KeyHandler self = KeyHandler.Lookup (rkey);
545 string dir = Path.Combine (self.Dir, ToUnix (keyname));
547 if (Directory.Exists (dir)){
548 Directory.Delete (dir, true);
549 KeyHandler.Drop (dir);
550 } else if (throw_if_missing)
551 throw new ArgumentException ("the given value does not exist", "value");
554 public string [] GetSubKeyNames (RegistryKey rkey)
556 KeyHandler self = KeyHandler.Lookup (rkey);
557 DirectoryInfo selfDir = new DirectoryInfo (self.Dir);
558 DirectoryInfo[] subDirs = selfDir.GetDirectories ();
559 string[] subKeyNames = new string[subDirs.Length];
560 for (int i = 0; i < subDirs.Length; i++) {
561 DirectoryInfo subDir = subDirs[i];
562 subKeyNames[i] = subDir.Name;
567 public string [] GetValueNames (RegistryKey rkey)
569 KeyHandler self = KeyHandler.Lookup (rkey);
570 ICollection keys = self.values.Keys;
572 string [] vals = new string [keys.Count];
573 keys.CopyTo (vals, 0);
577 public string ToString (RegistryKey rkey)