{"id":2315,"date":"2013-12-31T00:01:00","date_gmt":"2013-12-31T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2013\/12\/31\/reusing-powershell-registry-time-stamp-code\/"},"modified":"2013-12-31T00:01:00","modified_gmt":"2013-12-31T00:01:00","slug":"reusing-powershell-registry-time-stamp-code","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/reusing-powershell-registry-time-stamp-code\/","title":{"rendered":"Reusing PowerShell Registry Time Stamp Code"},"content":{"rendered":"<p><b>Summary<\/b>: Guest blogger, Rohn Edwards, discusses reusing code that obtains the last-modified time stamp from the registry.<\/p>\n<p>Microsoft Scripting Guy, Ed Wilson, is here. Today is the last day of the year, but don&rsquo;t despair. We are here to share some Windows PowerShell goodies with you. &nbsp;Welcome back Rohn Edwards as our guest blogger this week&#8230;<\/p>\n<p>In yesterday&rsquo;s post, <a href=\"https:\/\/devblogs.microsoft.com\/scripting\/use-powershell-to-access-registry-last-modified-time-stamp\/\" target=\"_blank\">Use PowerShell to Access Registry Last-Modified Time Stamp<\/a>, we covered how to use Windows PowerShell and P\/Invoke to get information from registry keys that isn&rsquo;t normally available to WMI and the .NET Framework. We covered how to build a C# signature for the <b>RegQueryInfoKey<\/b> Win32 function, and how to use that signature from Windows PowerShell.<\/p>\n<p>That wasn&rsquo;t a full solution, though, because using the function directly requires a ton of work. You have to create empty variables and pass a very large number of parameters to the function. Today we&rsquo;re going to create a reusable tool by wrapping that code in a Windows PowerShell function.<\/p>\n<p>First we have to come up with a name for the function, and to do that properly, we need to know what it&rsquo;s going to do. I plan to make it take a <b>RegistryKey<\/b> object (returned from <b>Get-Item<\/b> or <b>Get-ChildItem<\/b>) or a string to a registry path (which we would pass to <b>Get-Item<\/b> to get a <b>RegistryKey<\/b> object), get the extra key information by using the P\/Invoke function, and then add the information as extra <b>NoteProperties<\/b> on the <b>RegistryKey<\/b> object. Because we&rsquo;ll be adding properties, we&rsquo;re going to name the function <b>Add-RegKeyMember<\/b>. With these requirements, let&rsquo;s start with the following function declaration:<\/p>\n<p style=\"margin-left:30px\">#requires -version 3.0<\/p>\n<p style=\"margin-left:30px\">function Add-RegKeyMember {<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [CmdletBinding()]<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; param(<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Parameter(Mandatory, ParameterSetName=&quot;ByKey&quot;, Position=0, ValueFromPipeline)]<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Registry key object returned from Get-ChildItem or Get-Item<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Microsoft.Win32.RegistryKey] $RegistryKey,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [Parameter(Mandatory, ParameterSetName=&quot;ByPath&quot;, Position=0)]<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Path to a registry key<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [string] $Path<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; begin {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; process {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>First, we have a <b>#requires<\/b> statement because we&rsquo;re using some features that require Windows PowerShell&nbsp;3.0. Next, we declare the function and its parameters. Right now, there are only two parameters:<\/p>\n<p><b>$RegistryKey<\/b> &nbsp; This is an object that is returned from <b>Get-ChildItem<\/b> or <b>Get-Item<\/b> when a registry path has been passed.<\/p>\n<p><b>$Path<\/b> &nbsp; This is a string that represents a registry path, for example, HKLM:\\Software. This path will be passed to <b>Get-Item<\/b>, and the <b>RegistryKey<\/b> object that is returned will be used.<\/p>\n<p>Both parameters are mandatory, but they are in different parameter sets. This means that you must have one or the other, but not both. By creating the <b>param()<\/b> block, we&rsquo;ve told Windows PowerShell how to do a good bit of parameter validation.&nbsp;<\/p>\n<p>This is going to be an <a href=\"http:\/\/technet.microsoft.com\/en-us\/library\/hh847806.aspx\" target=\"_blank\">advanced function<\/a> that will accept pipeline input, so we create <b>begin<\/b> and <b>process<\/b> blocks. When the function is used in a pipeline, the <b>begin<\/b> block will run once, and the <b>process<\/b> block will run one time for each input that is sent to it. When it&rsquo;s not used in a pipeline, both blocks will run once.<\/p>\n<p>We&rsquo;re going to take the <b>Add-Type<\/b> call from yesterday and put that in the <b>begin<\/b> block (if the custom type has already been loaded in the current PS session, it won&rsquo;t be reloaded again). We&rsquo;ll also use the <b>begin<\/b> block to store our type in the <b>$RegTools<\/b> variable:<\/p>\n<p style=\"margin-left:30px\">begin {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; # Define the namespace (string array creates nested namespace):<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $Namespace = &quot;HeyScriptingGuy&quot;<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; # Make sure type is loaded (this will only get loaded on first run):<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; Add-Type @&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using System;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using System.Text;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using System.Runtime.InteropServices;<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $($Namespace | ForEach-Object {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;namespace $_ {&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; })<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public class advapi32 {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [DllImport(&quot;advapi32.dll&quot;, CharSet = CharSet.Auto)]<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public static extern Int32 RegQueryInfoKey(<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Microsoft.Win32.SafeHandles.SafeRegistryHandle hKey,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StringBuilder lpClass,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [In, Out] ref UInt32 lpcbClass,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UInt32 lpReserved,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out UInt32 lpcSubKeys,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out UInt32 lpcbMaxSubKeyLen,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out UInt32 lpcbMaxClassLen,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out UInt32 lpcValues,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;out UInt32 lpcbMaxValueNameLen,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out UInt32 lpcbMaxValueLen,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; out UInt32 lpcbSecurityDescriptor,<\/p>\n<p style=\"margin-left:30px\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;&nbsp;<span>out System.Runtime.InteropServices.ComTypes.FILETIME lpftLastWriteTime<\/span><\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; );<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $($Namespace | ForEach-Object { &quot;}&quot; })<\/p>\n<p style=\"margin-left:30px\">&quot;@<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; # Get a shortcut to the type:&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $RegTools = (&quot;{0}.advapi32&quot; -f ($Namespace -join &quot;.&quot;)) -as [type]<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>Next, it&rsquo;s time to work on the <strong>process<\/strong> block:<\/p>\n<p style=\"margin-left:30px\">process {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; switch ($PSCmdlet.ParameterSetName) {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;ByKey&quot; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;# Already have the key, no more work to be done \ud83d\ude42<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &quot;ByPath&quot; {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # We need a RegistryKey object (Get-Item should return that)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $Item = Get-Item -Path $Path -ErrorAction Stop<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Make sure this is of type [Microsoft.Win32.RegistryKey]<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if ($Item -isnot [Microsoft.Win32.RegistryKey]) {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw &quot;&#039;$Path&#039; is not a path to a registry key!&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $RegistryKey = $Item<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; # Initialize variables that will be populated:<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $ClassLength = 255 # Buffer size (class name is rarely used, and when it is, I&#039;ve never seen<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # it more than 8 characters. Buffer can be increased here, though.<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; $ClassName = New-Object System.Text.StringBuilder $ClassLength&nbsp; # Will hold the class name<\/p>\n<p style=\"margin-left:30px\">&nbsp; &nbsp; $LastWriteTime = New-Object System.Runtime.InteropServices.ComTypes.FILETIME&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; switch ($RegTools::RegQueryInfoKey($RegistryKey.Handle,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $ClassName,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;[ref] $ClassLength,<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $null,&nbsp; # Reserved<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ref] $null, # SubKeyCount<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ref] $null, # MaxSubKeyNameLength<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ref] $null, # MaxClassLength<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ref] $null, # ValueCount<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ref] $null, # MaxValueNameLength<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ref] $null, # MaxValueValueLength<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ref] $null, # SecurityDescriptorSize<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; [ref] $LastWriteTime<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; )) {<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 0 { # Success<\/p>\n<p style=\"margin-left:30px\">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; # Convert to DateTime object:<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $UnsignedLow = [System.BitConverter]::ToUInt32([System.BitConverter]::GetBytes($LastWriteTime.dwLowDateTime), 0)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $UnsignedHigh = [System.BitConverter]::ToUInt32([System.BitConverter]::GetBytes($LastWriteTime.dwHighDateTime), 0)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Shift high part so it is most significant 32 bits, then copy low part into 64-bit int:<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $FileTimeInt64 = ([Int64] $UnsignedHigh -shl 32) -bor $UnsignedLow<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Create datetime object<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $LastWriteTime = [datetime]::FromFileTime($FileTimeInt64)<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # Add properties to object and output them to pipeline<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; $RegistryKey | Add-Member -NotePropertyMembers @{<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; LastWriteTime = $LastWriteTime<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ClassName = $ClassName.ToString()<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; } -PassThru -Force<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;122&nbsp; { # ERROR_INSUFFICIENT_BUFFER (0x7a)<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw &quot;Class name buffer too small&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # function could be recalled with a larger buffer, but for<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; # now, just exit<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p>&nbsp;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; default {<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; throw &quot;Unknown error encountered (error code $_)&quot;<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">&nbsp;&nbsp;&nbsp; }<\/p>\n<p style=\"margin-left:30px\">}<\/p>\n<p>What the process block does is very simple:<\/p>\n<p>First, it checks to see if a <b>RegistryKey<\/b> object or a path was passed to the function. If it was a <b>RegistryKey<\/b> object, it moves along without doing anything else. If it was a path, it calls <b>Get-Item<\/b> to get a <b>RegistryKey<\/b> object.<\/p>\n<p>Next, it calls the same code from yesterday&rsquo;s post. We&rsquo;re having it get the class name and the last write time (registry keys with class names are very rare, but there are a few). The only thing extra is that the return code from <b>RegQueryInfoKey<\/b> is being captured in a switch statement. If the result is 0, the call was successful, and the extra info is added to the original <b>RegistryKey<\/b> object by using <b>Add-Member<\/b>.<\/p>\n<p>The only other known return code in the switch statement is <b>122<\/b>, which means that the class name buffer was too small. Any other errors generate a generic termination error. If you ever come across a non-zero error code, you can easily add it to the switch block to give your user a specific error message.<\/p>\n<p>Here is an example of using it:<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-12-31-13-1.png\"><img decoding=\"async\" alt=\" \" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/hsg-12-31-13-1.png\" border=\"0\" \/><\/a><\/p>\n<p>That&rsquo;s it for today. Tomorrow, we&rsquo;re going to create a proxy function for <b>Get-ChildItem<\/b> so that it automatically gets this information.<\/p>\n<p>~Rohn<\/p>\n<p>Thank you, Rohn. Happy New Year&rsquo;s Eve.<\/p>\n<p>I invite you to follow me on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> and <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send email to me at <a href=\"mailto:scripter@microsoft.com\" target=\"_blank\">scripter@microsoft.com<\/a>, or post your questions on the <a href=\"http:\/\/bit.ly\/scriptingforum\" target=\"_blank\">Official Scripting Guys Forum<\/a>. See you tomorrow. Until then, peace.<\/p>\n<p><b>Ed Wilson, Microsoft Scripting Guy<\/b>&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Summary: Guest blogger, Rohn Edwards, discusses reusing code that obtains the last-modified time stamp from the registry. Microsoft Scripting Guy, Ed Wilson, is here. Today is the last day of the year, but don&rsquo;t despair. We are here to share some Windows PowerShell goodies with you. &nbsp;Welcome back Rohn Edwards as our guest blogger this [&hellip;]<\/p>\n","protected":false},"author":596,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[56,31,26,342,3,45],"class_list":["post-2315","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-guest-blogger","tag-operating-system","tag-registry","tag-rohn-edwards","tag-scripting-guy","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>Summary: Guest blogger, Rohn Edwards, discusses reusing code that obtains the last-modified time stamp from the registry. Microsoft Scripting Guy, Ed Wilson, is here. Today is the last day of the year, but don&rsquo;t despair. We are here to share some Windows PowerShell goodies with you. &nbsp;Welcome back Rohn Edwards as our guest blogger this [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2315","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/users\/596"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=2315"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/2315\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media\/87096"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/media?parent=2315"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=2315"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=2315"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}