In my case, I wanted to use the Registry value at SYSTEM\CurrentControlSet\Control\Session Manager!PendingFileRenameOperations to delete a file on reboot. I am using PowerShell.
It would be relatively easy to use something like MoveFile or any other command-line binary to add the data to the Registry, but I consider it poor form to use third-party tools (even Sysinternals) when native methods are available.
The ProblemPendingFileRenameOperations only does anything when the entries within it (it is a Multistring object) are suffixed with two NUL characters, shown in Regedit's binary viewer as .. ... Adding the information normally via PowerShell doesn't work and adding binary data, while possible, would clear out any existing data in the value owing to PowerShell's (certainly PowerShell 2.0's) lack of ability to pull a Registry MultiString object's binary data.
The Question
Is it possible to use native methods on a PS2.0 machine to add entries to PendingFileRenameOperations in such a way that the OS actually acknowledges them?
2 Answers
Yes. Use the following PowerShell code:
new-ItemProperty -path "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name "PendingFileRenameOperations" -Value $($((Get-ItemProperty "HKLM:\SYSTEM\CurrentControlSet\Control\Session Manager" -Name PendingFileRenameOperations -ErrorAction SilentlyContinue).PendingFileRenameOperations) + "\??\C:\file.exe`0`0") -type MultiString -Force | Out-NullWhat we're doing here:
- Making a new item of MultiString type (using
set-itempropertydoesn't work for whatever reason) - Setting, as its primary value, the data that was in that Registry value to begin with (if anything)
- Adding the normal
\??\C:\File.exe, which in this case is\??\+ the file path of the file to delete - Adding two NUL bytes (
`0`0in PowerShell) to the end of the string
This will ensure the file is deleted on next reboot.
You could use .NET from Powershell, for example:
Add-Type @"
using System;
using System.Text;
using System.Runtime.InteropServices;
public class PFRO
{ public enum MoveFileFlags { MOVEFILE_REPLACE_EXISTING = 0x00000001, MOVEFILE_COPY_ALLOWED = 0x00000002, MOVEFILE_DELAY_UNTIL_REBOOT = 0x00000004, MOVEFILE_WRITE_THROUGH = 0x00000008, MOVEFILE_CREATE_HARDLINK = 0x00000010, MOVEFILE_FAIL_IF_NOT_TRACKABLE = 0x00000020 } [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] static extern bool MoveFileEx(string lpExistingFileName, string lpNewFileName, MoveFileFlags dwFlags); public static bool MarkFileForDelete (string srcfile) { bool brc = false; brc = MoveFileEx(srcfile, null, MoveFileFlags.MOVEFILE_DELAY_UNTIL_REBOOT); return brc; }
}
"@
$FullName = "C:\toolsde.txt"
if([PFRO]::MarkFileForDelete($FullName))
{ write-host $FullName "will be deleted on next boot"
}
else
{ write-host $FullName "will not be deleted on next boot"
}Additionally, I believe PendingFileRenameOperations2 is also a registry value processed by the Windows Session Manager at boot. I suspect you could use that for your purposes without the need to touch PendingFileRenameOperations. Might be worth a test.
2