One can view Windows Registry key’s LastWriteTime
by exporting key as TXT using Registry Editor:
Key Name: HKEY_LOCAL_MACHINESOFTWAREMicrosoftWindowsCurrentVersionAudio
Class Name: <NO NAME>
Last Write Time: 16.5.22 - 14:05
Value 0
Name: EnableCaptureMonitor
Type: REG_DWORD
Data: 0x1
Since there’s no built-in LastWriteTime
property for keys in PowerShell (as of $psversiontable.psversion
5), one has to use Add-RegKeyLastWriteTime.ps1
(kindly put together by @Cpt.Whale from Dr Scripto’s articles 1, 2, 3, 4, original scripts) to retrieve LastWriteTime
value:
#requires -version 3.0
function Add-RegKeyLastWriteTime {
[CmdletBinding()]
param(
[Parameter(Mandatory, ParameterSetName="ByKey", Position=0, ValueFromPipeline)]
# Registry key object returned from Get-ChildItem or Get-Item
[Microsoft.Win32.RegistryKey] $RegistryKey,
[Parameter(Mandatory, ParameterSetName="ByPath", Position=0)]
# Path to a registry key
[string] $Path
)
begin {
# Define the namespace (string array creates nested namespace):
$Namespace = "HeyScriptingGuy"
# Make sure type is loaded (this will only get loaded on first run):
Add-Type @"
using System;
using System.Text;
using System.Runtime.InteropServices;
$($Namespace | ForEach-Object {
"namespace $_ {"
})
public class advapi32 {
[DllImport("advapi32.dll", CharSet = CharSet.Auto)]
public static extern Int32 RegQueryInfoKey(
Microsoft.Win32.SafeHandles.SafeRegistryHandle hKey,
StringBuilder lpClass,
[In, Out] ref UInt32 lpcbClass,
UInt32 lpReserved,
out UInt32 lpcSubKeys,
out UInt32 lpcbMaxSubKeyLen,
out UInt32 lpcbMaxClassLen,
out UInt32 lpcValues,
out UInt32 lpcbMaxValueNameLen,
out UInt32 lpcbMaxValueLen,
out UInt32 lpcbSecurityDescriptor,
out System.Runtime.InteropServices.ComTypes.FILETIME lpftLastWriteTime
);
}
$($Namespace | ForEach-Object { "}" })
"@
# Get a shortcut to the type:
$RegTools = ("{0}.advapi32" -f ($Namespace -join ".")) -as [type]
}
process {
switch ($PSCmdlet.ParameterSetName) {
"ByKey" {
# Already have the key, no more work to be done 🙂
}
"ByPath" {
# We need a RegistryKey object (Get-Item should return that)
$Item = Get-Item -Path $Path -ErrorAction Stop
# Make sure this is of type [Microsoft.Win32.RegistryKey]
if ($Item -isnot [Microsoft.Win32.RegistryKey]) {
throw "'$Path' is not a path to a registry key!"
}
$RegistryKey = $Item
}
}
# Initialize variables that will be populated:
$ClassLength = 255 # Buffer size (class name is rarely used, and when it is, I've never seen
# it more than 8 characters. Buffer can be increased here, though.
$ClassName = New-Object System.Text.StringBuilder $ClassLength # Will hold the class name
$LastWriteTime = New-Object System.Runtime.InteropServices.ComTypes.FILETIME
switch ($RegTools::RegQueryInfoKey($RegistryKey.Handle,
$ClassName,
[ref] $ClassLength,
$null, # Reserved
[ref] $null, # SubKeyCount
[ref] $null, # MaxSubKeyNameLength
[ref] $null, # MaxClassLength
[ref] $null, # ValueCount
[ref] $null, # MaxValueNameLength
[ref] $null, # MaxValueValueLength
[ref] $null, # SecurityDescriptorSize
[ref] $LastWriteTime
)) {
0 { # Success
# Convert to DateTime object:
$UnsignedLow = [System.BitConverter]::ToUInt32([System.BitConverter]::GetBytes($LastWriteTime.dwLowDateTime), 0)
$UnsignedHigh = [System.BitConverter]::ToUInt32([System.BitConverter]::GetBytes($LastWriteTime.dwHighDateTime), 0)
# Shift high part so it is most significant 32 bits, then copy low part into 64-bit int:
$FileTimeInt64 = ([Int64] $UnsignedHigh -shl 32) -bor $UnsignedLow
# Create datetime object
$LastWriteTime = [datetime]::FromFileTime($FileTimeInt64)
# Add properties to object and output them to pipeline
$RegistryKey | Add-Member -NotePropertyMembers @{
LastWriteTime = $LastWriteTime
ClassName = $ClassName.ToString()
} -PassThru -Force
}
122 { # ERROR_INSUFFICIENT_BUFFER (0x7a)
throw "Class name buffer too small"
# function could be recalled with a larger buffer, but for
# now, just exit
}
default {
throw "Unknown error encountered (error code $_)"
}
}
}
}
Add-RegKeyLastWriteTime.ps1 usage example:
Get-ChildItem HKCU: | Add-RegKeyLastWriteTime | Select Name,LastWriteTime
'
Name LastWriteTime
---- -------------
HKEY_CURRENT_USERAppEvents 7/6/2020 8:56:11 AM
HKEY_CURRENT_USERConsole 7/6/2020 8:56:11 AM
HKEY_CURRENT_USERControl Panel 7/6/2020 1:04:53 PM
'
I’d like to set LastWriteTime
property to all keys recursively (i.e., to 2020/01/01 00:00:00 UTC
on offline image). Is that possible to achieve by utilizing Add-RegKeyLastWriteTime.ps1
methods?
P.S. There’s an AutoIt script SetRegTime that does that in Windows 7 but the recursion is not working in Windows 10 (tested in version 1607, 1809, 21H2 as TrustedInstaller
, System
and Administrator
accounts so it’s not a permissions issue) and the developer Joakim Schicht appears to be not active anymore. If there’s another software solution that can set last write time to Windows Registry keys recursively, please let me know since I couldn’t find any.