1

I am trying to insert a registry entry with a PowerShell script using C#. This is my code:

public void ExecuteCommand(string script)
{
    Runspace runspace = RunspaceFactory.CreateRunspace();
    runspace.Open();
            
    Pipeline pipeline = runspace.CreatePipeline();
    pipeline.Commands.AddScript(script);
    pipeline.Invoke();
    runspace.Close();
}

static void Main(string[] args)
{
    ExecuteCommand("New-ItemProperty -Path \"HKLM:\\SOFTWARE\\OpenSSH\" -Name DefaultShell -Value \"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -PropertyType String -Force");
}

The script works properly when I execute it by myself using Powershell but it doesn't work when I try to execute it like this.

How can I fix this problem?

Reply to JosefZ's Comment

The code block below also doesn't work.

public void ExecuteCommand()
{
    Runspace runspace = RunspaceFactory.CreateRunspace();
    runspace.Open();

    Pipeline pipeline = runspace.CreatePipeline();
    Command cmd = new Command("New-ItemProperty");
    cmd.Parameters.Add("Path", @"HKLM:\SOFTWARE\OpenSSH");
    cmd.Parameters.Add("Name", "DefaultShell");
    cmd.Parameters.Add("Value", @"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe");
    cmd.Parameters.Add("PropertyType", "String");
    cmd.Parameters.Add("Force");
    pipeline.Commands.Add(cmd);
    pipeline.Invoke();
}

Reply to mklement0's Comment

I added -ErrorAction Stop to the script to find where the error occurs, and I found it. The error is Cannot find path 'HKLM:\SOFTWARE\OpenSSH' because it does not exist. but the path exists. I also started the program as administrator.

regedit screenshot

I also got an error when I try to set ExecutionPolicy. The error is Unhandled Exception: System.NullReferenceException: Object reference not set to an instance of an object.. This is the new code block I tried:

public void Execute()
{
    using (var runspace = RunspaceFactory.CreateRunspace())
    {
        // Causes Error
        // runspace.InitialSessionState.ExecutionPolicy = Microsoft.PowerShell.ExecutionPolicy.RemoteSigned;
        runspace.Open();
        Pipeline pipeline = runspace.CreatePipeline();
        pipeline.Commands.AddScript("New-ItemProperty -Path \"HKLM:\\SOFTWARE\\OpenSSH\" -Name DefaultShell -Value \"C:\\Windows\\System32\\WindowsPowerShell\\v1.0\\powershell.exe\" -PropertyType String -Force -ErrorAction Stop");
        pipeline.Invoke();
    }
}
mklement0
  • 382,024
  • 64
  • 607
  • 775
BurakBey
  • 55
  • 5
  • https://stackoverflow.com/questions/17067971/invoking-powershell-cmdlets-from-c-sharp or https://stackoverflow.com/questions/21264112/how-to-call-powershell-cmdlets-from-c-sharp-in-visual-studio? – JosefZ Dec 24 '22 at 20:50
  • Please elaborate on "doesn't work". How do you determine the code doesn't work? Do you get any error message? When registry key "HKLM\Software" is involved, there could be issues related to 32/64-bit code. 32-bit code will actually write at "HKLM\Software\Wow6432Node". Have you looked under this key? – zett42 Dec 24 '22 at 21:44
  • When I say "doesn't work" I mean the registry wasn't created. The registry creates when you execute the script from powershell window manually but it doesn't create from the C# code. I checked this key but there aren't any issue. – BurakBey Dec 24 '22 at 21:56
  • 1
    Modifying data in the `HKLM:` (`HKEY_LOCAL_MACHINE`) registry hive requires _elevation_, i.e. running with elevated privileges ("Run As Administrator"). If you neglect to do that, given that you're using `.AddScript()`, the error won't surface by default - see [this answer](https://stackoverflow.com/a/69051991/45375). – mklement0 Dec 24 '22 at 22:44
  • 2
    Re your edits, it's propably a 32/64-bit issue. You are likely creating a 32-bit runspace which actually tries to write registry value at `HKLM:\SOFTWARE\Wow6432Node\OpenSSH` because of WOW64 emulation. Question: Why even bother with PowerShell when you could use .NET [registry classes](https://learn.microsoft.com/en-us/dotnet/api/microsoft.win32.registry?view=net-7.0)? This makes it much easier to explicitly [access the 64-bit registry view](https://stackoverflow.com/a/13232372/7571258), regardless of the bitness of your application. – zett42 Dec 25 '22 at 12:33

1 Answers1

1

Thanks to zett42, I decided to use .NET registry classes to solve this problem, and the code block below works how I expected.

public void InsertEntry()
{
    RegistryKey localKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64).OpenSubKey(@"SOFTWARE\OpenSSH", true);

    if (localKey != null)
    {
        localKey.SetValue("DefaultShell", @"C:\Windows\System32\WindowsPowerShell\v1.0\powershell.exe");
        localKey.Close();
        Console.WriteLine("Successful.");
    }
    else
    {
        Console.WriteLine("Null.");
    }
}
BurakBey
  • 55
  • 5
  • Nice - that is definitely the best solution in your case. Using PowerShell commands, you cannot directly access the 64-bit registry from a 32-bit process; expensive alternative solutions include calling via the 64-bit PowerShell CLI or via `reg.exe /reg:64` - see [this answer](https://stackoverflow.com/a/53687594/45375) for details. – mklement0 Dec 25 '22 at 15:56