Steven Penny Steven Penny - 4 months ago 21
PowerShell Question

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
collect
ing, example

0
[gc]::collect()


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

Answer

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.