<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://powershell.com/cs/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Dreaming in PowerShell : Explorer, COM, SpVoice, SAPI</title><link>http://powershell.com/cs/blogs/tobias/archive/tags/Explorer/COM/SpVoice/SAPI/default.aspx</link><description>Tags: Explorer, COM, SpVoice, SAPI</description><dc:language>en</dc:language><generator>CommunityServer 2008.5 (Build: 30929.2835)</generator><item><title>Clean your Desktop with COM</title><link>http://powershell.com/cs/blogs/tobias/archive/2008/10/14/clean-your-desktop-with-com.aspx</link><pubDate>Tue, 14 Oct 2008 05:04:00 GMT</pubDate><guid isPermaLink="false">f421715f-7aba-45f0-8a8d-44de5318a3a7:98</guid><dc:creator>Tobias Weltner</dc:creator><slash:comments>0</slash:comments><wfw:commentRss xmlns:wfw="http://wellformedweb.org/CommentAPI/">http://powershell.com/cs/blogs/tobias/rsscomments.aspx?PostID=98</wfw:commentRss><comments>http://powershell.com/cs/blogs/tobias/archive/2008/10/14/clean-your-desktop-with-com.aspx#comments</comments><description>&lt;p&gt;My desktop gets cluttered with open explorer windows often. Whenever I need quick access to my drives, I press &lt;strong&gt;WIN+E&lt;/strong&gt;, and since my hair is always on fire, these windows tend to remain open until I finally get frustrated and manually close them all. I was wondering if there wasn&amp;#39;t a magic trick to close all open explorer windows with PowerShell, and while trying to figure it out, I learned a lot about COM.&lt;/p&gt;
&lt;h2&gt;&lt;span style="color:#3366ff;"&gt;Accessing a COM library&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;How to you find&amp;nbsp;out which&amp;nbsp;explorer windows are open in the first place? Since Windows (including Vista and Server 2008) is still heavily dependant on COM (Component Object Model, the &amp;quot;old&amp;quot; world before .NET came out), you need to contact the appropriate COM library. &lt;/p&gt;
&lt;p&gt;Fortunately, that&amp;#39;s easy with PowerShell. All you need is&amp;nbsp;&lt;strong&gt;New-Object -COMObject&lt;/strong&gt;.&amp;nbsp;Add the name of the COM library you want to use, and&amp;nbsp;off you go.&amp;nbsp;Most of the UI is handled by a COM library called &lt;strong&gt;Shell.Application&lt;/strong&gt;, so here is how you can access this library:&lt;/p&gt;
&lt;div style="font-size:20px;background:#faf3e8;font-family:&amp;#39;Consolas&amp;#39;;"&gt;&lt;span style="color:#000000;background-color:#faf3e8;"&gt;&lt;span style="color:#888888;"&gt;PS&amp;gt;&amp;nbsp;&lt;/span&gt;$lib&amp;nbsp;=&amp;nbsp;New-Object&amp;nbsp;-comObject&amp;nbsp;Shell.Application&lt;br /&gt;&lt;span style="color:#888888;"&gt;PS&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;h2&gt;&lt;span style="color:#3366ff;"&gt;What&amp;#39;s &amp;quot;inside&amp;quot; a COM object?&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;Once you have access to your COM library through &lt;strong&gt;$lib&lt;/strong&gt;, the next thing you want to find out are the object members: which commands (&amp;quot;methods&amp;quot;) and information (&amp;quot;properties&amp;quot;) does the library provide? With native PowerShell methods, you could pipe the object to &lt;strong&gt;Get-Member&lt;/strong&gt; or append $lib with a dot &amp;quot;.&amp;quot; and press TAB to get code completion. &lt;/p&gt;
&lt;p&gt;A much more convenient way is integrated into &lt;strong&gt;&lt;a target="_blank" href="http://www.idera.com/Products/PowerShell/"&gt;PowerShellPlus&lt;/a&gt;&lt;/strong&gt;: once you type a dot, you get intellisense-like code completion menus.&lt;/p&gt;
&lt;p&gt;&lt;img src="http://powershell.com/cs/cfs-file.ashx/__key/CommunityServer.Blogs.Components.WeblogFiles/tobias.com_5F00_and_5F00_explorer/psp3.png" alt="" /&gt;&lt;/p&gt;
&lt;p&gt;As it turns out, the method &lt;strong&gt;Windows()&lt;/strong&gt; returns a list of all open explorer windows. Actually, &lt;strong&gt;Windows()&lt;/strong&gt; returns not just explorer windows but also Internet Explorer- and Outlook-Windows. Essentially, it returns any window that uses a ShellWindows Interface. Since all I want is closing my open explorer windows, I need to examine the returned window objects a bit more closely. &lt;/p&gt;
&lt;p&gt;By way of experimenting I found that explorer windows have a property called &lt;strong&gt;FullName&lt;/strong&gt; which contains the full path of the executable, so in order to only close explorer windows, I could sort out all unwanted window objects and focus on explorer windows like this:&lt;/p&gt;
&lt;p&gt;(Please visit the site to view this media)&lt;/p&gt;
&lt;p&gt;A foreach loop enumerates all open windows and then checks to see if the returned object has a FullName property at all. As it turns out, &lt;strong&gt;Windows()&lt;/strong&gt; also returns Outlook panes which do not have&amp;nbsp;a &lt;strong&gt;FullName&lt;/strong&gt; property. Next, the script checks to see if Fullname ends with &amp;quot;\explorer.exe&amp;quot;, making sure we only close real explorer windows. If you wanted to close browser windows instead,&amp;nbsp;all you needed to do was to replace &amp;quot;\explorer.exe&amp;quot; with &amp;quot;\iexplore.exe&amp;quot;.&lt;/p&gt;
&lt;h2&gt;&lt;span style="color:#3366ff;"&gt;Programming in &amp;quot;Style&amp;quot;&lt;/span&gt;&lt;/h2&gt;
&lt;p&gt;A lot of folks are migrating from VBScript to PowerShell, and the previous example was really VBScript style.&amp;nbsp;This is not so much a question of political correctness but rather functionality. The previous script&amp;nbsp;had in fact flaws. &lt;/p&gt;
&lt;p&gt;When you run it, it&amp;nbsp;will not necessarily close all explorer windows. Some may&amp;nbsp;survive your script, and only when you run it a second time will the remaining explorer windows be wiped away.&amp;nbsp;How come?&lt;/p&gt;
&lt;p&gt;The problem is the enumeration returned by &lt;strong&gt;Windows()&lt;/strong&gt;. If you start and close windows&amp;nbsp;in the middle of enumerating the very same windows, the enumerator gets&amp;nbsp;confused. Lets say you&amp;nbsp;had six open windows to begin with, then you closed one.&amp;nbsp;The enumerator now will no longer supply the last&amp;nbsp;(sixth) window to your script because it &amp;quot;thinks&amp;quot; there are only five, not&amp;nbsp;taking into account that the closed window was somewhere at the beginning of the collection and that there were originally six windows when the enumeration started. The bottom line is: never change a collection while enumerating it.&lt;/p&gt;
&lt;p&gt;In&amp;nbsp;PowerShell, there is a much easier (and better) way to handle this: use the&amp;nbsp;Pipeline! When you throw the result of &lt;strong&gt;Windows()&lt;/strong&gt; into the PowerShell pipeline,&amp;nbsp;apparently&amp;nbsp;changes to the collection do not affect the enumerator (and your script shrinks a lot in size, too). Actually, I tend to think that it is a speed thing and that the pipeline happily accepts all input from &lt;strong&gt;Windows()&lt;/strong&gt; before actually the first window is closed somewhere at the end of the pipeline.&lt;/p&gt;
&lt;p&gt;Here is the same thing as one-liner:&lt;/p&gt;
&lt;div style="FONT-SIZE:20px;BACKGROUND:#faf3e8;FONT-FAMILY:&amp;#39;Consolas&amp;#39;;"&gt;&lt;span style="color:#000000;background-color:#faf3e8;"&gt;&lt;span style="color:#888888;"&gt;PS&amp;gt;&lt;/span&gt;&amp;nbsp;$shapp&amp;nbsp;=&amp;nbsp;New-Object&amp;nbsp;-comObject&amp;nbsp;Shell.Application&lt;br /&gt;&lt;span style="color:#888888;"&gt;PS&amp;gt;&amp;nbsp;&lt;/span&gt;$shapp.Windows()&amp;nbsp;|&amp;nbsp;?&amp;nbsp;{&amp;nbsp;$_.FullName&amp;nbsp;-ne&amp;nbsp;$null}&amp;nbsp;|&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div style="FONT-SIZE:20px;BACKGROUND:#faf3e8;FONT-FAMILY:&amp;#39;Consolas&amp;#39;;"&gt;&lt;span style="color:#000000;background-color:#faf3e8;"&gt;?&amp;nbsp;{&amp;nbsp;$_.FullName.toLower().Endswith(&amp;#39;\explorer.exe&amp;#39;)&amp;nbsp;}&amp;nbsp;|&amp;nbsp;&lt;/span&gt;&lt;/div&gt;
&lt;div style="FONT-SIZE:20px;BACKGROUND:#faf3e8;FONT-FAMILY:&amp;#39;Consolas&amp;#39;;"&gt;&lt;span style="color:#000000;background-color:#faf3e8;"&gt;%&amp;nbsp;{&amp;nbsp;&amp;#39;Closed&amp;nbsp;&amp;quot;{0}&amp;quot;&amp;#39;&amp;nbsp;-f&amp;nbsp;$_.Locationname;&amp;nbsp;$_.Quit()&amp;nbsp;}&lt;br /&gt;&lt;span style="color:#888888;"&gt;Closed&amp;nbsp;&amp;quot;Tobias&amp;quot;&lt;br /&gt;Closed&amp;nbsp;&amp;quot;Documents&amp;quot;&lt;br /&gt;Closed&amp;nbsp;&amp;quot;Music&amp;quot;&lt;br /&gt;Closed&amp;nbsp;&amp;quot;Games&amp;quot;&lt;br /&gt;&lt;/span&gt;&lt;span style="color:#888888;"&gt;PS&amp;gt;&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;Basically, this line throws all ShellWindows objects into the pipeline and then applies two filters (&amp;quot;&lt;strong&gt;?&lt;/strong&gt;&amp;quot; aka &lt;strong&gt;Where-Object&lt;/strong&gt;): it first checks whether there is a property &lt;strong&gt;FullName&lt;/strong&gt;, and it then makes sure the &lt;strong&gt;Fullname&lt;/strong&gt; property ends with &amp;#39;\explorer.exe&amp;#39;. Any object passing the filters must be an explorer window, so at the end the pipeline processes each explorer window (&amp;#39;&lt;strong&gt;%&lt;/strong&gt;&amp;#39; aka &lt;strong&gt;Foreach-Object&lt;/strong&gt;) and first outputs its name (found in the &lt;strong&gt;LocationName&lt;/strong&gt; property), then quits the explorer window using the &lt;strong&gt;Quit()&lt;/strong&gt; method.&lt;/p&gt;
&lt;p&gt;Another way to a clean desktop are the methods MinimizeAll() and UNDOMinimizeAll() which in essence do the WIN+M trick programmatically. They minimize and restore all windows, respectively:&lt;/p&gt;
&lt;div style="font-size:20px;background:#faf3e8;font-family:&amp;#39;Consolas&amp;#39;;"&gt;&lt;span style="color:#000000;background-color:#faf3e8;"&gt;&lt;span style="color:#888888;"&gt;PS&amp;gt;&lt;/span&gt;&amp;nbsp;$lib.MinimizeAll()&lt;br /&gt;&lt;span style="color:#888888;"&gt;PS&amp;gt;&amp;nbsp;&lt;/span&gt;$lib.UndoMinimizeALL()&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;There are a ton of additional things you can do with the &lt;strong&gt;Shell.Application&lt;/strong&gt; COM object, and there are tons of additional COM objects. The thing to keep in mind is that PowerShellPlus not only provides advanced intellisense-like code completion to PowerShell Cmdlets and .NET but also to COM. Have a great time playing with COM! For example, let PowerShell speak or read files to you:&lt;/p&gt;
&lt;div style="FONT-SIZE:20px;BACKGROUND:#faf3e8;FONT-FAMILY:&amp;#39;Consolas&amp;#39;;"&gt;&lt;span style="color:#888888;background-color:#faf3e8;"&gt;PS&amp;gt;&amp;nbsp;# get the COM library and assign it to a variable:&lt;/span&gt;&lt;/div&gt;
&lt;div style="FONT-SIZE:20px;BACKGROUND:#faf3e8;FONT-FAMILY:&amp;#39;Consolas&amp;#39;;"&gt;&lt;span style="color:#000000;background-color:#faf3e8;"&gt;&lt;span style="color:#888888;"&gt;PS&amp;gt;&lt;/span&gt; $blabla&amp;nbsp;=&amp;nbsp;New-Object&amp;nbsp;-comObject&amp;nbsp;SAPI.SpVoice&lt;/span&gt;&lt;/div&gt;
&lt;div style="FONT-SIZE:20px;BACKGROUND:#faf3e8;FONT-FAMILY:&amp;#39;Consolas&amp;#39;;"&gt;&lt;span style="color:#000000;background-color:#faf3e8;"&gt;&lt;span style="color:#888888;"&gt;PS&amp;gt; &lt;br /&gt;PS&amp;gt;&lt;/span&gt;&amp;nbsp;$blabla.Speak(&amp;quot;That&amp;#39;s&amp;nbsp;cool,&amp;nbsp;dude!&amp;quot;)&lt;br /&gt;&lt;span style="color:#888888;"&gt;1&lt;br /&gt;&lt;/span&gt;&lt;/span&gt;&lt;/div&gt;
&lt;div style="FONT-SIZE:20px;BACKGROUND:#faf3e8;FONT-FAMILY:&amp;#39;Consolas&amp;#39;;"&gt;&lt;span style="color:#888888;background-color:#faf3e8;"&gt;PS&amp;gt; # to get rid of the return value, cast to VOID:&lt;/span&gt;&lt;/div&gt;
&lt;div style="FONT-SIZE:20px;BACKGROUND:#faf3e8;FONT-FAMILY:&amp;#39;Consolas&amp;#39;;"&gt;&lt;span style="color:#000000;background-color:#faf3e8;"&gt;&lt;span style="color:#888888;"&gt;PS&amp;gt;&lt;/span&gt;&amp;nbsp;[void]$blabla.Speak(&amp;quot;I&amp;nbsp;can&amp;nbsp;also&amp;nbsp;read&amp;nbsp;files!&amp;quot;)&lt;/span&gt;&lt;/div&gt;
&lt;div style="FONT-SIZE:20px;BACKGROUND:#faf3e8;FONT-FAMILY:&amp;#39;Consolas&amp;#39;;"&gt;&lt;span style="color:#000000;background-color:#faf3e8;"&gt;&lt;span style="color:#888888;"&gt;PS&amp;gt; # you can also read file content:&lt;br /&gt;PS&amp;gt;&amp;nbsp;&lt;/span&gt;[void]$blabla.Speak(&amp;quot;c:\autoexec.bat&amp;quot;,&amp;nbsp;4)&lt;br /&gt;&lt;/span&gt;&lt;/div&gt;
&lt;p&gt;Cheerio&lt;/p&gt;
&lt;p&gt;Tobias&lt;br /&gt;&lt;/p&gt;&lt;div style="clear:both;"&gt;&lt;/div&gt;&lt;img src="http://powershell.com/cs/aggbug.aspx?PostID=98" width="1" height="1"&gt;</description><category domain="http://powershell.com/cs/blogs/tobias/archive/tags/Speak/default.aspx">Speak</category><category domain="http://powershell.com/cs/blogs/tobias/archive/tags/SpVoice/default.aspx">SpVoice</category><category domain="http://powershell.com/cs/blogs/tobias/archive/tags/SAPI/default.aspx">SAPI</category><category domain="http://powershell.com/cs/blogs/tobias/archive/tags/New-Object+-COMObject/default.aspx">New-Object -COMObject</category><category domain="http://powershell.com/cs/blogs/tobias/archive/tags/COM/default.aspx">COM</category><category domain="http://powershell.com/cs/blogs/tobias/archive/tags/Shell.Application/default.aspx">Shell.Application</category><category domain="http://powershell.com/cs/blogs/tobias/archive/tags/Explorer/default.aspx">Explorer</category></item></channel></rss>