//
-// Microsoft.Win32/IRegistryApi.cs
+// Microsoft.Win32/UnixRegistryApi.cs
//
// Authors:
// Miguel de Icaza (miguel@gnome.org)
+// Gert Driesen (drieseng@users.sourceforge.net)
//
-// (C) 2005 Novell, Inc (http://www.novell.com)
+// (C) 2005, 2006 Novell, Inc (http://www.novell.com)
//
// MISSING:
-// Someone could the same subkey twice: once read/write once readonly,
-// currently since we use a unique hash based on the file name, we are unable
-// to have two versions of the same key and hence unable to throw an exception
-// if the user tries to write to a read-only key.
+// It would be useful if we do case-insensitive expansion of variables,
+// the registry is very windows specific, so we probably should default to
+// those semantics in expanding environment variables, for example %path%
+//
+// We should use an ordered collection for storing the values (instead of
+// a Hashtable).
//
// Copyright (C) 2004 Novell, Inc (http://www.novell.com)
//
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
+#if !NET_2_1
+
using System;
using System.Collections;
using System.Globalization;
}
} else {
sb.Append (value [i]);
- }
+ }
}
return sb.ToString ();
}
}
- class KeyHandler {
+
+ class KeyHandler
+ {
static Hashtable key_to_handler = new Hashtable ();
- static Hashtable dir_to_key = new Hashtable ();
+ static Hashtable dir_to_handler = new Hashtable (
+ new CaseInsensitiveHashCodeProvider (), new CaseInsensitiveComparer ());
public string Dir;
- public IntPtr Handle;
- public Hashtable values;
+ Hashtable values;
string file;
bool dirty;
- bool valid = true;
-
+
KeyHandler (RegistryKey rkey, string basedir)
{
if (!Directory.Exists (basedir)){
try {
Directory.CreateDirectory (basedir);
- } catch (Exception e){
- Console.Error.WriteLine ("KeyHandler error while creating directory {0}:\n{1}", basedir, e);
+ } catch (UnauthorizedAccessException){
+ throw new SecurityException ("No access to the given key");
}
}
Dir = basedir;
}
}
}
+ } catch (UnauthorizedAccessException){
+ values.Clear ();
+ throw new SecurityException ("No access to the given key");
} catch (Exception e){
Console.Error.WriteLine ("While loading registry key at {0}: {1}", file, e);
values.Clear ();
values [name] = Int32.Parse (se.Text);
break;
case "bytearray":
- Convert.FromBase64String (se.Text);
+ values [name] = Convert.FromBase64String (se.Text);
break;
case "string":
values [name] = se.Text;
}
}
- public RegistryKey Ensure (RegistryKey rkey, string extra)
+ public RegistryKey Ensure (RegistryKey rkey, string extra, bool writable)
{
lock (typeof (KeyHandler)){
string f = Path.Combine (Dir, extra);
- if (dir_to_key.Contains (f))
- return (RegistryKey) dir_to_key [f];
-
- KeyHandler kh = new KeyHandler (rkey, f);
- RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra));
+ KeyHandler kh = (KeyHandler) dir_to_handler [f];
+ if (kh == null)
+ kh = new KeyHandler (rkey, f);
+ RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra), writable);
key_to_handler [rk] = kh;
- dir_to_key [f] = rk;
+ dir_to_handler [f] = kh;
return rk;
}
}
- public RegistryKey Probe (RegistryKey rkey, string extra, bool write)
+ public RegistryKey Probe (RegistryKey rkey, string extra, bool writable)
{
+ RegistryKey rk = null;
+
lock (typeof (KeyHandler)){
string f = Path.Combine (Dir, extra);
- if (dir_to_key.Contains (f))
- return (RegistryKey) dir_to_key [f];
- if (Directory.Exists (f)){
- KeyHandler kh = new KeyHandler (rkey, f);
- RegistryKey rk = new RegistryKey (kh, CombineName (rkey, extra));
- dir_to_key [f] = rk;
+ KeyHandler kh = (KeyHandler) dir_to_handler [f];
+ if (kh != null) {
+ rk = new RegistryKey (kh, CombineName (rkey,
+ extra), writable);
+ key_to_handler [rk] = kh;
+ } else if (Directory.Exists (f)) {
+ kh = new KeyHandler (rkey, f);
+ rk = new RegistryKey (kh, CombineName (rkey, extra),
+ writable);
+ dir_to_handler [f] = kh;
key_to_handler [rk] = kh;
- return rk;
}
- return null;
+ return rk;
}
}
return String.Concat (rkey.Name, "\\", extra);
}
- public static KeyHandler Lookup (RegistryKey rkey)
+ public static KeyHandler Lookup (RegistryKey rkey, bool createNonExisting)
{
lock (typeof (KeyHandler)){
KeyHandler k = (KeyHandler) key_to_handler [rkey];
if (k != null)
return k;
- RegistryHive x = (RegistryHive) rkey.Data;
+ // when a non-root key is requested for no keyhandler exist
+ // then that key must have been marked for deletion
+ if (!rkey.IsRoot || !createNonExisting)
+ return null;
+
+ RegistryHive x = (RegistryHive) rkey.Hive;
switch (x){
- case RegistryHive.ClassesRoot:
- case RegistryHive.CurrentConfig:
case RegistryHive.CurrentUser:
+ string userDir = Path.Combine (UserStore, x.ToString ());
+ k = new KeyHandler (rkey, userDir);
+ dir_to_handler [userDir] = k;
+ break;
+ case RegistryHive.CurrentConfig:
+ case RegistryHive.ClassesRoot:
case RegistryHive.DynData:
case RegistryHive.LocalMachine:
case RegistryHive.PerformanceData:
case RegistryHive.Users:
- string d = Path.Combine (RegistryStore, x.ToString ());
- k = new KeyHandler (rkey, d);
+ string machine_dir = Path.Combine (MachineStore, x.ToString ());
+ k = new KeyHandler (rkey, machine_dir);
+ dir_to_handler [machine_dir] = k;
break;
default:
throw new Exception ("Unknown RegistryHive");
public static void Drop (RegistryKey rkey)
{
- KeyHandler k = (KeyHandler) key_to_handler [rkey];
- if (k == null)
- return;
- k.valid = false;
- dir_to_key.Remove (k.Dir);
- key_to_handler.Remove (rkey);
+ lock (typeof (KeyHandler)) {
+ KeyHandler k = (KeyHandler) key_to_handler [rkey];
+ if (k == null)
+ return;
+ key_to_handler.Remove (rkey);
+
+ // remove cached KeyHandler if no other keys reference it
+ int refCount = 0;
+ foreach (DictionaryEntry de in key_to_handler)
+ if (de.Value == k)
+ refCount++;
+ if (refCount == 0)
+ dir_to_handler.Remove (k.Dir);
+ }
}
public static void Drop (string dir)
{
- if (dir_to_key.Contains (dir)){
- RegistryKey rkey = (RegistryKey) dir_to_key [dir];
- Drop (rkey);
+ lock (typeof (KeyHandler)) {
+ KeyHandler kh = (KeyHandler) dir_to_handler [dir];
+ if (kh == null)
+ return;
+
+ dir_to_handler.Remove (dir);
+
+ // remove (other) references to keyhandler
+ ArrayList keys = new ArrayList ();
+ foreach (DictionaryEntry de in key_to_handler)
+ if (de.Value == kh)
+ keys.Add (de.Key);
+
+ foreach (object key in keys)
+ key_to_handler.Remove (key);
}
}
+ public object GetValue (string name, RegistryValueOptions options)
+ {
+ if (IsMarkedForDeletion)
+ return null;
+
+ if (name == null)
+ name = string.Empty;
+ object value = values [name];
+ ExpandString exp = value as ExpandString;
+ if (exp == null)
+ return value;
+ if ((options & RegistryValueOptions.DoNotExpandEnvironmentNames) == 0)
+ return exp.Expand ();
+
+ return exp.ToString ();
+ }
+
public void SetValue (string name, object value)
{
+ AssertNotMarkedForDeletion ();
+
+ if (name == null)
+ name = string.Empty;
+
// immediately convert non-native registry values to string to avoid
// returning it unmodified in calls to UnixRegistryApi.GetValue
if (value is int || value is string || value is byte[] || value is string[])
SetDirty ();
}
+ public string [] GetValueNames ()
+ {
+ AssertNotMarkedForDeletion ();
+
+ ICollection keys = values.Keys;
+
+ string [] vals = new string [keys.Count];
+ keys.CopyTo (vals, 0);
+ return vals;
+ }
+
#if NET_2_0
//
// This version has to do argument validation based on the valueKind
public void SetValue (string name, object value, RegistryValueKind valueKind)
{
SetDirty ();
+
+ if (name == null)
+ name = string.Empty;
+
switch (valueKind){
case RegistryValueKind.String:
if (value is string){
break;
case RegistryValueKind.ExpandString:
if (value is string){
- Console.WriteLine ("SETTING THIS BAD BOY {0} to {1}", name, "Exp");
values [name] = new ExpandString ((string)value);
return;
}
throw new ArgumentException ("Value could not be converted to specified type", "valueKind");
}
#endif
-
+
void SetDirty ()
{
lock (typeof (KeyHandler)){
{
Flush ();
}
-
+
public void Flush ()
{
- lock (typeof (KeyHandler)){
- Save ();
- dirty = false;
+ lock (typeof (KeyHandler)) {
+ if (dirty) {
+ Save ();
+ dirty = false;
+ }
+ }
+ }
+
+ public bool ValueExists (string name)
+ {
+ if (name == null)
+ name = string.Empty;
+
+ return values.Contains (name);
+ }
+
+ public int ValueCount {
+ get {
+ return values.Keys.Count;
}
}
+ public bool IsMarkedForDeletion {
+ get {
+ return !dir_to_handler.Contains (Dir);
+ }
+ }
+
+ public void RemoveValue (string name)
+ {
+ AssertNotMarkedForDeletion ();
+
+ values.Remove (name);
+ SetDirty ();
+ }
+
~KeyHandler ()
{
Flush ();
void Save ()
{
- if (!valid)
+ if (IsMarkedForDeletion)
return;
-
+
if (!File.Exists (file) && values.Count == 0)
return;
SecurityElement se = new SecurityElement ("values");
+ // With SecurityElement.Text = value, and SecurityElement.AddAttribute(key, value)
+ // the values must be escaped prior to being assigned.
foreach (DictionaryEntry de in values){
object val = de.Value;
SecurityElement value = new SecurityElement ("value");
- value.AddAttribute ("name", (string) de.Key);
+ value.AddAttribute ("name", SecurityElement.Escape ((string) de.Key));
if (val is string){
value.AddAttribute ("type", "string");
- value.Text = (string) val;
+ value.Text = SecurityElement.Escape ((string) val);
} else if (val is int){
value.AddAttribute ("type", "int");
value.Text = val.ToString ();
- } else if (val is long){
+ } else if (val is long) {
value.AddAttribute ("type", "qword");
value.Text = val.ToString ();
} else if (val is byte []){
value.Text = Convert.ToBase64String ((byte[]) val);
} else if (val is ExpandString){
value.AddAttribute ("type", "expand");
- value.Text = val.ToString ();
+ value.Text = SecurityElement.Escape (val.ToString ());
} else if (val is string []){
value.AddAttribute ("type", "string-array");
foreach (string ss in (string[]) val){
SecurityElement str = new SecurityElement ("string");
- str.Text = ss;
+ str.Text = SecurityElement.Escape (ss);
value.AddChild (str);
}
}
se.AddChild (value);
}
- try {
- using (FileStream fs = File.Create (file)){
- StreamWriter sw = new StreamWriter (fs);
+ using (FileStream fs = File.Create (file)){
+ StreamWriter sw = new StreamWriter (fs);
- sw.Write (se.ToString ());
- sw.Flush ();
- }
- } catch (Exception e){
- Console.Error.WriteLine ("When saving {0} got {1}", file, e);
+ sw.Write (se.ToString ());
+ sw.Flush ();
}
}
- public static string RegistryStore {
+ private void AssertNotMarkedForDeletion ()
+ {
+ if (IsMarkedForDeletion)
+ throw RegistryKey.CreateMarkedForDeletionException ();
+ }
+
+ private static string UserStore {
get {
return Path.Combine (Environment.GetFolderPath (Environment.SpecialFolder.Personal),
".mono/registry");
}
}
+ private static string MachineStore {
+ get {
+ string s;
+
+ s = Environment.GetEnvironmentVariable ("MONO_REGISTRY_PATH");
+ if (s != null)
+ return s;
+ s = Environment.GetMachineConfigPath ();
+ int p = s.IndexOf ("machine.config");
+ return Path.Combine (Path.Combine (s.Substring (0, p-1), ".."), "registry");
+ }
+ }
}
internal class UnixRegistryApi : IRegistryApi {
static bool IsWellKnownKey (string parentKeyName, string keyname)
{
- if (string.Compare ("software", keyname, true, CultureInfo.InvariantCulture) == 0)
- return (parentKeyName == Registry.CurrentUser.Name ||
- parentKeyName == Registry.LocalMachine.Name);
-
- // required for event log support
- if (string.Compare (@"SYSTEM\CurrentControlSet\Services\EventLog", keyname, true, CultureInfo.InvariantCulture) == 0)
- return (parentKeyName == Registry.LocalMachine.Name);
+ // FIXME: Add more keys if needed
+ if (parentKeyName == Registry.CurrentUser.Name ||
+ parentKeyName == Registry.LocalMachine.Name)
+ return (0 == String.Compare ("software", keyname, true, CultureInfo.InvariantCulture));
return false;
}
public RegistryKey CreateSubKey (RegistryKey rkey, string keyname)
{
- KeyHandler self = KeyHandler.Lookup (rkey);
- return self.Ensure (rkey, ToUnix (keyname));
+ return CreateSubKey (rkey, keyname, true);
+ }
+
+ public RegistryKey OpenRemoteBaseKey (RegistryHive hKey, string machineName)
+ {
+ throw new NotImplementedException ();
}
- public RegistryKey OpenSubKey (RegistryKey rkey, string keyname, bool writtable)
+ public RegistryKey OpenSubKey (RegistryKey rkey, string keyname, bool writable)
{
- KeyHandler self = KeyHandler.Lookup (rkey);
- RegistryKey result = self.Probe (rkey, ToUnix (keyname), writtable);
+ KeyHandler self = KeyHandler.Lookup (rkey, true);
+ if (self == null) {
+ // return null if parent is marked for deletion
+ return null;
+ }
+
+ RegistryKey result = self.Probe (rkey, ToUnix (keyname), writable);
if (result == null && IsWellKnownKey (rkey.Name, keyname)) {
- result = CreateSubKey (rkey, keyname);
+ // create the subkey even if its parent was opened read-only
+ result = CreateSubKey (rkey, keyname, writable);
}
return result;
public void Flush (RegistryKey rkey)
{
- KeyHandler self = KeyHandler.Lookup (rkey);
+ KeyHandler self = KeyHandler.Lookup (rkey, false);
+ if (self == null) {
+ // we do not need to flush changes as key is marked for deletion
+ return;
+ }
self.Flush ();
}
{
KeyHandler.Drop (rkey);
}
-
- public object GetValue (RegistryKey rkey, string name, bool return_default_value, object default_value)
- {
- KeyHandler self = KeyHandler.Lookup (rkey);
- if (self.values.Contains (name)){
- object r = self.values [name];
-
- if (r is ExpandString){
- return ((ExpandString)r).Expand ();
- }
-
- return r;
- }
- if (return_default_value)
+ public object GetValue (RegistryKey rkey, string name, object default_value, RegistryValueOptions options)
+ {
+ KeyHandler self = KeyHandler.Lookup (rkey, true);
+ if (self == null) {
+ // key was removed since it was opened
return default_value;
- return null;
+ }
+
+ if (self.ValueExists (name))
+ return self.GetValue (name, options);
+ return default_value;
}
public void SetValue (RegistryKey rkey, string name, object value)
{
- KeyHandler self = KeyHandler.Lookup (rkey);
+ KeyHandler self = KeyHandler.Lookup (rkey, true);
+ if (self == null)
+ throw RegistryKey.CreateMarkedForDeletionException ();
self.SetValue (name, value);
}
#if NET_2_0
public void SetValue (RegistryKey rkey, string name, object value, RegistryValueKind valueKind)
{
- KeyHandler self = KeyHandler.Lookup (rkey);
+ KeyHandler self = KeyHandler.Lookup (rkey, true);
+ if (self == null)
+ throw RegistryKey.CreateMarkedForDeletionException ();
self.SetValue (name, value, valueKind);
}
#endif
-
+
public int SubKeyCount (RegistryKey rkey)
{
- KeyHandler self = KeyHandler.Lookup (rkey);
-
+ KeyHandler self = KeyHandler.Lookup (rkey, true);
+ if (self == null)
+ throw RegistryKey.CreateMarkedForDeletionException ();
return Directory.GetDirectories (self.Dir).Length;
}
public int ValueCount (RegistryKey rkey)
{
- KeyHandler self = KeyHandler.Lookup (rkey);
-
- return self.values.Keys.Count;
+ KeyHandler self = KeyHandler.Lookup (rkey, true);
+ if (self == null)
+ throw RegistryKey.CreateMarkedForDeletionException ();
+ return self.ValueCount;
}
public void DeleteValue (RegistryKey rkey, string name, bool throw_if_missing)
{
- KeyHandler self = KeyHandler.Lookup (rkey);
+ KeyHandler self = KeyHandler.Lookup (rkey, true);
+ if (self == null) {
+ // if key is marked for deletion, report success regardless of
+ // throw_if_missing
+ return;
+ }
- if (throw_if_missing && !self.values.Contains (name))
- throw new ArgumentException ("the given value does not exist", "name");
+ if (throw_if_missing && !self.ValueExists (name))
+ throw new ArgumentException ("the given value does not exist");
- self.values.Remove (name);
+ self.RemoveValue (name);
}
public void DeleteKey (RegistryKey rkey, string keyname, bool throw_if_missing)
{
- KeyHandler self = KeyHandler.Lookup (rkey);
- string dir = Path.Combine (self.Dir, ToUnix (keyname));
+ KeyHandler self = KeyHandler.Lookup (rkey, true);
+ if (self == null) {
+ // key is marked for deletion
+ if (!throw_if_missing)
+ return;
+ throw new ArgumentException ("the given value does not exist");
+ }
+ string dir = Path.Combine (self.Dir, ToUnix (keyname));
+
if (Directory.Exists (dir)){
Directory.Delete (dir, true);
KeyHandler.Drop (dir);
} else if (throw_if_missing)
- throw new ArgumentException ("the given value does not exist", "value");
+ throw new ArgumentException ("the given value does not exist");
}
public string [] GetSubKeyNames (RegistryKey rkey)
{
- KeyHandler self = KeyHandler.Lookup (rkey);
+ KeyHandler self = KeyHandler.Lookup (rkey, true);
DirectoryInfo selfDir = new DirectoryInfo (self.Dir);
DirectoryInfo[] subDirs = selfDir.GetDirectories ();
string[] subKeyNames = new string[subDirs.Length];
public string [] GetValueNames (RegistryKey rkey)
{
- KeyHandler self = KeyHandler.Lookup (rkey);
- ICollection keys = self.values.Keys;
-
- string [] vals = new string [keys.Count];
- keys.CopyTo (vals, 0);
- return vals;
+ KeyHandler self = KeyHandler.Lookup (rkey, true);
+ if (self == null)
+ throw RegistryKey.CreateMarkedForDeletionException ();
+ return self.GetValueNames ();
}
public string ToString (RegistryKey rkey)
{
return rkey.Name;
}
+
+ private RegistryKey CreateSubKey (RegistryKey rkey, string keyname, bool writable)
+ {
+ KeyHandler self = KeyHandler.Lookup (rkey, true);
+ if (self == null)
+ throw RegistryKey.CreateMarkedForDeletionException ();
+ return self.Ensure (rkey, ToUnix (keyname), writable);
+ }
}
}
+
+#endif // NET_2_1
+