Retrieving a Registry Key LastWriteTime Using PowerShell

While navigating through the registry, you may have noticed that there is something missing from there that you normally see in other places such as the file system. That little thing is a timestamp that might tell you when a particular key has been modified. In this case, you can’t see it because it is not openly visible to be seen. In fact, the timestamp is only available on the registry key itself, not on the values within the key. If a value gets updated, removed or added under the key, the timestamp on the key gets updated.

In order to actually view this information you would have to export the registry key in question and make sure to save it as a txt file so that when you open it up, it would list the lastwritetime of the key for you to see.

image

Now wouldn’t be great if we could somehow use some sort of command line approach to retrieving the same timestamp? Well, with a little bit of work, we can accomplish this using PowerShell with some help from our friends with p/invoke and some reflection!

First let’s start off with getting the proper signature which happens to be RegQueryInfoKey from pinvoke.net

image

More information about this actual function can be found at the following link:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms724902%28v=vs.85%29.aspx

I will be making a small change to this with the last parameter: lpftLastWriteTime. Instead of an IntPtr I will be using long as the Ref so it will be easier to convert the value to a DateTime object.

With that, I am going to build out the signature using reflection. Unlike some of my other scripts that use this approach, I only have to build the RegQueryInfoKey signature and have no need to worry about any Enums or Structs.

 
#region Create Win32 API Object
Try {
    [void][advapi32]
} Catch {
    #region Module Builder
    $Domain = [AppDomain]::CurrentDomain
    $DynAssembly = New-Object System.Reflection.AssemblyName('RegAssembly')
    $AssemblyBuilder = $Domain.DefineDynamicAssembly($DynAssembly, [System.Reflection.Emit.AssemblyBuilderAccess]::Run) # Only run in memory
    $ModuleBuilder = $AssemblyBuilder.DefineDynamicModule('RegistryTimeStampModule', $False)
    #endregion Module Builder
 
    #region DllImport
    $TypeBuilder = $ModuleBuilder.DefineType('advapi32', 'Public, Class')
 
    #region RegQueryInfoKey Method
    $PInvokeMethod = $TypeBuilder.DefineMethod(
        'RegQueryInfoKey', #Method Name
        [Reflection.MethodAttributes] 'PrivateScope, Public, Static, HideBySig, PinvokeImpl', #Method Attributes
        [IntPtr], #Method Return Type
        [Type[]] @(
            [Microsoft.Win32.SafeHandles.SafeRegistryHandle], #Registry Handle
            [System.Text.StringBuilder], #Class Name
            [UInt32 ].MakeByRefType(),  #Class Length
            [UInt32], #Reserved
            [UInt32 ].MakeByRefType(), #Subkey Count
            [UInt32 ].MakeByRefType(), #Max Subkey Name Length
            [UInt32 ].MakeByRefType(), #Max Class Length
            [UInt32 ].MakeByRefType(), #Value Count
            [UInt32 ].MakeByRefType(), #Max Value Name Length
            [UInt32 ].MakeByRefType(), #Max Value Name Length
            [UInt32 ].MakeByRefType(), #Security Descriptor Size           
            [long].MakeByRefType() #LastWriteTime
        ) #Method Parameters
    )
 
    $DllImportConstructor = [Runtime.InteropServices.DllImportAttribute].GetConstructor(@([String]))
    $FieldArray = [Reflection.FieldInfo[]] @(       
        [Runtime.InteropServices.DllImportAttribute].GetField('EntryPoint'),
        [Runtime.InteropServices.DllImportAttribute].GetField('SetLastError')
    )
 
    $FieldValueArray = [Object[]] @(
        'RegQueryInfoKey', #CASE SENSITIVE!!
        $True
    )
 
    $SetLastErrorCustomAttribute = New-Object Reflection.Emit.CustomAttributeBuilder(
        $DllImportConstructor,
        @('advapi32.dll'),
        $FieldArray,
        $FieldValueArray
    )
 
    $PInvokeMethod.SetCustomAttribute($SetLastErrorCustomAttribute)
    #endregion RegQueryInfoKey Method
 
    [void]$TypeBuilder.CreateType()
    #endregion DllImport
}
#endregion Create Win32 API object

If you are interested in a more detailed explanation of this process, just check out one of my other articles with the reflection tag to learn more.

I can verify that I at least have everything compiled correctly by looking at the method.

 
[advapi32]::RegQueryInfoKey

image

At least this looks good. We now need to grab a registry key and then figure out what the timestamp is.

 
$RegistryKey = Get-Item 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\IniFileMapping\Autorun.inf'
$RegistryKey

