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”.
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.