2

I'm writing code that sets a value in the Windows registry. when I set that value manually, it works as expected; when I want to set the value programmatically, however, it gives an error. The value I want to define is a DWORD value type that is "4294967295". When I define that in the script, though, it says that DWORD does not support this value. And, yet, I can assign that exact value via the program I am using to manually update the registry.

Here's my code:

RegistryKey key = Registry.CurrentUser.OpenSubKey(@"Software\Lost in Days Studio\NO TIME", true);
key.SetValue("Current", 4294967295, RegistryValueKind.DWord);
key.Close();
Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77
  • Hello, and welcome to Stack Overflow! I assume that by manually setting the value, you mean through e.g. `RegEdit.msc` or a similar application? Out of curiosity, what application are you using to manually edit your Windows Registry? – Jeremy Caney Mar 14 '20 at 00:40
  • I go through regedit, and the program I'm editing is a game – A beginner programmer Mar 14 '20 at 00:44
  • When calling `RegistryKey.SetValue` with `RegistryValueKind.DWord`, the method tries to `Convert.ToInt32()` the Value you passed on. Use `QWord` instead. If you don't specify the Type and you set `uint.MaxValue`, it will store a string. – Jimi Mar 14 '20 at 01:08
  • @Jimi: Shouldn’t it call `Convert.ToUInt32()` internally, since a `DWORD` is an unsigned integer? That said, if it did, I’d expect the OP’s code to work correctly since a `long` with the value of `uint.MaxValue` can obviously be converted to a `uint`. – Jeremy Caney Mar 14 '20 at 01:55
  • @Jeremy Caney [RegistryKey.SetValue()](https://referencesource.microsoft.com/#mscorlib/microsoft/win32/registrykey.cs,1555) calls `Convert.ToInt32()`, `Int32` is not a `long`. – Jimi Mar 14 '20 at 02:10
  • 1
    Issue #24954 - [Registry.GetValue returns an Int32 for REG_DWORD instead of an Uint32](https://github.com/dotnet/runtime/issues/24954) (can't fix). – Jimi Mar 14 '20 at 02:22

1 Answers1

4

As you likely know, a DWORD is stored as a 32 bit binary number. What may not be immediately obvious, though, is that it's unsigned—and, thus, a UInt32 (uint). Otherwise, you wouldn't be able to store a value of 4294967295, since the maximum value for a signed integer (int) is 2,147,483,647.

There's a catch, however! As @Jimi noted in the comments, SetValue() will attempt to do a Convert.ToInt32(), which will cause an overflow with any value above Int32.MaxValue—thus the error you are receiving. One would expect it to use Convert.ToUInt32() but, as @Jimi also discovered, that is a known bug in the method, which Microsoft is unable to fix due to backward compatibility concerns.

Instead, the SetValue() method converts a signed Int32 (int) into an unsigned 32 bit binary number, with values of 0…2147483647 remaining as is, but values of -2147483647…-1 getting saved to the 2147483648…4294967295 range.

That binary conversion is a bit convoluted if you're thinking about this as a signed integer. But, fortunately, you can use C#’s built-in unchecked() keyword to permit the overflow and effectively treat your uint as a signed int within those ranges:

key.SetValue("Current", unchecked((int)4294967295), RegistryValueKind.DWord);

This is really handy because it allows for you to continue to work with a standard UInt32 (uint) range of 0…4294967295, exactly like you would via e.g. RegEdit, without having to think about how the binary conversion is being handled.

Jeremy Caney
  • 7,102
  • 69
  • 48
  • 77