MattB
MattB

Reputation: 1104

Backup Registry Key in C# without Reg.exe

I need to backup a registry key in C#. I've been trying to P/Invoke RegSaveKey to no avail. I can't use "Reg.exe" to backup the registry due to Group Policy settings which can't be turned off.

Here's all of the code:

private static UIntPtr GetKey(string registryPath)
{
  UIntPtr hKey = UIntPtr.Zero;

  if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, registryPath, 0, KEY_READ, out hKey) != 0)
  {
     throw new Exception("Error getting key!");
  }

  return hKey;
}

private static void ExportRegistry(string registryPath)
{
  UIntPtr key = UIntPtr.Zero;
  try
  {
    key = GetKey(registryPath);
  }
  catch (Exception ex)
  {
    Console.WriteLine(ex.Message);
    return;
  }

  if (key == UIntPtr.Zero)
  {
    Console.WriteLine("Not key to export!");
    return;
  }

  try
  {
    RegSaveKey(key, "c:\\temp\\test.reg", IntPtr.Zero);
  }
  catch (Exception ex)
  {
    Console.WriteLine(ex);
  }

  if (key != UIntPtr.Zero)
  {
    RegCloseKey(key);
  }
}

private static int KEY_READ = 131097;
private static UIntPtr HKEY_LOCAL_MACHINE = new UIntPtr(0x80000002u);

[DllImport("advapi32.dll", SetLastError = true)]
private static extern int RegCloseKey(UIntPtr hKey);

[DllImport("advapi32.dll", SetLastError = true)]
private static extern int RegOpenKeyEx(UIntPtr hKey, string subKey, int ulOptions, int samDesired, out UIntPtr hkResult);

[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern uint RegSaveKey(UIntPtr hKey, string lpFile, IntPtr lpSecurityAttributes);

Upvotes: 0

Views: 3538

Answers (2)

David Heffernan
David Heffernan

Reputation: 613451

First of all I would like to comment that your question misses some very important details. Namely that you do not actually give any information as to how your code is failing. All you say is:

I've been trying to P/Invoke RegSaveKey to no avail.

Questions like this one are all about the details. We need to see precise and detailed error diagnostics. Remember that we cannot see your screen.

You need to pay more attention to the return values from API calls. You are checking the return value of RegOpenKeyEx against zero, but that's all. You need to capture and inspect the value returned by RegOpenKeyEx. That's an error code. You can throw a Win32Exception if the error code is not equal to zero.

int err = RegOpenKeyEx(...);
if (err != 0)
    throw new Win32Exception(err);

Change the code to throw Win32Exception and you will at least get some informative text when the error occurs. So, if RegOpenKeyEx is failing, you will at least find out why.

And you aren't checking the other API calls at all. Give them the same treatment.

There are really very few failure modes for your call to RegOpenKeyEx. The only plausible explanation that I can concoct is that the registry key does not exist. I expect that you already checked that. But watch out for the registry redirector. If your process is a 32 bit process running on 64 bit OS, then the redirector will take you to the WOW6432Node section of the registry, the 32 bit view. Perhaps what you mean by "to no avail" is that information is saved to the file, but it's the wrong information. That would be consistent with the registry redirector confusing you.

If that's what is biting you then include the KEY_WOW64_64KEY flag when calling RegOpenKeyEx. Or target x64.

It would be a lot easier to use RegistryKey to open the key. I understand that you can't readily call RegSaveKey so you still need to p/invoke that. But RegistryKey exposes a Handle property that you can pass to RegSaveKey. There's one proviso to this. If you do need to use the KEY_WOW64_64KEY flag then that requires the .net 4 and RegistryView.

As for RegSaveKey, I defer to the information given by Stephen in his answer.

Upvotes: 1

Stephen Martin
Stephen Martin

Reputation: 9645

You don't appear to be checking the return value of either RegOpenKeyEx or RegSaveKey for errors. I suspect that you are getting a return value of ERROR_PRIVILEGE_NOT_HELD. The Registry functions do not use SetLastError they return it directly. And as with virtually every Win32 API call the return value must be checked.

RegSaveKey requires the SE_BACKUP_NAME privilege to be enabled regardless of your ACL access level. So you'll need to add code to enable this privilege before the call to RegSaveKey and then disable it after the call is complete.

Here's another question with more info and I'm sure there are others.

Upvotes: 2

Related Questions