image

What better key to use than the one that I showed in my previous example. Now I will create some common variables that will be used with the method.

 
#region Constant Variables
$ClassLength = 255
[long]$TimeStamp = $null
#endregion Constant Variables

With my registry key, I need the registry handle and the name that will be supplied to the method.

 
$ClassName = New-Object System.Text.StringBuilder $RegistryKey.Name
$RegistryHandle = $RegistryKey.Handle

I had to use a StringBuilder because it is required by one of the method parameters. Now that we have enough information, we can proceed with gathering the timestamp.

 
[advapi32]::RegQueryInfoKey(
    $RegistryHandle,
    $ClassName,
    [ref]$ClassLength,
    $Null,
    [ref]$Null,
    [ref]$Null,
    [ref]$Null,
    [ref]$Null,
    [ref]$Null,
    [ref]$Null,
    [ref]$Null,
    [ref]$TimeStamp
)

SNAGHTML5719a54

Most of the parameters here can be $Null, which is why they are that way. We know that this was successful by the return of the IntPtr 0. Any other value would mean that something happened and that would need to be investigated.

We are not quite done yet! We have the timestamp, but it is in a non-usable format and should be converted into a DateTime object using [datetime]::FromFileTime().

 
#Convert to DateTime Object
$LastWriteTime = [datetime]::FromFileTime($TimeStamp)
 
#Return object
$Object = [pscustomobject]@{
    FullName = $RegistryKey.Name
    Name = $RegistryKey.Name -replace '.*\\(.*)','$1'
    LastWriteTime = $LastWriteTime
}
$Object.pstypenames.insert(0,'Microsoft.Registry.Timestamp')
$Object

image

If you compare this to what I showed earlier with the manual exporting of the registry key to a text file, you will see that these times are exactly the same. Well, this also includes the seconds, which the exported file does not.

Now wouldn’t it be nice to have a function that does all of this work for you? Of course you would! I wrote a function called Get-RegistryKeyLastWriteTime that will allow you to get the registry key timestamp from either a remote or local system.

Let’s give it a couple of runs to see how it works. The first example shows how you can pipe an existing registry key object into the function to get the timestamp.

 
$RegistryKey = Get-Item "HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion\IniFileMapping\Autorun.inf"
$RegistryKey | Get-RegistryKeyTimestamp | Format-List

image

The next example runs against a remote system and allows you to specify the hive and subkey that you wish to query.

 
Get-RegistryKeyTimestamp -Computername boe-pc -RegistryHive LocalMachine –SubKey  'SOFTWARE\Microsoft\Windows NT\CurrentVersion\IniFileMapping\Autorun.inf' |
Format-List

image

Works like a champ! Be sure to download this and give it a run and let me know what you think!

Download Get-RegistryKeyLastWriteTime

https://gallery.technet.microsoft.com/scriptcenter/Get-RegistryKeyLastWriteTim-63f4dd96

Posted in powershell | Tagged , , , , , | Leave a comment

What is Your PowerShell Resolution?

As the year is winding down to its final couple of weeks of 2014 before we ring in the new year, it is time again to think about the new year and what things I want to accomplish in terms of IT and PowerShell. Basically I am coming up with my own resolutions for the new year that aren’t related to the usual “go visit this city” or “hit the gym more” type of things. Those are fine but I am more interested in the professional side of the house and things that I would like to accomplish.

Over the past year I was fortunate enough to be re-awarded the Microsoft MVP for Windows PowerShell, continued to write more PowerShell articles in my blog, did some guest spots on Hey, Scripting Guy! and even took a part time gig writing for MCPMag.com. Besides that, I broke out of my speaking shell to speak at NorCal PowerShell Users Group as well as for Philly Posh (both of which were amazing opportunities). I also helped to co-found and co-lead a local PowerShell user group here in Omaha.

This year I wanted to at least look at a couple of things which I want to do, but for one reason or another I just never found the time to accomplish. I figured that those items were make for a great resolution for the New Year. There are some things that I would have like to do more of or hope to continue to do again in 2015 so those will be added again as a reminder to myself of what I would like to do.

So with that, here are a few things (in no particular order) that I am looking to accomplish in 2015 as my PowerShell Resolutions:

  • Learn more about Desired State Configuration (DSC)
  • Speak at a local conference (SQL Saturday, InfoTech, Nebraska.Code(), etc…)
  • Continue to speak at User groups (at least 2 this year)
  • Develop a new project for 2015 (I actually have mine for 2014 published, but haven’t gone ‘public’ with it just yet)

