{"id":51883,"date":"2009-12-02T00:01:00","date_gmt":"2009-12-02T00:01:00","guid":{"rendered":"https:\/\/blogs.technet.microsoft.com\/heyscriptingguy\/2009\/12\/02\/hey-scripting-guy-can-i-use-the-registry-to-retrieve-a-list-of-the-most-recently-run-programs\/"},"modified":"2009-12-02T00:01:00","modified_gmt":"2009-12-02T00:01:00","slug":"hey-scripting-guy-can-i-use-the-registry-to-retrieve-a-list-of-the-most-recently-run-programs","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/scripting\/hey-scripting-guy-can-i-use-the-registry-to-retrieve-a-list-of-the-most-recently-run-programs\/","title":{"rendered":"Hey, Scripting Guy! Can I Use the Registry to Retrieve a List of the Most Recently Run Programs?"},"content":{"rendered":"<p><!-- AddThis Button BEGIN --><a class=\"addthis_button\" href=\"http:\/\/www.addthis.com\/bookmark.php?v=250&amp;pub=scriptingguys\"><img decoding=\"async\" alt=\"Bookmark and Share\" src=\"http:\/\/s7.addthis.com\/static\/btn\/v2\/lg-share-en.gif\" width=\"125\" height=\"16\"><\/a><!-- AddThis Button END --><\/p>\n<p class=\"MsoNormal\"><span><\/span>&nbsp;<\/p>\n<p><span><\/p>\n<p><img decoding=\"async\" title=\"Hey, Scripting Guy! Question\" border=\"0\" alt=\"Hey, Scripting Guy! Question\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/q-for-powertip.jpg\" width=\"34\" height=\"34\"><\/p>\n<p class=\"MsoNormal\">Hey, Scripting Guy! When I work on a computer, I often run many commands. I generally use <b>Run<\/b> from the Start menu to launch these programs. When I am finished, I use <a href=\"http:\/\/blogs.technet.com\/heyscriptingguy\/archive\/2006\/03\/24\/how-can-i-retrieve-a-list-of-the-commands-typed-into-the-run-dialog-box.aspx\">a script I copied from the Scripting Guys archive<\/a> to display all the commands that I ran and then I save them in a Notepad file. However, that script no longer works on Windows 7. Because Windows 7 contains Windows PowerShell 2.0, I am wondering if you could convert that old VBScript script to Windows PowerShell for me. Pretty please?<\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>&#8212; KE<br><\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span><img decoding=\"async\" title=\"Hey, Scripting Guy! Answer\" border=\"0\" alt=\"Hey, Scripting Guy! Answer\" align=\"left\" src=\"https:\/\/devblogs.microsoft.com\/wp-content\/uploads\/sites\/29\/2019\/02\/a-for-powertip.jpg\" width=\"34\" height=\"34\">Hello KE, <\/span><\/p>\n<p class=\"MsoNormal\"><span>Microsoft Scripting Guy Ed Wilson here. It is the day after <a href=\"http:\/\/en.wikipedia.org\/wiki\/Thanksgiving\">Thanksgiving<\/a> in the United States as I am answering your e-mail. I feel a little sluggish due to an overdose of carbs. I have had two pots of English Breakfast tea this morning, and I still feel sleepy. <\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>But, KE, your e-mail intrigued me, so it pulled me to action. The script you refer to reads the registry and performs a little cleanup of the data that is contained there. The ReadMostRecentlyRunCommands.vbs script from that post is seen here. <\/p>\n<p><\/span><\/p>\n<p class=\"CodeBlockScreenedHead\"><span><strong>ReadMostRecentlyRunCommands.vbs<\/p>\n<p><\/strong><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN\"><font><font face=\"Lucida Sans Typewriter\">Const HKEY_CURRENT_USER = &amp;H80000001<br>&nbsp;<br>strComputer = &#8220;.&#8221;<br>&nbsp;<br>Set objRegistry = GetObject(&#8220;winmgmts:\\&#8221; &amp; strComputer &amp; &#8220;rootdefault:StdRegProv&#8221;)<\/p>\n<p>strKeyPath = &#8220;SoftwareMicrosoftWindowsCurrentVersionExplorerRunMRU&#8221;<br>objRegistry.EnumValues HKEY_CURRENT_USER, strKeyPath, arrValueNames, arrValueTypes<br>&nbsp;<br>For Each strValue in arrValueNames<br>&nbsp;&nbsp;&nbsp; If Len(strValue) = 1 Then<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; objRegistry.GetStringValue HKEY_CURRENT_USER,strKeyPath,strValue,strRunCommand<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; intLength = Len(strRunCommand)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; strRunCommand = Left(strRunCommand, intLength &#8211; 2)<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Wscript.Echo strRunCommand<br>&nbsp;&nbsp;&nbsp; End If&nbsp;&nbsp; <br>Next<\/p>\n<p><\/font><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span>There is no magic in Windows PowerShell. If the VBScript script will not work, rewriting it in Windows PowerShell will not work either. To fix something that was broken because of a change in the operating system, you would need to discover a new approach. When I ran the ReadMostRecentlyRunCommands.vbs script, it returned the error seen here:<\/p>\n<p><\/span><\/p>\n<p class=\"Fig-Graphic\"><img decoding=\"async\" title=\"Image of error returned by script\" alt=\"Image of error returned by script\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/december\/hey1202\/hsg-12-2-09-01.jpg\" width=\"424\" height=\"219\"><a href=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/december\/hey1202\/hsg-12-2-09-01.jpg\"><font face=\"Segoe\"><\/font><\/a><\/p>\n<p class=\"MsoNormal\"><br><span>The &ldquo;Object not a collection&rdquo; error occurs in VBScript when the script tries to use <b>For&hellip;Each&hellip;Next<\/b> to walk through something that either does not exist or is a single item. The error is displayed because the object is not a collection or an array. When I checked the registry, I saw that the <b>RunMRU<\/b> registry key was empty. This is shown here:<\/p>\n<p><\/span><\/p>\n<p class=\"Fig-Graphic\"><img decoding=\"async\" title=\"Image of empty RunMRU registry key\" alt=\"Image of empty RunMRU registry key\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/december\/hey1202\/hsg-12-2-09-02.jpg\" width=\"600\" height=\"419\"><a href=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/december\/hey1202\/hsg-12-2-09-02.jpg\"><font face=\"Segoe\"><\/font><\/a><\/p>\n<p class=\"MsoNormal\"><br><span>Then it dawned on me: In Windows 7 (as in Windows Vista), the <b>Start\/Run<\/b> menu item is not enabled because you use the Search Programs and Files box to launch programs. To add the <b>Run<\/b> command, right-click the <b>Start<\/b> button, click <b>Properties<\/b>, click <b>Customize<\/b>, and select the <b>Run command<\/b> check box (near the bottom of the list). This is shown here: <\/p>\n<p><\/span><\/p>\n<p class=\"Fig-Graphic\"><img decoding=\"async\" title=\"Image of selecting the Run command check box\" alt=\"Image of selecting the Run command check box\" src=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/december\/hey1202\/hsg-12-2-09-03.jpg\" width=\"393\" height=\"483\"><a href=\"http:\/\/img.microsoft.com\/library\/media\/1033\/technet\/images\/scriptcenter\/qanda\/hsg\/2009\/december\/hey1202\/hsg-12-2-09-03.jpg\"><font face=\"Segoe\"><\/font><\/a><\/p>\n<p class=\"MsoNormal\"><br><span>After the <b>Run<\/b> command is added and you run a couple of programs, the ReadMostRecentlyRunCommands.vbs script will work. KE, now we can set about translating your VBScript script into Windows PowerShell. <\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>Rather than use the WMI <b>StdRegProv<\/b> WMI class that was used in the ReadMostRecentlyRunCommands.vbs script, we will use the Windows PowerShell Registry provider. Instead of using the <b>Len<\/b> and <b>Left<\/b> string manipulation functions, we will use regular expressions. The net result is the GetMostRecentlyRunPrograms.ps1 script. <\/p>\n<p><\/span><\/p>\n<p class=\"CodeBlockScreenedHead\"><span><strong>GetMostRecentlyRunPrograms.ps1<\/p>\n<p><\/strong><\/span><\/p>\n<p class=\"CodeBlockScreened\"><span lang=\"EN\"><font><font face=\"Lucida Sans Typewriter\">$MRUregKey = &#8220;HKCU:SoftwareMicrosoftWindowsCurrentVersionExplorerrunmru&#8221;<br>Get-Itemproperty $MRUregKey |<br>ForEach-Object {<br>&nbsp;$_.psbase.members |<br>&nbsp;Where-Object { $_.name.length -eq 1 } | <br>&nbsp;ForEach-Object { [regex]::match($_.value,&#8221;w*&#8221;).value }<br>} #end foreach-object<\/p>\n<p><\/font><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span>The GetMostRecentlyRunPrograms.ps1 script begins by assigning the registry key to the <b>$MRUregKey<\/b> variable. The <b>HKCU:<\/b> Windows PowerShell drive is rooted in the <b>HKEY_CURRENT_USER<\/b> registry hive. The <b>Get-ItemProperty<\/b> cmdlet returns all of the values contained in the registry key. In reality, the information about the previously run commands is contained in the results of this one-line command. However, the display is not very clean. This is shown here:<\/p>\n<p><\/span><\/p>\n<p class=\"CodeBlock\"><span lang=\"EN\"><font face=\"Lucida Sans Typewriter\">PS C:&gt; Get-ItemProperty &#8220;HKCU:SoftwareMicrosoftWindowsCurrentVersionExplorerrunmru&#8221;<\/p>\n<p><br>PSPath&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : Microsoft.PowerShell.CoreRegistry::HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionExplorer<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; runmru<br>PSParentPath : Microsoft.PowerShell.CoreRegistry::HKEY_CURRENT_USERSoftwareMicrosoftWindowsCurrentVersionExplorer<br>PSChildName&nbsp; : runmru<br>PSDrive&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : HKCU<br>PSProvider&nbsp;&nbsp; : Microsoft.PowerShell.CoreRegistry<br>a&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : notepad1<br>MRUList&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : cba<br>b&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : calc1<br>c&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; : regedit1<\/p>\n<p>PS C:&gt;<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span>As you can see, the registry values a, b and c contain the most recently run programs. The <b>MRUList<\/b> value keeps track of each of the entries to avoid duplication. If you run Notepad five times, there will still only be one entry for Notepad: item a. <\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>The results of the <b>Get-ItemProperty<\/b> cmdlet are piped down the pipeline for further processing. This is shown here:<\/p>\n<p><\/span><\/p>\n<p class=\"CodeBlock\"><span lang=\"EN\"><font face=\"Lucida Sans Typewriter\">Get-Itemproperty $MRUregKey |<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span>Because there could be multiple registry values to be processed, the <b>ForEach-Object<\/b> cmdlet is used. The code block opens with a curly bracket; it is important to remember to close the curly bracket when all the code is written. To keep from forgetting to close the curly bracket, I always type the closing bracket at the same time I type the opening bracket. This is shown here:<\/p>\n<p><\/span><\/p>\n<p class=\"CodeBlock\"><span lang=\"EN\"><font face=\"Lucida Sans Typewriter\">ForEach-Object {<\/p>\n<p>}<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span>The action we wish to perform on each item in the collection is placed between the curly brackets. Because the name of the property is not directly accessible from the custom Windows PowerShell object that is created by the <b>Get-ItemProperty<\/b> cmdlet, it is necessary to reference the underlying Windows PowerShell object. To do this, use the <b>psbase<\/b> property and choose the members of that base object. We pipe these for further processing. This is seen here:<\/p>\n<p><\/span><\/p>\n<p class=\"CodeBlock\"><span lang=\"EN\"><font face=\"Lucida Sans Typewriter\">$_.psbase.members |<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span>Now we want to choose property names that are only one character in length. We can access the <b>name<\/b> property from the collection of members of the <b>psbase<\/b> object. This is done by using the <b>$_<\/b> automatic variable. The <b>$_<\/b> automatic variable refers to the current item on the pipeline. The <b>name<\/b> property has the <b>length<\/b> property. If the name is one character in length, it gets piped down the pipeline for further processing. This is shown here:<\/p>\n<p><\/span><\/p>\n<p class=\"CodeBlock\"><span lang=\"EN\"><font face=\"Lucida Sans Typewriter\">Where-Object { $_.name.length -eq 1 } |<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span>It is now time to remove the backward slash (<b><\/b>) and the number character <b>(#<\/b>). To do this, we once again need to use the <b>ForEach-Object<\/b> cmdlet because we want to walk through all the one-character property names that were found in the previous command. We again use the <b>$_<\/b> automatic variable to refer to the current object on the pipeline, and this time we use the <b>[regex]<\/b> type accelerator to allow us to find objects that match the regular expression pattern &ldquo;<b>w*<\/b>&rdquo;. The <b>w<\/b> pattern will match lowercase letters, uppercase letters, and numbers. But because the numbers, in our example are separated by the backward slash, our pattern will work. To test the pattern, you can work interactively on the Windows PowerShell console. This is seen here:<\/p>\n<p><\/span><\/p>\n<p class=\"CodeBlock\"><span lang=\"EN\"><font face=\"Lucida Sans Typewriter\">PS C:&gt; $a = &#8220;notepad1&#8221;<br>PS C:&gt; [regex]::match($a,&#8221;w*&#8221;)<\/p>\n<p><br>Groups&nbsp;&nbsp; : {}<br>Success&nbsp; : True<br>Captures : {}<br>Index&nbsp;&nbsp;&nbsp; : 0<br>Length&nbsp;&nbsp; : 0<br>Value&nbsp;&nbsp;&nbsp; :<\/p>\n<p>PS C:&gt;<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span>The completed <b>ForEach-Object<\/b> command is shown here:<\/p>\n<p><\/span><\/p>\n<p class=\"CodeBlock\"><span lang=\"EN\"><font face=\"Lucida Sans Typewriter\">ForEach-Object { [regex]::match($_.value,&#8221;w*&#8221;).value }<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span>When the script runs, the following output is displayed on my computer:<\/p>\n<p><\/span><\/p>\n<p class=\"CodeBlock\"><span lang=\"EN\"><font face=\"Lucida Sans Typewriter\">PS C:Usersed.NWTRADERS&gt; C:dataScriptingGuysGetMostRecentlyRunPrograms.ps1<br>notepad<br>calc<br>regedit<\/p>\n<p><\/font><\/span><\/p>\n<p class=\"MsoNormal\"><span>KE, that is all there is to using the registry to retrieve a listing of the most recently run programs. Registry Week will continue tomorrow when we will talk about&hellip;wait a minute. <\/p>\n<p><\/span><\/p>\n<p class=\"MsoNormal\"><span>If you want to know exactly what we will be looking at tomorrow, follow us on <a href=\"http:\/\/bit.ly\/scriptingguystwitter\" target=\"_blank\">Twitter<\/a> or <a href=\"http:\/\/bit.ly\/scriptingguysfacebook\" target=\"_blank\">Facebook<\/a>. If you have any questions, send e-mail to us at <a href=\"http:\/\/blogs.technet.commailto: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><\/span><\/p>\n<p class=\"MsoNormal\"><span>&nbsp;<\/p>\n<p><\/span><\/p>\n<p><b><span>Ed Wilson and Craig Liebendorfer, Scripting Guys<\/p>\n<p><\/span><\/b><\/p>\n<p class=\"MsoNormal\">&nbsp;<\/p>\n","protected":false},"excerpt":{"rendered":"<p>&nbsp; Hey, Scripting Guy! When I work on a computer, I often run many commands. I generally use Run from the Start menu to launch these programs. When I am finished, I use a script I copied from the Scripting Guys archive to display all the commands that I ran and then I save them [&hellip;]<\/p>\n","protected":false},"author":595,"featured_media":87096,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[31,26,174,3,4,45],"class_list":["post-51883","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-scripting","tag-operating-system","tag-registry","tag-regular-expressions","tag-scripting-guy","tag-scripting-techniques","tag-windows-powershell"],"acf":[],"blog_post_summary":"<p>&nbsp; Hey, Scripting Guy! When I work on a computer, I often run many commands. I generally use Run from the Start menu to launch these programs. When I am finished, I use a script I copied from the Scripting Guys archive to display all the commands that I ran and then I save them [&hellip;]<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/51883","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\/595"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/comments?post=51883"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/posts\/51883\/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=51883"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/categories?post=51883"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/scripting\/wp-json\/wp\/v2\/tags?post=51883"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}