Wed Feb 24 15:47:16 CET 2010 Paolo Molaro <lupus@ximian.com>
[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                 public string Dir;
100
101                 Hashtable values;
102                 string file;
103                 bool dirty;
104
105                 KeyHandler (RegistryKey rkey, string basedir)
106                 {
107                         if (!Directory.Exists (basedir)){
108                                 try {
109                                         Directory.CreateDirectory (basedir);
110                                 } catch (UnauthorizedAccessException){
111                                         throw new SecurityException ("No access to the given key");
112                                 }
113                         }
114                         Dir = basedir;
115                         file = Path.Combine (Dir, "values.xml");
116                         Load ();
117                 }
118
119                 public void Load ()
120                 {
121                         values = new Hashtable ();
122                         if (!File.Exists (file))
123                                 return;
124                         
125                         try {
126                                 using (FileStream fs = File.OpenRead (file)){
127                                         StreamReader r = new StreamReader (fs);
128                                         string xml = r.ReadToEnd ();
129                                         if (xml.Length == 0)
130                                                 return;
131                                         
132                                         SecurityElement tree = SecurityElement.FromString (xml);
133                                         if (tree.Tag == "values" && tree.Children != null){
134                                                 foreach (SecurityElement value in tree.Children){
135                                                         if (value.Tag == "value"){
136                                                                 LoadKey (value);
137                                                         }
138                                                 }
139                                         }
140                                 }
141                         } catch (UnauthorizedAccessException){
142                                 values.Clear ();
143                                 throw new SecurityException ("No access to the given key");
144                         } catch (Exception e){
145                                 Console.Error.WriteLine ("While loading registry key at {0}: {1}", file, e);
146                                 values.Clear ();
147                         }
148                 }
149
150                 void LoadKey (SecurityElement se)
151                 {
152                         Hashtable h = se.Attributes;
153                         try {
154                                 string name = (string) h ["name"];
155                                 if (name == null)
156                                         return;
157                                 string type = (string) h ["type"];
158                                 if (type == null)
159                                         return;
160                                 
161                                 switch (type){
162                                 case "int":
163                                         values [name] = Int32.Parse (se.Text);
164                                         break;
165                                 case "bytearray":
166                                         values [name] = Convert.FromBase64String (se.Text);
167                                         break;
168                                 case "string":
169                                         values [name] = se.Text == null ? String.Empty : se.Text;
170                                         break;
171                                 case "expand":
172                                         values [name] = new ExpandString (se.Text);
173                                         break;
174                                 case "qword":
175                                         values [name] = Int64.Parse (se.Text);
176                                         break;
177                                 case "string-array":
178                                         ArrayList sa = new ArrayList ();
179                                         if (se.Children != null){
180                                                 foreach (SecurityElement stre in se.Children){
181                                                         sa.Add (stre.Text);
182                                                 }
183                                         }
184                                         values [name] = sa.ToArray (typeof (string));
185                                         break;
186                                 }
187                         } catch {
188                                 // We ignore individual errors in the file.
189                         }
190                 }
191                 
192                 public RegistryKey Ensure (RegistryKey rkey, string extra, bool writable)
193                 {
194                         lock (typeof (KeyHandler)){
195                                 string f = Path.Combine (Dir, extra);
196                                 KeyHandler kh = (KeyHandler) dir_to_handler [f];
197                                 if (kh == null)
198                                         kh = new KeyHandler (rkey, f);
199                                 RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra), writable);
200                                 key_to_handler [rk] = kh;
201                                 dir_to_handler [f] = kh;
202                                 return rk;
203                         }
204                 }
205
206                 public RegistryKey Probe (RegistryKey rkey, string extra, bool writable)
207                 {
208                         RegistryKey rk = null;
209
210                         lock (typeof (KeyHandler)){
211                                 string f = Path.Combine (Dir, extra);
212                                 KeyHandler kh = (KeyHandler) dir_to_handler [f];
213                                 if (kh != null) {
214                                         rk = new RegistryKey (kh, CombineName (rkey,
215                                                 extra), writable);
216                                         key_to_handler [rk] = kh;
217                                 } else if (Directory.Exists (f)) {
218                                         kh = new KeyHandler (rkey, f);
219                                         rk = new RegistryKey (kh, CombineName (rkey, extra),
220                                                 writable);
221                                         dir_to_handler [f] = kh;
222                                         key_to_handler [rk] = kh;
223                                 }
224                                 return rk;
225                         }
226                 }
227
228                 static string CombineName (RegistryKey rkey, string extra)
229                 {
230                         if (extra.IndexOf ('/') != -1)
231                                 extra = extra.Replace ('/', '\\');
232                         
233                         return String.Concat (rkey.Name, "\\", extra);
234                 }
235                 
236                 public static KeyHandler Lookup (RegistryKey rkey, bool createNonExisting)
237                 {
238                         lock (typeof (KeyHandler)){
239                                 KeyHandler k = (KeyHandler) key_to_handler [rkey];
240                                 if (k != null)
241                                         return k;
242
243                                 // when a non-root key is requested for no keyhandler exist
244                                 // then that key must have been marked for deletion
245                                 if (!rkey.IsRoot || !createNonExisting)
246                                         return null;
247
248                                 RegistryHive x = (RegistryHive) rkey.Hive;
249                                 switch (x){
250                                 case RegistryHive.CurrentUser:
251                                         string userDir = Path.Combine (UserStore, x.ToString ());
252                                         k = new KeyHandler (rkey, userDir);
253                                         dir_to_handler [userDir] = k;
254                                         break;
255                                 case RegistryHive.CurrentConfig:
256                                 case RegistryHive.ClassesRoot:
257                                 case RegistryHive.DynData:
258                                 case RegistryHive.LocalMachine:
259                                 case RegistryHive.PerformanceData:
260                                 case RegistryHive.Users:
261                                         string machine_dir = Path.Combine (MachineStore, x.ToString ());
262                                         k = new KeyHandler (rkey, machine_dir);
263                                         dir_to_handler [machine_dir] = k;
264                                         break;
265                                 default:
266                                         throw new Exception ("Unknown RegistryHive");
267                                 }
268                                 key_to_handler [rkey] = k;
269                                 return k;
270                         }
271                 }
272
273                 public static void Drop (RegistryKey rkey)
274                 {
275                         lock (typeof (KeyHandler)) {
276                                 KeyHandler k = (KeyHandler) key_to_handler [rkey];
277                                 if (k == null)
278                                         return;
279                                 key_to_handler.Remove (rkey);
280
281                                 // remove cached KeyHandler if no other keys reference it
282                                 int refCount = 0;
283                                 foreach (DictionaryEntry de in key_to_handler)
284                                         if (de.Value == k)
285                                                 refCount++;
286                                 if (refCount == 0)
287                                         dir_to_handler.Remove (k.Dir);
288                         }
289                 }
290
291                 public static void Drop (string dir)
292                 {
293                         lock (typeof (KeyHandler)) {
294                                 KeyHandler kh = (KeyHandler) dir_to_handler [dir];
295                                 if (kh == null)
296                                         return;
297
298                                 dir_to_handler.Remove (dir);
299
300                                 // remove (other) references to keyhandler
301                                 ArrayList keys = new ArrayList ();
302                                 foreach (DictionaryEntry de in key_to_handler)
303                                         if (de.Value == kh)
304                                                 keys.Add (de.Key);
305
306                                 foreach (object key in keys)
307                                         key_to_handler.Remove (key);
308                         }
309                 }
310
311                 public RegistryValueKind GetValueKind (string name)
312                 {
313                         if (name == null)
314                                 return RegistryValueKind.Unknown;
315                         object value = values [name];
316                         if (value == null)
317                                 return RegistryValueKind.Unknown;
318
319                         if (value is int)
320                                 return RegistryValueKind.DWord;
321                         if (value is string [])
322                                 return RegistryValueKind.MultiString;
323                         if (value is long)
324                                 return RegistryValueKind.QWord;
325                         if (value is byte [])
326                                 return RegistryValueKind.Binary;
327                         if (value is string)
328                                 return RegistryValueKind.String;
329                         if (value is ExpandString)
330                                 return RegistryValueKind.ExpandString;
331                         return RegistryValueKind.Unknown;
332                 }
333                 
334                 public object GetValue (string name, RegistryValueOptions options)
335                 {
336                         if (IsMarkedForDeletion)
337                                 return null;
338
339                         if (name == null)
340                                 name = string.Empty;
341                         object value = values [name];
342                         ExpandString exp = value as ExpandString;
343                         if (exp == null)
344                                 return value;
345                         if ((options & RegistryValueOptions.DoNotExpandEnvironmentNames) == 0)
346                                 return exp.Expand ();
347
348                         return exp.ToString ();
349                 }
350
351                 public void SetValue (string name, object value)
352                 {
353                         AssertNotMarkedForDeletion ();
354
355                         if (name == null)
356                                 name = string.Empty;
357
358                         // immediately convert non-native registry values to string to avoid
359                         // returning it unmodified in calls to UnixRegistryApi.GetValue
360                         if (value is int || value is string || value is byte[] || value is string[])
361                                 values[name] = value;
362                         else
363                                 values[name] = value.ToString ();
364                         SetDirty ();
365                 }
366
367                 public string [] GetValueNames ()
368                 {
369                         AssertNotMarkedForDeletion ();
370
371                         ICollection keys = values.Keys;
372
373                         string [] vals = new string [keys.Count];
374                         keys.CopyTo (vals, 0);
375                         return vals;
376                 }
377
378                 //
379                 // This version has to do argument validation based on the valueKind
380                 //
381                 public void SetValue (string name, object value, RegistryValueKind valueKind)
382                 {
383                         SetDirty ();
384
385                         if (name == null)
386                                 name = string.Empty;
387
388                         switch (valueKind){
389                         case RegistryValueKind.String:
390                                 if (value is string){
391                                         values [name] = value;
392                                         return;
393                                 }
394                                 break;
395                         case RegistryValueKind.ExpandString:
396                                 if (value is string){
397                                         values [name] = new ExpandString ((string)value);
398                                         return;
399                                 }
400                                 break;
401                                 
402                         case RegistryValueKind.Binary:
403                                 if (value is byte []){
404                                         values [name] = value;
405                                         return;
406                                 }
407                                 break;
408                                 
409                         case RegistryValueKind.DWord:
410                                 if (value is long &&
411                                     (((long) value) < Int32.MaxValue) &&
412                                     (((long) value) > Int32.MinValue)){
413                                         values [name] = (int) ((long)value);
414                                         return;
415                                 }
416                                 if (value is int){
417                                         values [name] = value;
418                                         return;
419                                 }
420                                 break;
421                                 
422                         case RegistryValueKind.MultiString:
423                                 if (value is string []){
424                                         values [name] = value;
425                                         return;
426                                 }
427                                 break;
428                                 
429                         case RegistryValueKind.QWord:
430                                 if (value is int){
431                                         values [name] = (long) ((int) value);
432                                         return;
433                                 }
434                                 if (value is long){
435                                         values [name] = value;
436                                         return;
437                                 }
438                                 break;
439                         default:
440                                 throw new ArgumentException ("unknown value", "valueKind");
441                         }
442                         throw new ArgumentException ("Value could not be converted to specified type", "valueKind");
443                 }
444
445                 void SetDirty ()
446                 {
447                         lock (typeof (KeyHandler)){
448                                 if (dirty)
449                                         return;
450                                 dirty = true;
451                                 new Timer (DirtyTimeout, null, 3000, Timeout.Infinite);
452                         }
453                 }
454
455                 public void DirtyTimeout (object state)
456                 {
457                         Flush ();
458                 }
459
460                 public void Flush ()
461                 {
462                         lock (typeof (KeyHandler)) {
463                                 if (dirty) {
464                                         Save ();
465                                         dirty = false;
466                                 }
467                         }
468                 }
469
470                 public bool ValueExists (string name)
471                 {
472                         if (name == null)
473                                 name = string.Empty;
474
475                         return values.Contains (name);
476                 }
477
478                 public int ValueCount {
479                         get {
480                                 return values.Keys.Count;
481                         }
482                 }
483
484                 public bool IsMarkedForDeletion {
485                         get {
486                                 return !dir_to_handler.Contains (Dir);
487                         }
488                 }
489
490                 public void RemoveValue (string name)
491                 {
492                         AssertNotMarkedForDeletion ();
493
494                         values.Remove (name);
495                         SetDirty ();
496                 }
497
498                 ~KeyHandler ()
499                 {
500                         Flush ();
501                 }
502                 
503                 void Save ()
504                 {
505                         if (IsMarkedForDeletion)
506                                 return;
507
508                         if (!File.Exists (file) && values.Count == 0)
509                                 return;
510
511                         SecurityElement se = new SecurityElement ("values");
512                         
513                         // With SecurityElement.Text = value, and SecurityElement.AddAttribute(key, value)
514                         // the values must be escaped prior to being assigned. 
515                         foreach (DictionaryEntry de in values){
516                                 object val = de.Value;
517                                 SecurityElement value = new SecurityElement ("value");
518                                 value.AddAttribute ("name", SecurityElement.Escape ((string) de.Key));
519                                 
520                                 if (val is string){
521                                         value.AddAttribute ("type", "string");
522                                         value.Text = SecurityElement.Escape ((string) val);
523                                 } else if (val is int){
524                                         value.AddAttribute ("type", "int");
525                                         value.Text = val.ToString ();
526                                 } else if (val is long) {
527                                         value.AddAttribute ("type", "qword");
528                                         value.Text = val.ToString ();
529                                 } else if (val is byte []){
530                                         value.AddAttribute ("type", "bytearray");
531                                         value.Text = Convert.ToBase64String ((byte[]) val);
532                                 } else if (val is ExpandString){
533                                         value.AddAttribute ("type", "expand");
534                                         value.Text = SecurityElement.Escape (val.ToString ());
535                                 } else if (val is string []){
536                                         value.AddAttribute ("type", "string-array");
537
538                                         foreach (string ss in (string[]) val){
539                                                 SecurityElement str = new SecurityElement ("string");
540                                                 str.Text = SecurityElement.Escape (ss); 
541                                                 value.AddChild (str);
542                                         }
543                                 }
544                                 se.AddChild (value);
545                         }
546
547                         using (FileStream fs = File.Create (file)){
548                                 StreamWriter sw = new StreamWriter (fs);
549
550                                 sw.Write (se.ToString ());
551                                 sw.Flush ();
552                         }
553                 }
554
555                 private void AssertNotMarkedForDeletion ()
556                 {
557                         if (IsMarkedForDeletion)
558                                 throw RegistryKey.CreateMarkedForDeletionException ();
559                 }
560
561                 private static string UserStore {
562                         get {
563                                 return Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal),
564                                         ".mono/registry");
565                         }
566                 }
567
568                 private static string MachineStore {
569                         get {
570                                 string s;
571
572                                 s = Environment.GetEnvironmentVariable ("MONO_REGISTRY_PATH");
573                                 if (s != null)
574                                         return s;
575                                 s = Environment.GetMachineConfigPath ();
576                                 int p = s.IndexOf ("machine.config");
577                                 return Path.Combine (Path.Combine (s.Substring (0, p-1), ".."), "registry");
578                         }
579                 }
580         }
581         
582         internal class UnixRegistryApi : IRegistryApi {
583
584                 static string ToUnix (string keyname)
585                 {
586                         if (keyname.IndexOf ('\\') != -1)
587                                 keyname = keyname.Replace ('\\', '/');
588                         return keyname.ToLower ();
589                 }
590
591                 static bool IsWellKnownKey (string parentKeyName, string keyname)
592                 {
593                         // FIXME: Add more keys if needed
594                         if (parentKeyName == Registry.CurrentUser.Name ||
595                                 parentKeyName == Registry.LocalMachine.Name)
596                                 return (0 == String.Compare ("software", keyname, true, CultureInfo.InvariantCulture));
597
598                         return false;
599                 }
600
601                 public RegistryKey CreateSubKey (RegistryKey rkey, string keyname)
602                 {
603                         return CreateSubKey (rkey, keyname, true);
604                 }
605
606                 public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
607                 {
608                         throw new NotImplementedException ();
609                 }
610
611                 public RegistryKey OpenSubKey (RegistryKey rkey, string keyname, bool writable)
612                 {
613                         KeyHandler self = KeyHandler.Lookup (rkey, true);
614                         if (self == null) {
615                                 // return null if parent is marked for deletion
616                                 return null;
617                         }
618
619                         RegistryKey result = self.Probe (rkey, ToUnix (keyname), writable);
620                         if (result == null && IsWellKnownKey (rkey.Name, keyname)) {
621                                 // create the subkey even if its parent was opened read-only
622                                 result = CreateSubKey (rkey, keyname, writable);
623                         }
624
625                         return result;
626                 }
627                 
628                 public void Flush (RegistryKey rkey)
629                 {
630                         KeyHandler self = KeyHandler.Lookup (rkey, false);
631                         if (self == null) {
632                                 // we do not need to flush changes as key is marked for deletion
633                                 return;
634                         }
635                         self.Flush ();
636                 }
637                 
638                 public void Close (RegistryKey rkey)
639                 {
640                         KeyHandler.Drop (rkey);
641                 }
642
643                 public object GetValue (RegistryKey rkey, string name, object default_value, RegistryValueOptions options)
644                 {
645                         KeyHandler self = KeyHandler.Lookup (rkey, true);
646                         if (self == null) {
647                                 // key was removed since it was opened
648                                 return default_value;
649                         }
650
651                         if (self.ValueExists (name))
652                                 return self.GetValue (name, options);
653                         return default_value;
654                 }
655                 
656                 public void SetValue (RegistryKey rkey, string name, object value)
657                 {
658                         KeyHandler self = KeyHandler.Lookup (rkey, true);
659                         if (self == null)
660                                 throw RegistryKey.CreateMarkedForDeletionException ();
661                         self.SetValue (name, value);
662                 }
663
664                 public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
665                 {
666                         KeyHandler self = KeyHandler.Lookup (rkey, true);
667                         if (self == null)
668                                 throw RegistryKey.CreateMarkedForDeletionException ();
669                         self.SetValue (name, value, valueKind);
670                 }
671
672                 public int SubKeyCount (RegistryKey rkey)
673                 {
674                         KeyHandler self = KeyHandler.Lookup (rkey, true);
675                         if (self == null)
676                                 throw RegistryKey.CreateMarkedForDeletionException ();
677                         return Directory.GetDirectories (self.Dir).Length;
678                 }
679                 
680                 public int ValueCount (RegistryKey rkey)
681                 {
682                         KeyHandler self = KeyHandler.Lookup (rkey, true);
683                         if (self == null)
684                                 throw RegistryKey.CreateMarkedForDeletionException ();
685                         return self.ValueCount;
686                 }
687                 
688                 public void DeleteValue (RegistryKey rkey, string name, bool throw_if_missing)
689                 {
690                         KeyHandler self = KeyHandler.Lookup (rkey, true);
691                         if (self == null) {
692                                 // if key is marked for deletion, report success regardless of
693                                 // throw_if_missing
694                                 return;
695                         }
696
697                         if (throw_if_missing && !self.ValueExists (name))
698                                 throw new ArgumentException ("the given value does not exist");
699
700                         self.RemoveValue (name);
701                 }
702                 
703                 public void DeleteKey (RegistryKey rkey, string keyname, bool throw_if_missing)
704                 {
705                         KeyHandler self = KeyHandler.Lookup (rkey, true);
706                         if (self == null) {
707                                 // key is marked for deletion
708                                 if (!throw_if_missing)
709                                         return;
710                                 throw new ArgumentException ("the given value does not exist");
711                         }
712
713                         string dir = Path.Combine (self.Dir, ToUnix (keyname));
714                         
715                         if (Directory.Exists (dir)){
716                                 Directory.Delete (dir, true);
717                                 KeyHandler.Drop (dir);
718                         } else if (throw_if_missing)
719                                 throw new ArgumentException ("the given value does not exist");
720                 }
721                 
722                 public string [] GetSubKeyNames (RegistryKey rkey)
723                 {
724                         KeyHandler self = KeyHandler.Lookup (rkey, true);
725                         DirectoryInfo selfDir = new DirectoryInfo (self.Dir);
726                         DirectoryInfo[] subDirs = selfDir.GetDirectories ();
727                         string[] subKeyNames = new string[subDirs.Length];
728                         for (int i = 0; i < subDirs.Length; i++) {
729                                 DirectoryInfo subDir = subDirs[i];
730                                 subKeyNames[i] = subDir.Name;
731                         }
732                         return subKeyNames;
733                 }
734                 
735                 public string [] GetValueNames (RegistryKey rkey)
736                 {
737                         KeyHandler self = KeyHandler.Lookup (rkey, true);
738                         if (self == null)
739                                 throw RegistryKey.CreateMarkedForDeletionException ();
740                         return self.GetValueNames ();
741                 }
742
743                 public string ToString (RegistryKey rkey)
744                 {
745                         return rkey.Name;
746                 }
747
748                 private RegistryKey CreateSubKey (RegistryKey rkey, string keyname, bool writable)
749                 {
750                         KeyHandler self = KeyHandler.Lookup (rkey, true);
751                         if (self == null)
752                                 throw RegistryKey.CreateMarkedForDeletionException ();
753                         return self.Ensure (rkey, ToUnix (keyname), writable);
754                 }
755
756                 public RegistryValueKind GetValueKind (RegistryKey rkey, string name)
757                 {
758                         KeyHandler self = KeyHandler.Lookup (rkey, true);
759                         if (self != null) 
760                                 return self.GetValueKind (name);
761
762                         // key was removed since it was opened or it does not exist.
763                         return RegistryValueKind.Unknown;
764                 }
765                 
766         }
767 }
768
769 #endif // NET_2_1
770