Search code examples
powershellregistry

reg unload and new key


I can run these commands and everything is as expected

reg load HKU\Kayla C:\Users\Kayla\ntuser.dat
New-Item -Force Registry::HKU\Kayla\Foo

However running this after causes an error

PS > reg unload HKU\Kayla
ERROR: Access is denied.

If I manually open up the Registry Editor I can unload the hive, but I would like to unload from a script if possible.

Update: after reading Matt’s answer I found it to work if you run a command before collecting, example

0
[gc]::collect()

It appears the 0 acts as "Recycle Bin" and collect is the “permanent delete”.


Solution

  • Edit 2015-04-07: You may need to wait for pending finalizers after forcing garbage collection if you're hosting PowerShell and running in Debug mode. More below.


    The problem is that New-Item creates a handle to the registry key and leaves it open, and you've got to manually close that handle before the GC call can clean it up if it's all running in the same script. (You can see the open handle with Process Explorer's "Find -> Find Handle or DLL..." function; search for your key name in there.)

    Fortunately, the result of New-Item gives you easy access to that handle:

    $result = New-Item # ...
    $result.Handle.Close()
    

    Now you can [gc]::Collect() to clean up the handle and reg unload.

    In a custom PowerShell host running in Debug mode, because of differences in GC between Debug and Release modes, you may have to follow this with a call to [gc]::WaitForPendingFinalizers(), but please read the literature, as this can deadlock under certain conditions. In my testing, Release mode works without waiting for pending finalizers.

    Full working example for SOME_USER:

    $path = "HKLM:\TEMP_hive\newkey" # Key we're going to create.
    reg load HKLM\TEMP_hive C:\Users\SOME_USER\NTUSER.DAT
    $result = New-Item -Path $path
    $result.Handle.Close()
    [gc]::Collect()
    [gc]::WaitForPendingFinalizers() # Optional, and beware of deadlocks! Only seen this needed in Debug mode.
    reg unload HKLM\TEMP_hive
    

    Note that you do have to be an administrator and running elevated.

    Details:

    In my testing, [gc]::Collect() is unable clean up the open handle as long as the same script is running that called New-Item. Interestingly, that means in an interactive context like PowerShell ISE, if control returns to the prompt before the [gc]::Collect(), it has the same effect and the handle becomes fair game for the collector without even closing it. In PowerShell ISE, run:

    $path = "HKLM:\TEMP_hive\differentnewkey" # Key we're going to create.
    reg load HKLM\TEMP_hive C:\Users\SOME_USER\NTUSER.DAT
    New-Item -Path $path
    

    Then run:

    [gc]::Collect()
    reg unload HKLM\TEMP_hive
    

    reg unload always succeeds in this context too.

    Notice in that last example I no longer assign the return result of New-Item to a variable like $result, because if you do that, you'll still have that handle kicking around as long as $result is in the environment at your prompt, and reg unload will fail again. Try it:

    $path = "HKLM:\TEMP_hive\thirdnewkey" # Key we're going to create.
    reg load HKLM\TEMP_hive C:\Users\SOME_USER\NTUSER.DAT
    $result = New-Item -Path $path
    

    Then run:

    [gc]::Collect()
    reg unload HKLM\TEMP_hive
    

    Doesn't work until you call $result.Handle.Close().

    Also, despite posts around the internet suggesting that there's a timing issue going on, throwing a sleep in there shouldn't change anything (unless you're in the custom host + Debug situation above, in which case I'd recommend waiting on finalizers) — it's all about if that handle is eligible for garbage collection. Would be extremely interested if anyone can explain at a deeper level just what's going on with that handle and the garbage collector.