Obsługa rejestru systemu Windows w C#

Dzisiejszy wpis poświęcę – tak jak wcześniej obiecałem zagadnieniu, jakim jest obsługa rejestru, przy użyciu języka C#. Nie przedłużając więc – do dzieła!

Czym właściwie jest rejestr?

Zanim przejdziemy do kodu, warto przypomnieć sobie, czym rejestr właściwie jest. Najprościej rzecz ujmując, jest to baza danych przechowującą m.in dane o konfiguracji systemu, urządzeń, oprogramowania. System odwołuje się do niego za każdym razem, kiedy takie dane są mu potrzebne. To w nim przechowywane są na przykład informacje o powiązaniach rozszerzeń plików z oprogramowaniem, czy też o tym, jakie oprogramowanie ma być uruchamiane wraz z systemem. Lista danych, które mieszczą się w tym miejscu, jest bardzo długa, a znajomość ich modyfikowania bywa bardzo pomocna. W końcu czasami możemy chcieć, żeby nasz program został wpisany np. do auto-startu 😉

Budowa rejestru

Rejestr systemu Windows ma postać hierarchicznej bazy danych. Znajduje się w nim pięć kluczy głównych:

  • HKEY_CLASSES_ROOT
  • HKEY_CURRENT_USER
  • HKEY_LOCAL_MACHINE
  • HKEY_USERS
  • HKEY_CURRENT_CONFIG

Każdy klucz(z wyjątkiem głównego) jest przypisany do dokładnie jednego nadrzędnego klucza. Każdy klucz może przechowywać dowolną ilość podkluczy i wartości. Wartości to elementy przechowujące dane, edytor rejestru pozwala na dodawanie ich sześciu typów:

  • Wartość ciągu (REG_SZ) – jest to prosty ciąg tekstowy, o stałej wartości.
  • Wartość binarna (REG_BINARY) – są to najzwyczajniej w świecie dane binarne.
  • Wartość DWORD (32-bitowa) – dane reprezentowane przez liczbę 32-bitową liczbę całkowitą.
  • Wartość QWORD (64-bitowa) – dane reprezentowane przez 64-bitową liczbę całkowitą.
  • Wartość ciągu wielokrotnego (REG_MULTI_SZ) – pozwala przechowywać kilka ciągów.
  • Wartość ciągu rozwijalnego (REG_EXPAND_SZ) – ciąg tekstowy o zmiennej długości.

Chociaż w sumie jest ich aż… jedenaście. Jeżeli kogoś interesują, takie szczegóły to odsyłam na stronę Microsoftu. Znajdziecie tam pełną listę typów, jak i więcej szczegółowych informacji dotyczących rejestru.

Jeżeli ktoś ma problemy ze zrozumieniem struktury rejestru, to może wyobrazić go sobie jako zbiór katalogów, bo w sumie takowy przypomina. Klucze można traktować jako foldery, a wartości jako pliki. Ot, prosta analogia, a jakże trafna 😉

Poruszanie się po rejestrze przy użyciu C#

Odczytywanie danych

Na początku zaznaczę może, że pracę z rejestrem umożliwia nam przestrzeń nazw Microsoft.Win32. Aby odczytać jakieś wartości z rejestru należy najpierw otworzyć docelowy klucz. Robi się to w taki sposób:

using Microsoft.Win32;
using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            RegistryKey rkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE\Classes\Local Settings\Software\Microsoft\Windows\Shell\MuiCache"); //otwieranie klucza
            string[] values = rkey.GetValueNames(); //pobranie listy nazw jego właściwości
            string[] subKeyNames = rkey.GetSubKeyNames(); //pobranie listy jego podkluczy
            Console.WriteLine("Values:");
            foreach (string value in values)
            {
                Console.WriteLine("Value: " + value + " Value Data: " + rkey.GetValue(value)); //wyświetlenie nazw właściwości i ich wartości
            }
            Console.WriteLine("SubKeys:");
            foreach (string subKey in subKeyNames)
                Console.WriteLine("SubKey: " + subKey);
            Console.ReadKey();
        }
    }
}

Jak widać na przykładzie powyższego kodu, za otwieranie klucza odpowiada metoda Registry.CurrentUser.OpenSubKey(subKey);. Otwiera ona podklucz w głównej gałęzi HKEY_CURRENT_USER. Do otwierania podkluczy innych gałęzi głównych musimy używać analogicznych metod:

RegistryKey rkey1 = Registry.ClassesRoot.OpenSubKey();
RegistryKey rkey2 = Registry.CurrentConfig.OpenSubKey();
RegistryKey rkey3 = Registry.LocalMachine.OpenSubKey();
RegistryKey rkey4 = Registry.Users.OpenSubKey();

Kolejne linijki kodu pobierają podklucze/nazwy właściwości danego klucza(oraz ich dane) i je wyświetlają. Wszystko realizowane jest bardzo prosto i czytelnie. To co chcemy z takimi danymi zrobić, zależy już tylko i wyłącznie od nas.

