Fix a few cases of mixed line-endings
[mono.git] / mcs / class / corlib / Microsoft.Win32 / UnixRegistryApi.cs
1 //
2 // Microsoft.Win32/UnixRegistryApi.cs
3 //
4 // Authors:
5 //      Miguel de Icaza (miguel@gnome.org)
6 //      Gert Driesen (drieseng@users.sourceforge.net)
7 //
8 // (C) 2005, 2006 Novell, Inc (http://www.novell.com)
9 // 
10 // MISSING:
11 //   It would be useful if we do case-insensitive expansion of variables,
12 //   the registry is very windows specific, so we probably should default to
13 //   those semantics in expanding environment variables, for example %path%
14 //
15 //   We should use an ordered collection for storing the values (instead of
16 //   a Hashtable).
17 //
18 // Copyright (C) 2004 Novell, Inc (http://www.novell.com)
19 //
20 // Permission is hereby granted, free of charge, to any person obtaining
21 // a copy of this software and associated documentation files (the
22 // "Software"), to deal in the Software without restriction, including
23 // without limitation the rights to use, copy, modify, merge, publish,
24 // distribute, sublicense, and/or sell copies of the Software, and to
25 // permit persons to whom the Software is furnished to do so, subject to
26 // the following conditions:
27 // 
28 // The above copyright notice and this permission notice shall be
29 // included in all copies or substantial portions of the Software.
30 // 
31 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
32 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
33 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
34 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
35 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
36 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
37 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
38 //
39
40 #if !NET_2_1
41
42 using System;
43 using System.Collections;
44 using System.Globalization;
45 using System.IO;
46 using System.Text;
47 using System.Runtime.InteropServices;
48 using System.Reflection;
49 using System.Security;
50 using System.Threading;
51
52 namespace Microsoft.Win32 {
53
54         class ExpandString {
55                 string value;
56                 
57                 public ExpandString (string s)
58                 {
59                         value = s;
60                 }
61
62                 public override string ToString ()
63                 {
64                         return value;
65                 }
66
67                 public string Expand ()
68                 {
69                         StringBuilder sb = new StringBuilder ();
70
71                         for (int i = 0; i < value.Length; i++){
72                                 if (value [i] == '%'){
73                                         int j = i + 1;
74                                         for (; j < value.Length; j++){
75                                                 if (value [j] == '%'){
76                                                         string key = value.Substring (i + 1, j - i - 1);
77
78                                                         sb.Append (Environment.GetEnvironmentVariable (key));
79                                                         i += j;
80                                                         break;
81                                                 }
82                                         }
83                                         if (j == value.Length){
84                                                 sb.Append ('%');
85                                         }
86                                 } else {
87                                         sb.Append (value [i]);
88                                 }
89                         }
90                         return sb.ToString ();
91                 }
92         }
93
94         class KeyHandler
95         {
96                 static Hashtable key_to_handler = new Hashtable ();
97                 static Hashtable dir_to_handler = new Hashtable (
98                         new CaseInsensitiveHashCodeProvider (), new CaseInsensitiveComparer ());
99                 const string VolatileDirectoryName = "volatile-keys";
100
101                 public string Dir;
102                 string ActualDir; // Lets keep this one private.
103                 public bool IsVolatile;
104
105                 Hashtable values;
106                 string file;
107                 bool dirty;
108
109                 KeyHandler (RegistryKey rkey, string basedir) : this (rkey, basedir, false)
110                 {
111                 }
112
113                 KeyHandler (RegistryKey rkey, string basedir, bool is_volatile)
114                 {
115                         // Force ourselved to reuse the key, if any.
116                         string volatile_basedir = GetVolatileDir (basedir);
117                         string actual_basedir = basedir;
118
119                         if (Directory.Exists (basedir))
120                                 is_volatile = false;
121                         else if (Directory.Exists (volatile_basedir)) {
122                                 actual_basedir = volatile_basedir;
123                                 is_volatile = true;
124                         } else if (is_volatile)
125                                 actual_basedir = volatile_basedir;
126
127                         if (!Directory.Exists (actual_basedir)) {
128                                 try {
129                                         Directory.CreateDirectory (actual_basedir);
130                                 } catch (UnauthorizedAccessException){
131                                         throw new SecurityException ("No access to the given key");
132                                 }
133                         }
134                         Dir = basedir; // This is our identifier.
135                         ActualDir = actual_basedir; // This our actual location.
136                         IsVolatile = is_volatile;
137                         file = Path.Combine (ActualDir, "values.xml");
138                         Load ();
139                 }
140
141                 public void Load ()
142                 {
143                         values = new Hashtable ();
144                         if (!File.Exists (file))
145                                 return;
146                         
147                         try {
148                                 using (FileStream fs = File.OpenRead (file)){
149                                         StreamReader r = new StreamReader (fs);
150                                         string xml = r.ReadToEnd ();
151                                         if (xml.Length == 0)
152                                                 return;
153                                         
154                                         SecurityElement tree = SecurityElement.FromString (xml);
155                                         if (tree.Tag == "values" && tree.Children != null){
156                                                 foreach (SecurityElement value in tree.Children){
157                                                         if (value.Tag == "value"){
158                                                                 LoadKey (value);
159                                                         }
160                                                 }
161                                         }
162                                 }
163                         } catch (UnauthorizedAccessException){
164                                 values.Clear ();
165                                 throw new SecurityException ("No access to the given key");
166                         } catch (Exception e){
167                                 Console.Error.WriteLine ("While loading registry key at {0}: {1}", file, e);
168                                 values.Clear ();
169                         }
170                 }
171
172                 void LoadKey (SecurityElement se)
173                 {
174                         Hashtable h = se.Attributes;
175                         try {
176                                 string name = (string) h ["name"];
177                                 if (name == null)
178                                         return;
179                                 string type = (string) h ["type"];
180                                 if (type == null)
181                                         return;
182                                 
183                                 switch (type){
184                                 case "int":
185                                         values [name] = Int32.Parse (se.Text);
186                                         break;
187                                 case "bytearray":
188                                         values [name] = Convert.FromBase64String (se.Text);
189                                         break;
190                                 case "string":
191                                         values [name] = se.Text == null ? String.Empty : se.Text;
192                                         break;
193                                 case "expand":
194                                         values [name] = new ExpandString (se.Text);
195                                         break;
196                                 case "qword":
197                                         values [name] = Int64.Parse (se.Text);
198                                         break;
199                                 case "string-array":
200                                         ArrayList sa = new ArrayList ();
201                                         if (se.Children != null){
202                                                 foreach (SecurityElement stre in se.Children){
203                                                         sa.Add (stre.Text);
204                                                 }
205                                         }
206                                         values [name] = sa.ToArray (typeof (string));
207                                         break;
208                                 }
209                         } catch {
210                                 // We ignore individual errors in the file.
211                         }
212                 }
213
214                 public RegistryKey Ensure (RegistryKey rkey, string extra, bool writable)
215                 {
216                         return Ensure (rkey, extra, writable, false);
217                 }
218
219                 // 'is_volatile' is used only if the key hasn't been created already.
220                 public RegistryKey Ensure (RegistryKey rkey, string extra, bool writable, bool is_volatile)
221                 {
222                         lock (typeof (KeyHandler)){
223                                 string f = Path.Combine (Dir, extra);
224                                 KeyHandler kh = (KeyHandler) dir_to_handler [f];
225                                 if (kh == null)
226                                         kh = new KeyHandler (rkey, f, is_volatile);
227                                 RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra), writable);
228                                 key_to_handler [rk] = kh;
229                                 dir_to_handler [f] = kh;
230                                 return rk;
231                         }
232                 }
233
234                 public RegistryKey Probe (RegistryKey rkey, string extra, bool writable)
235                 {
236                         RegistryKey rk = null;
237
238                         lock (typeof (KeyHandler)){
239                                 string f = Path.Combine (Dir, extra);
240                                 KeyHandler kh = (KeyHandler) dir_to_handler [f];
241                                 if (kh != null) {
242                                         rk = new RegistryKey (kh, CombineName (rkey,
243                                                 extra), writable);
244                                         key_to_handler [rk] = kh;
245                                 } else if (Directory.Exists (f) || VolatileKeyExists (f)) {
246                                         kh = new KeyHandler (rkey, f);
247                                         rk = new RegistryKey (kh, CombineName (rkey, extra),
248                                                 writable);
249                                         dir_to_handler [f] = kh;
250                                         key_to_handler [rk] = kh;
251                                 }
252                                 return rk;
253                         }
254                 }
255
256                 static string CombineName (RegistryKey rkey, string extra)
257                 {
258                         if (extra.IndexOf ('/') != -1)
259                                 extra = extra.Replace ('/', '\\');
260                         
261                         return String.Concat (rkey.Name, "\\", extra);
262                 }
263                                 
264                 public static bool VolatileKeyExists (string dir)
265                 {
266                         lock (typeof (KeyHandler)) {
267                                 KeyHandler kh = (KeyHandler) dir_to_handler [dir];
268                                 if (kh != null)
269                                         return kh.IsVolatile;
270                         }
271
272                         if (Directory.Exists (dir)) // Non-volatile key exists.
273                                 return false;
274
275                         return Directory.Exists (GetVolatileDir (dir));
276                 }
277
278                 public static string GetVolatileDir (string dir)
279                 {
280                         string root = GetRootFromDir (dir);
281                         string volatile_dir = dir.Replace (root, Path.Combine (root, VolatileDirectoryName));
282                         return volatile_dir;
283                 }
284
285                 public static KeyHandler Lookup (RegistryKey rkey, bool createNonExisting)
286                 {
287                         lock (typeof (KeyHandler)){
288                                 KeyHandler k = (KeyHandler) key_to_handler [rkey];
289                                 if (k != null)
290                                         return k;
291
292                                 // when a non-root key is requested for no keyhandler exist
293                                 // then that key must have been marked for deletion
294                                 if (!rkey.IsRoot || !createNonExisting)
295                                         return null;
296
297                                 RegistryHive x = (RegistryHive) rkey.Hive;
298                                 switch (x){
299                                 case RegistryHive.CurrentUser:
300                                         string userDir = Path.Combine (UserStore, x.ToString ());
301                                         k = new KeyHandler (rkey, userDir);
302                                         dir_to_handler [userDir] = k;
303                                         break;
304                                 case RegistryHive.CurrentConfig:
305                                 case RegistryHive.ClassesRoot:
306                                 case RegistryHive.DynData:
307                                 case RegistryHive.LocalMachine:
308                                 case RegistryHive.PerformanceData:
309                                 case RegistryHive.Users:
310                                         string machine_dir = Path.Combine (MachineStore, x.ToString ());
311                                         k = new KeyHandler (rkey, machine_dir);
312                                         dir_to_handler [machine_dir] = k;
313                                         break;
314                                 default:
315                                         throw new Exception ("Unknown RegistryHive");
316                                 }
317                                 key_to_handler [rkey] = k;
318                                 return k;
319                         }
320                 }
321
322                 static string GetRootFromDir (string dir)
323                 {
324                         if (dir.IndexOf (UserStore) > -1)
325                                 return UserStore;
326                         else if (dir.IndexOf (MachineStore) > -1)
327                                 return MachineStore;
328
329                         throw new Exception ("Could not get root for dir " + dir);
330                 }
331
332                 public static void Drop (RegistryKey rkey)
333                 {
334                         lock (typeof (KeyHandler)) {
335                                 KeyHandler k = (KeyHandler) key_to_handler [rkey];
336                                 if (k == null)
337                                         return;
338                                 key_to_handler.Remove (rkey);
339
340                                 // remove cached KeyHandler if no other keys reference it
341                                 int refCount = 0;
342                                 foreach (DictionaryEntry de in key_to_handler)
343                                         if (de.Value == k)
344                                                 refCount++;
345                                 if (refCount == 0)
346                                         dir_to_handler.Remove (k.Dir);
347                         }
348                 }
349
350                 public static void Drop (string dir)
351                 {
352                         lock (typeof (KeyHandler)) {
353                                 KeyHandler kh = (KeyHandler) dir_to_handler [dir];
354                                 if (kh == null)
355                                         return;
356
357                                 dir_to_handler.Remove (dir);
358
359                                 // remove (other) references to keyhandler
360                                 ArrayList keys = new ArrayList ();
361                                 foreach (DictionaryEntry de in key_to_handler)
362                                         if (de.Value == kh)
363                                                 keys.Add (de.Key);
364
365                                 foreach (object key in keys)
366                                         key_to_handler.Remove (key);
367                         }
368                 }
369
370                 public static bool Delete (string dir)
371                 {
372                         if (!Directory.Exists (dir)) {
373                                 string volatile_dir = GetVolatileDir (dir);
374                                 if (!Directory.Exists (volatile_dir))
375                                         return false;
376
377                                 dir = volatile_dir;
378                         }
379
380                         Directory.Delete (dir, true);
381                         Drop (dir);
382                         return true;
383                 }
384
385                 public RegistryValueKind GetValueKind (string name)
386                 {
387                         if (name == null)
388                                 return RegistryValueKind.Unknown;
389                         object value = values [name];
390                         if (value == null)
391                                 return RegistryValueKind.Unknown;
392
393                         if (value is int)
394                                 return RegistryValueKind.DWord;
395                         if (value is string [])
396                                 return RegistryValueKind.MultiString;
397                         if (value is long)
398                                 return RegistryValueKind.QWord;
399                         if (value is byte [])
400                                 return RegistryValueKind.Binary;
401                         if (value is string)
402                                 return RegistryValueKind.String;
403                         if (value is ExpandString)
404                                 return RegistryValueKind.ExpandString;
405                         return RegistryValueKind.Unknown;
406                 }
407                 
408                 public object GetValue (string name, RegistryValueOptions options)
409                 {
410                         if (IsMarkedForDeletion)
411                                 return null;
412
413                         if (name == null)
414                                 name = string.Empty;
415                         object value = values [name];
416                         ExpandString exp = value as ExpandString;
417                         if (exp == null)
418                                 return value;
419                         if ((options & RegistryValueOptions.DoNotExpandEnvironmentNames) == 0)
420                                 return exp.Expand ();
421
422                         return exp.ToString ();
423                 }
424
425                 public void SetValue (string name, object value)
426                 {
427                         AssertNotMarkedForDeletion ();
428
429                         if (name == null)
430                                 name = string.Empty;
431
432                         // immediately convert non-native registry values to string to avoid
433                         // returning it unmodified in calls to UnixRegistryApi.GetValue
434                         if (value is int || value is string || value is byte[] || value is string[])
435                                 values[name] = value;
436                         else
437                                 values[name] = value.ToString ();
438                         SetDirty ();
439                 }
440
441                 public string [] GetValueNames ()
442                 {
443                         AssertNotMarkedForDeletion ();
444
445                         ICollection keys = values.Keys;
446
447                         string [] vals = new string [keys.Count];
448                         keys.CopyTo (vals, 0);
449                         return vals;
450                 }
451
452                 //
453                 // This version has to do argument validation based on the valueKind
454                 //
455                 public void SetValue (string name, object value, RegistryValueKind valueKind)
456                 {
457                         SetDirty ();
458
459                         if (name == null)
460                                 name = string.Empty;
461
462                         switch (valueKind){
463                         case RegistryValueKind.String:
464                                 if (value is string){
465                                         values [name] = value;
466                                         return;
467                                 }
468                                 break;
469                         case RegistryValueKind.ExpandString:
470                                 if (value is string){
471                                         values [name] = new ExpandString ((string)value);
472                                         return;
473                                 }
474                                 break;
475                                 
476                         case RegistryValueKind.Binary:
477                                 if (value is byte []){
478                                         values [name] = value;
479                                         return;
480                                 }
481                                 break;
482                                 
483                         case RegistryValueKind.DWord:
484                                 if (value is long &&
485                                     (((long) value) < Int32.MaxValue) &&
486                                     (((long) value) > Int32.MinValue)){
487                                         values [name] = (int) ((long)value);
488                                         return;
489                                 }
490                                 if (value is int){
491                                         values [name] = value;
492                                         return;
493                                 }
494                                 break;
495                                 
496                         case RegistryValueKind.MultiString:
497                                 if (value is string []){
498                                         values [name] = value;
499                                         return;
500                                 }
501                                 break;
502                                 
503                         case RegistryValueKind.QWord:
504                                 if (value is int){
505                                         values [name] = (long) ((int) value);
506                                         return;
507                                 }
508                                 if (value is long){
509                                         values [name] = value;
510                                         return;
511                                 }
512                                 break;
513                         default:
514                                 throw new ArgumentException ("unknown value", "valueKind");
515                         }
516                         throw new ArgumentException ("Value could not be converted to specified type", "valueKind");
517                 }
518
519                 void SetDirty ()
520                 {
521                         lock (typeof (KeyHandler)){
522                                 if (dirty)
523                                         return;
524                                 dirty = true;
525                                 new Timer (DirtyTimeout, null, 3000, Timeout.Infinite);
526                         }
527                 }
528
529                 public void DirtyTimeout (object state)
530                 {
531                         Flush ();
532                 }
533
534                 public void Flush ()
535                 {
536                         lock (typeof (KeyHandler)) {
537                                 if (dirty) {
538                                         Save ();
539                                         dirty = false;
540                                 }
541                         }
542                 }
543
544                 public bool ValueExists (string name)
545                 {
546                         if (name == null)
547                                 name = string.Empty;
548
549                         return values.Contains (name);
550                 }
551
552                 public int ValueCount {
553                         get {
554                                 return values.Keys.Count;
555                         }
556                 }
557
558                 public bool IsMarkedForDeletion {
559                         get {
560                                 return !dir_to_handler.Contains (Dir);
561                         }
562                 }
563
564                 public void RemoveValue (string name)
565                 {
566                         AssertNotMarkedForDeletion ();
567
568                         values.Remove (name);
569                         SetDirty ();
570                 }
571
572                 ~KeyHandler ()
573                 {
574                         Flush ();
575                 }
576                 
577                 void Save ()
578                 {
579                         if (IsMarkedForDeletion)
580                                 return;
581
582                         if (!File.Exists (file) && values.Count == 0)
583                                 return;
584
585                         SecurityElement se = new SecurityElement ("values");
586                         
587                         // With SecurityElement.Text = value, and SecurityElement.AddAttribute(key, value)
588                         // the values must be escaped prior to being assigned. 
589                         foreach (DictionaryEntry de in values){
590                                 object val = de.Value;
591                                 SecurityElement value = new SecurityElement ("value");
592                                 value.AddAttribute ("name", SecurityElement.Escape ((string) de.Key));
593                                 
594                                 if (val is string){
595                                         value.AddAttribute ("type", "string");
596                                         value.Text = SecurityElement.Escape ((string) val);
597                                 } else if (val is int){
598                                         value.AddAttribute ("type", "int");
599                                         value.Text = val.ToString ();
600                                 } else if (val is long) {
601                                         value.AddAttribute ("type", "qword");
602                                         value.Text = val.ToString ();
603                                 } else if (val is byte []){
604                                         value.AddAttribute ("type", "bytearray");
605                                         value.Text = Convert.ToBase64String ((byte[]) val);
606                                 } else if (val is ExpandString){
607                                         value.AddAttribute ("type", "expand");
608                                         value.Text = SecurityElement.Escape (val.ToString ());
609                                 } else if (val is string []){
610                                         value.AddAttribute ("type", "string-array");
611
612                                         foreach (string ss in (string[]) val){
613                                                 SecurityElement str = new SecurityElement ("string");
614                                                 str.Text = SecurityElement.Escape (ss); 
615                                                 value.AddChild (str);
616                                         }
617                                 }
618                                 se.AddChild (value);
619                         }
620
621                         using (FileStream fs = File.Create (file)){
622                                 StreamWriter sw = new StreamWriter (fs);
623
624                                 sw.Write (se.ToString ());
625                                 sw.Flush ();
626                         }
627                 }
628
629                 private void AssertNotMarkedForDeletion ()
630                 {
631                         if (IsMarkedForDeletion)
632                                 throw RegistryKey.CreateMarkedForDeletionException ();
633                 }
634
635                 static string user_store;
636                 static string machine_store;
637
638                 private static string UserStore {
639                         get {
640                                 if (user_store == null)
641                                         user_store = Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal),
642                                         ".mono/registry");
643
644                                 return user_store;
645                         }
646                 }
647
648                 private static string MachineStore {
649                         get {
650                                 if (machine_store == null) {
651                                         machine_store = Environment.GetEnvironmentVariable ("MONO_REGISTRY_PATH");
652                                         if (machine_store == null) {
653                                                 string s = Environment.GetMachineConfigPath ();
654                                                 int p = s.IndexOf ("machine.config");
655                                                 machine_store = Path.Combine (Path.Combine (s.Substring (0, p-1), ".."), "registry");
656                                         }
657                                 }
658
659                                 return machine_store;
660                         }
661                 }
662         }
663         
664         internal class UnixRegistryApi : IRegistryApi {
665
666                 static string ToUnix (string keyname)
667                 {
668                         if (keyname.IndexOf ('\\') != -1)
669                                 keyname = keyname.Replace ('\\', '/');
670                         return keyname.ToLower ();
671                 }
672
673                 static bool IsWellKnownKey (string parentKeyName, string keyname)
674                 {
675                         // FIXME: Add more keys if needed
676                         if (parentKeyName == Registry.CurrentUser.Name ||
677                                 parentKeyName == Registry.LocalMachine.Name)
678                                 return (0 == String.Compare ("software", keyname, true, CultureInfo.InvariantCulture));
679
680                         return false;
681                 }
682
683                 public RegistryKey CreateSubKey (RegistryKey rkey, string keyname)
684                 {
685                         return CreateSubKey (rkey, keyname, true);
686                 }
687
688 #if NET_4_0
689                 public RegistryKey CreateSubKey (RegistryKey rkey, string keyname, RegistryOptions options)
690                 {
691                         return CreateSubKey (rkey, keyname, true, options == RegistryOptions.Volatile);
692                 }
693 #endif
694
695                 public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
696                 {
697                         throw new NotImplementedException ();
698                 }
699
700                 public RegistryKey OpenSubKey (RegistryKey rkey, string keyname, bool writable)
701                 {
702                         KeyHandler self = KeyHandler.Lookup (rkey, true);
703                         if (self == null) {
704                                 // return null if parent is marked for deletion
705                                 return null;
706                         }
707
708                         RegistryKey result = self.Probe (rkey, ToUnix (keyname), writable);
709                         if (result == null && IsWellKnownKey (rkey.Name, keyname)) {
710                                 // create the subkey even if its parent was opened read-only
711                                 result = CreateSubKey (rkey, keyname, writable);
712                         }
713
714                         return result;
715                 }
716                 
717                 public void Flush (RegistryKey rkey)
718                 {
719                         KeyHandler self = KeyHandler.Lookup (rkey, false);
720                         if (self == null) {
721                                 // we do not need to flush changes as key is marked for deletion
722                                 return;
723                         }
724                         self.Flush ();
725                 }
726                 
727                 public void Close (RegistryKey rkey)
728                 {
729                         KeyHandler.Drop (rkey);
730                 }
731
732                 public object GetValue (RegistryKey rkey, string name, object default_value, RegistryValueOptions options)
733                 {
734                         KeyHandler self = KeyHandler.Lookup (rkey, true);
735                         if (self == null) {
736                                 // key was removed since it was opened
737                                 return default_value;
738                         }
739
740                         if (self.ValueExists (name))
741                                 return self.GetValue (name, options);
742                         return default_value;
743                 }
744                 
745                 public void SetValue (RegistryKey rkey, string name, object value)
746                 {
747                         KeyHandler self = KeyHandler.Lookup (rkey, true);
748                         if (self == null)
749                                 throw RegistryKey.CreateMarkedForDeletionException ();
750                         self.SetValue (name, value);
751                 }
752
753                 public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
754                 {
755                         KeyHandler self = KeyHandler.Lookup (rkey, true);
756                         if (self == null)
757                                 throw RegistryKey.CreateMarkedForDeletionException ();
758                         self.SetValue (name, value, valueKind);
759                 }
760
761                 public int SubKeyCount (RegistryKey rkey)
762                 {
763                         KeyHandler self = KeyHandler.Lookup (rkey, true);
764                         if (self == null)
765                                 throw RegistryKey.CreateMarkedForDeletionException ();
766                         return Directory.GetDirectories (self.Dir).Length;
767                 }
768                 
769                 public int ValueCount (RegistryKey rkey)
770                 {
771                         KeyHandler self = KeyHandler.Lookup (rkey, true);
772                         if (self == null)
773                                 throw RegistryKey.CreateMarkedForDeletionException ();
774                         return self.ValueCount;
775                 }
776                 
777                 public void DeleteValue (RegistryKey rkey, string name, bool throw_if_missing)
778                 {
779                         KeyHandler self = KeyHandler.Lookup (rkey, true);
780                         if (self == null) {
781                                 // if key is marked for deletion, report success regardless of
782                                 // throw_if_missing
783                                 return;
784                         }
785
786                         if (throw_if_missing && !self.ValueExists (name))
787                                 throw new ArgumentException ("the given value does not exist");
788
789                         self.RemoveValue (name);
790                 }
791                 
792                 public void DeleteKey (RegistryKey rkey, string keyname, bool throw_if_missing)
793                 {
794                         KeyHandler self = KeyHandler.Lookup (rkey, true);
795                         if (self == null) {
796                                 // key is marked for deletion
797                                 if (!throw_if_missing)
798                                         return;
799                                 throw new ArgumentException ("the given value does not exist");
800                         }
801
802                         string dir = Path.Combine (self.Dir, ToUnix (keyname));
803                         
804                         if (!KeyHandler.Delete (dir) && throw_if_missing)
805                                 throw new ArgumentException ("the given value does not exist");
806                 }
807                 
808                 public string [] GetSubKeyNames (RegistryKey rkey)
809                 {
810                         KeyHandler self = KeyHandler.Lookup (rkey, true);
811                         DirectoryInfo selfDir = new DirectoryInfo (self.Dir);
812                         DirectoryInfo[] subDirs = selfDir.GetDirectories ();
813                         string[] subKeyNames = new string[subDirs.Length];
814                         for (int i = 0; i < subDirs.Length; i++) {
815                                 DirectoryInfo subDir = subDirs[i];
816                                 subKeyNames[i] = subDir.Name;
817                         }
818                         return subKeyNames;
819                 }
820                 
821                 public string [] GetValueNames (RegistryKey rkey)
822                 {
823                         KeyHandler self = KeyHandler.Lookup (rkey, true);
824                         if (self == null)
825                                 throw RegistryKey.CreateMarkedForDeletionException ();
826                         return self.GetValueNames ();
827                 }
828
829                 public string ToString (RegistryKey rkey)
830                 {
831                         return rkey.Name;
832                 }
833
834                 private RegistryKey CreateSubKey (RegistryKey rkey, string keyname, bool writable)
835                 {
836                         return CreateSubKey (rkey, keyname, writable, false);
837                 }
838
839                 private RegistryKey CreateSubKey (RegistryKey rkey, string keyname, bool writable, bool is_volatile)
840                 {
841                         KeyHandler self = KeyHandler.Lookup (rkey, true);
842                         if (self == null)
843                                 throw RegistryKey.CreateMarkedForDeletionException ();
844                         if (KeyHandler.VolatileKeyExists (self.Dir) && !is_volatile)
845                                 throw new IOException ("Cannot create a non volatile subkey under a volatile key.");
846
847                         return self.Ensure (rkey, ToUnix (keyname), writable, is_volatile);
848                 }
849
850                 public RegistryValueKind GetValueKind (RegistryKey rkey, string name)
851                 {
852                         KeyHandler self = KeyHandler.Lookup (rkey, true);
853                         if (self != null) 
854                                 return self.GetValueKind (name);
855
856                         // key was removed since it was opened or it does not exist.
857                         return RegistryValueKind.Unknown;
858                 }
859                 
860         }
861 }
862
863 #endif // NET_2_1
864