PowerShellで最終書き込み時刻/最終変更時刻でレジストリエントリを並べ替える方法

Dec 14 2020

私はPowerShellを学んでおり、Windows 10 20H2でPSCore7.1を使用しています。現在、これを行うことができます。

Get-ChildItem -Path "C:\" -Directory -Recurse -Depth 5 | Where-Object{$_.LastWriteTime.ToString("yyyy-MM-dd") -eq "2020-12-14"} | Sort-Object LastWriteTime

指定されたディレクトリ(この場合はC :)の指定された深さ(この場合は5)内のサブフォルダーを再帰的に取得するには、指定された時間範囲(この場合は今日または2020年12月14日)で変更されたオブジェクトを検索し、最後にタイムスタンプによる結果。

しかし、get-childitemを実行してレジストリを表示すると、次のようになります。

Get-ChildItem -Path "HKLM:\SOFTWARE"

名前とプロパティの2つの項目のみがあり、タイムスタンプはありません。また、regedit.exeにはタイムスタンプもありません。PowerShellを使用して上記で投稿したコマンドのように、レジストリキーを最終変更時刻で並べ替えるにはどうすればよいですか。

編集:スーパーユーザーでここを読みました。レジストリエディタでレジストリキーをtxtファイルにエクスポートしてタイムスタンプを表示できます。簡単ですが、コンソールでタイムスタンプでレジストリキーを並べ替えたいので、この質問とは関係ありません。

再編集:

私はこのコマンドを実行しました:

get-childitem -path "HKLM:\"  | Get-Member

そしてここに結果があります:

   TypeName: Microsoft.Win32.RegistryKey

Name                      MemberType   Definition
----                      ----------   ----------
Close                     Method       void Close()
CreateSubKey              Method       Microsoft.Win32.RegistryKey CreateSubKey(string subkey), Microsoft.Win32.RegistryKey CreateSubKey(string subkey, bool writable…
DeleteSubKey              Method       void DeleteSubKey(string subkey), void DeleteSubKey(string subkey, bool throwOnMissingSubKey)
DeleteSubKeyTree          Method       void DeleteSubKeyTree(string subkey), void DeleteSubKeyTree(string subkey, bool throwOnMissingSubKey)
DeleteValue               Method       void DeleteValue(string name), void DeleteValue(string name, bool throwOnMissingValue)
Dispose                   Method       void Dispose(), void IDisposable.Dispose()
Equals                    Method       bool Equals(System.Object obj)
Flush                     Method       void Flush()
GetAccessControl          Method       System.Security.AccessControl.RegistrySecurity GetAccessControl(), System.Security.AccessControl.RegistrySecurity GetAccessCon…
GetHashCode               Method       int GetHashCode()
GetLifetimeService        Method       System.Object GetLifetimeService()
GetSubKeyNames            Method       string[] GetSubKeyNames()
GetType                   Method       type GetType()
GetValue                  Method       System.Object GetValue(string name), System.Object GetValue(string name, System.Object defaultValue), System.Object GetValue(s…
GetValueKind              Method       Microsoft.Win32.RegistryValueKind GetValueKind(string name)
GetValueNames             Method       string[] GetValueNames()
InitializeLifetimeService Method       System.Object InitializeLifetimeService()
OpenSubKey                Method       Microsoft.Win32.RegistryKey OpenSubKey(string name), Microsoft.Win32.RegistryKey OpenSubKey(string name, bool writable), Micro…
SetAccessControl          Method       void SetAccessControl(System.Security.AccessControl.RegistrySecurity registrySecurity)
SetValue                  Method       void SetValue(string name, System.Object value), void SetValue(string name, System.Object value, Microsoft.Win32.RegistryValue…
ToString                  Method       string ToString()
Property                  NoteProperty string[] Property=System.String[]
PSChildName               NoteProperty string PSChildName=BCD00000000
PSDrive                   NoteProperty PSDriveInfo PSDrive=HKLM
PSIsContainer             NoteProperty bool PSIsContainer=True
PSParentPath              NoteProperty string PSParentPath=Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE
PSPath                    NoteProperty string PSPath=Microsoft.PowerShell.Core\Registry::HKEY_LOCAL_MACHINE\BCD00000000
PSProvider                NoteProperty ProviderInfo PSProvider=Microsoft.PowerShell.Core\Registry
Handle                    Property     Microsoft.Win32.SafeHandles.SafeRegistryHandle Handle {get;}
Name                      Property     string Name {get;}
SubKeyCount               Property     int SubKeyCount {get;}
ValueCount                Property     int ValueCount {get;}
View                      Property     Microsoft.Win32.RegistryView View {get;}

ご覧のとおり、タイムスタンプはありません...

引用:Windowsレジストリキー/値の変更日を表示する方法はありますか?

証明:

Key Name:          HKEY_CLASSES_ROOT\._bsln140
Class Name:        <NO CLASS>
Last Write Time:   2020-12-11 - 14:54
Value 0
  Name:            <NO NAME>
  Type:            REG_SZ
  Data:            VisualStudio.Launcher._bsln140

回答

2 user19702 Dec 14 2020 at 16:21

残念ながら、これはWindowsがレジストリキー情報にアクセスする方法のために複雑になる可能性がありますが、それは可能です。以下は、MicrosoftdevblogのDrScriptoによる一連のチュートリアルです。

  • PowerShellを使用してレジストリの最終変更タイムスタンプにアクセスするには、PowerShellでWin32関数RegQueryInfoKeyを使用してタイムスタンプを取得する方法について説明します。
  • PowerShellレジストリのタイムスタンプコードの再利用では、そのコードをWindowsPowerShell関数でラップして再利用可能なツールを作成します。
  • PowerShellを介してレジストリキーのタイムスタンプを活用すると、実際の使用法がカバーされます。

最終的な製品は次のようになり、Add-RegKeyLastWriteTime.ps1として保存されます。

#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 $_)"
        }
    }
}
}

そして最後に、使用例:

Get-ChildItem HKCU:\ | Add-RegKeyLastWriteTime | Select Name,LastWriteTime
'
Name                                                         LastWriteTime         
----                                                         -------------         
HKEY_CURRENT_USER\AppEvents                                  7/6/2020 8:56:11 AM   
HKEY_CURRENT_USER\Console                                    7/6/2020 8:56:11 AM   
HKEY_CURRENT_USER\Control Panel                              7/6/2020 1:04:53 PM  
'