Type-safe registry access in .NET
Wrapping the Windows registry behind a small, strongly-typed API so the call site reads like intent, not plumbing.
The Windows registry API is one of those things you only touch occasionally — just often enough to forget the exact ceremony, and just rarely enough that you copy-paste it wrong. That itch is why I wrote RegistryWrapper.
The problem
Reading a value with the built-in API looks something like this:
using Microsoft.Win32;
object? raw = Registry.GetValue(
@"HKEY_LOCAL_MACHINE\SOFTWARE\MyApp",
"InstallPath",
null);
string installPath = raw as string ?? throw new InvalidOperationException();
There’s a lot going on for what is, conceptually, “give me this string.” You
deal with object?, a stringly-typed key path, manual null handling, and a cast
that silently lies if the value is the wrong type.
A small abstraction
The goal isn’t to reinvent the API — it’s to make the call site read like the intent. A thin, generic accessor gets us most of the way:
public static T GetValue<T>(RegistryHive hive, string subKey, string name)
{
using RegistryKey root = RegistryKey.OpenBaseKey(hive, RegistryView.Default);
using RegistryKey? key = root.OpenSubKey(subKey)
?? throw new KeyNotFoundException($"Missing subkey: {subKey}");
object? value = key.GetValue(name)
?? throw new KeyNotFoundException($"Missing value: {name}");
return (T)Convert.ChangeType(value, typeof(T));
}
Now the everyday case is unambiguous:
string path = RegistryWrapper.GetValue<string>(
RegistryHive.LocalMachine, @"SOFTWARE\MyApp", "InstallPath");
Why it’s worth the wrapper
A few things fall out of this for free:
- The type is the contract.
GetValue<int>either gives you anintor throws — no silentnullfrom a bad cast. - Failures are loud and specific. A missing key and a missing value are different exceptions with useful messages.
usingeverywhere. Registry handles are disposable; centralizing access means you can’t forget to release them.
Good abstractions don’t hide capability — they hide ceremony. The wrapper still lets you reach the raw API when you need it; it just makes the 95% case pleasant.
I keep two flavors of this around — one targeting .NET Framework and one for .NET Core — because the registry isn’t going anywhere, and neither is my need to read from it without wincing.