These are things that I hope to do and there are other things that I am looking to do but I decided to limit myself to 4 things here. With that, I am interested to hear what you would like to do with PowerShell in 2015 be it learning something new about it or maybe working on some project in the new year.

Posted in News, powershell | Tagged , , | 2 Comments

Managing Services using PowerShell on MCPMag.com

This month I will be talking about managing Windows services using PowerShell on MCPMag.com. Be sure to stop over there and check out the articles and let me know what you think!

Managing Services, Part 1: Investigating Service Information Using PowerShell

Managing Services, Part 2: Working with Service States

Posted in News, powershell | Tagged , , | Leave a comment

5 Day Spot on Hey, Scripting Guy! Talking Script Troubleshooting

I have a 5 day spot on Hey, Scripting Guy! this week (1-5 Dec) talking about troubleshooting scripts using some built-in cmdlets and using the PowerShell debugger. Be sure to check them out this week and let me know what you think!

I’ll update this page with links to each article in the series (and the PowerTips as well!).

Monday

Tuesday

Wednesday

Thursday

Friday

Posted in powershell | Tagged , , , , | Leave a comment

Exploring iTunes Using PowerShell Part 3: Connected Devices

Continuing on with my article on PoweShell and iTunes, I am now going to show you how to find out what devices are currently connected to iTunes. By connected, I mean that the device is actually showing up as being connected to iTunes to be able to sync, update or ejecting the device from iTunes.

First thing is first: connect to the iTunes COM object.

 
$itunes = New-Object  -ComObject iTunes.Application

Now that we have accomplished that, I also need to connect my iPhone to my computer so it actually shows up in iTunes. Once that has been done, we can start digging for information on the iPhone.

The “source” of what we are looking for just happens to reside in a property call…wait for it…sources.

 
$Sources = @($itunes.Sources)
$Sources

image

Note: I have to cast the results as an array using @() when dealing with COM objects, otherwise it will be treated as a single item in the collection.

In this case, the iPhone is easily distinguished by its name. But what if the device had another name or there were more sources than just these 3? We need to look at the kind property as the means to filter our the sources. Fortunately, looking through the SDK allowed me to find the enum to map the integer to what kind of source I am dealing with.

enum
ITSourceKind {
ITSourceKindUnknown = 0,
ITSourceKindLibrary,
ITSourceKindIPod,
ITSourceKindAudioCD,
ITSourceKindMP3CD,
ITSourceKindDevice,
ITSourceKindRadioTuner,
ITSourceKindSharedLibrary
}

This can easily be made into a hash table.

 
$SourceKind = @{
    Unknown = 0
    Library = 1
    iPhone = 2
    CD = 3
    MP3 = 4
    Device = 5
    Radio = 6
    SharedLibrary = 7
}

Now I can filter out the sources to find my actualy iDevice.

 
#Need Kind -eq 2
$iPhone = $Sources | Where {
    $_.Kind -eq $SourceKind.iPhone
}

#View the actual iPhone device
$iPhone

image

Here I can see the size of the of device as well as how much free space is left. I can also locate the version of the iOS (8.1 in this case). So this is pretty neat, but I wonder what methods are available for me to use?

 
$iPhone | Get-Member -MemberType Method

image

Each of these does what you would expect them to:

  • EjectIpod ejects the iDevice from the computer
  • UpdateIpod attempts to update the software on the iDevice, assuming that is what you want to happen.

In this case, I won’t be demoing these two methods as they are pretty straightforward and require no additional parameters to the methods.

I can take a look at my playlists and see what I have.

 
@($iPhone.Playlists) | 
Select Name, Time, @{L='SizeGB';E={"{0:N}" -f ($_.Size/1GB)}} | 
Format-Table –AutoSize

image

I’m curious as to what I have for Podcasts on my iPhone currently. I know that under playlists, it is the third index in the collection.

 
@($iPhone.Playlists)[3]

image

Now let’s see what all is in here.

 
@(@($iPhone.Playlists)[3].Tracks) | 
Select-Object Name, Album, Artist, DateAdded, 
@{L='SizeGB';E={"{0:N}" -f ($_.Size/1GB)}}, Genre, Description, Time

image

Pretty cool! I won’t dive much more into playlists as I am going to spend more time with that at a later date. But here you can see what I have on my iPhone currently.

Unfortunately, there really isn’t a lot I can uncover using the COM object about my device, at least not publicly (the SDK doesn’t even mention anything else). But if I happen to find anything else out, I will be sure to write about it!

Posted in powershell | Tagged , , , | Leave a comment