Zapisywanie i usuwanie danych, na przykładzie dodania programu do auto-startu

Zapisywanie i usuwanie danych jest również bardzo proste, bo realizuje się je przy pomocy kilku metod:

RegistryKey rkey = Registry.CurrentUser.OpenSubKey(@"SOFTWARE", true);
rkey.CreateSubKey(keyName); //tworzenie podklucza
rkey.SetValue("ValueName", obj); //zapisanie wartości
rkey.DeleteValue(keyName, false); //usunięcie wartości
rkey.DeleteSubKey(keyName); //usunięcie podklucza
rkey.DeleteSubKeyTree(keyName); //usunięcie podklucza, z jego podkluczami

Jedyną rzeczą, na którą należy tutaj zwrócić uwagę, jest fakt, że klucz musi zostać otworzony w trybie umożliwiającym zapis. Odpowiada za to drugi parametr metody OpenSubKey();.

Znając te zasady, możemy z powodzeniem napisać kod, który doda naszą aplikację do auto-startu:

using Microsoft.Win32;
using System;

namespace ConsoleApp1
{
    class Program
    {
        static void Main(string[] args)
        {
            if (AddToAutoStart("MyApp"))
                Console.WriteLine("Aplikacja dodana do autostartu!");
            Console.ReadKey();
            if (RemoveFromAutostart("MyApp"))
                Console.WriteLine("Aplikacja usunięta z autostartu!");
            Console.ReadKey();

        }
        private static bool AddToAutoStart(string AppName)
        {
            RegistryKey rkApp = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true); //otworzenie klucza, w którym obecne są aplikacje uruchamiane z systemem
            if (!IsStartupItem(AppName)) //sprawdzenie czy aplikacja znajduje się już w autostarcie
            {
                try
                {
                    rkApp.SetValue(AppName, System.Reflection.Assembly.GetEntryAssembly().Location.ToString()); //dodanie wartości do klucza
                }
                catch (Exception)
                {
                    return false;
                }
                return true;
            }
            return false;
        }
        public static bool RemoveFromAutostart(string AppName)
        {
            RegistryKey rkApp = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);

            if (IsStartupItem(AppName))
            {
                try
                {
                    rkApp.DeleteValue(AppName, false); //usunięcie wartości klucza
                }
                catch (Exception)
                {
                    return false;
                }
                return true;
            }
            return false;
        }
        private static bool IsStartupItem(string AppName)
        {
            RegistryKey rkApp = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run", true);
            if (rkApp.GetValue(AppName) == null)
                return false;
            else
                return true;
        }
    }
}

Usuwanie kluczy i właściwości przy użyciu samej nazwy

Czasami może zajść potrzeba (taka zaszła w moim programie), żeby usunąć jakiś klucz rejestru/właściwość przy użyciu samej jego nazwy. Do tego celu bardzo przydatne mogą okazać się funkcje, które udało mi się na potrzeby Cleanera napisać:

private static RegistryKey GetMasterKeyByName(string name)
{
    string key = name.Remove(name.IndexOf(@"\"));
    if (key == "HKEY_CLASSES_ROOT")
       return Registry.ClassesRoot;
    if (key == "HKEY_CURRENT_USER")
       return Registry.CurrentUser;
    if (key == "HKEY_LOCAL_MACHINE")
       return Registry.LocalMachine;
    if (key == "HKEY_USERS")
       return Registry.Users;
    if (key == "HKEY_CURRENT_CONFIG")
       return Registry.CurrentConfig;
    return null;
}
private static void DeleteKey(string key)
{
    string _key = key.Substring((key.IndexOf(@"\") + 1));
    string _keyWithOutSubKey = _key.Remove(_key.LastIndexOf(@"\"));
    string _subKeyName = _key.Substring(_key.LastIndexOf(@"\") + 1);
    RegistryKey rkey = GetMasterKeyByName(key).OpenSubKey(_keyWithOutSubKey, true);
    rkey.DeleteSubKey(_subKeyName);
}
private static void DeleteValue(string key, string value)
{
    RegistryKey rkey = GetMasterKeyByName(key).OpenSubKey(key.Substring((key.IndexOf(@"\") + 1)), true);
    rkey.DeleteValue(value);
}

Ich użycie jest banalnie proste:

DeleteKey(keyName);
DeleteValue(keyName, valueName);

Gdzie keyName jest ścieżką do klucza rejestru, typu: „HKEY_CURRENT_USER\Control Panel\Accessibility\Blind Access”. Z kolei valueName nazwą właściwości. Funkcja GetMasterKeyName() z powodzeniem może służyć do pozyskiwania obiektu klucza głównego przy użyciu jego pełnej nazwy(string).

Jak widać obsługa rejestru, nie jest czymś trudnym. Przestrzegam jednak przed wprowadzaniem w nim pochopnych zmian – bo mogą unieruchomić komputer. Cóż, rejestr jest dosyć delikatnym tworem. Ode mnie to byłoby na tyle. Jak zwykle czekam na Wasze uwagi w komentarzach 🙂

2,323 total views, 2 views today