COM pt 5–FileSystem Object
I haven’t finished with the WScript.Shell object and we will return to it. This time I want to look at the FileSystem object. If you have worked with VBScript and files you will be familiar with this object. While we have techniques in PowerShell to perform many of the same tasks there are still a few places where using this object can save effort.
For instance getting the size of folder. We can use get-childitem and use measure-object on the output but that gets a bit messy. An alternative is to do this
001
002 003 004 005 006 |
$fso = New-Object -ComObject "Scripting.FileSystemObject"
$top = $fso.GetFolder("c:\test") "{0,-20} {1,12}" -f $top.Path, $top.Size foreach ($sf in $top.SubFolders) { "{0,-20} {1,12}" -f $sf.Path, $sf.Size } |
Create the FileSystem object and get the folder we are interested in. Display the Path and Size properties.
we can then use the SubFolders collection to loop through the and get the path and size of each sub folder.
This is only the first level sub folders – if we want more we need to think about recursion
COM pt 4
If you have been following along so far you may be thinking that you can use
ls env:
to view the environmental variables.
True.
But if you compare the output of part 3, the PowerShell enc: drive and getting the environmental variables via WMI you will find that they aren’t identical.
The point of this post is to show that we can combine what we are learning about the WScript objects with “pure” PowerShell.
Look at the path variable – on my test machine I get
PS> $env:path
%SystemRoot%\system32\WindowsPowerShell\v1.0\;C:\Program Files\Common Files\Microsoft Shared\Windows Live;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files\jZip;C:\Program Files\TortoiseHg\;C:\Program Files\Windows Live\Shared
OK. Now notice the first entry %systemroot%. We can’t resolve that directly in PowerShell but what we can do is this
001
002 003 004 005 006 007 008 009 010 011 012 013 014 |
function resolve-envar {
param([string]$var) $wshell = New-Object -ComObject "WScript.Shell" $wshell.ExpandEnvironmentStrings("$var") } $data = $env:path -split "\\" if ($data[$($_)] -match "%.*%") { $data[$($_)] = resolve-envar $data[$($_)] } } $path = $data -join "\" $path |
We get the path and split it at the \ folder delimiters. We need to use \\ as \ is interpreted as an escape character. We examine each element in the path - a range operator is less typing than a for loop – and if it is of the form %…….% we use the resolve-envar function to expand it.
We can then stick the bits back together again and display the path.
Not something we are likely to use every day but it does show how we can drop bits of functionality into our scripts when they are the easiest way to do things.
COM pt 3–More shell
Last time we looked at the Environment parameterized property
If you ran the script in part two the output would have contained things like
windir=%SystemRoot%
We need to be able to resolve the variable %SystemRoot% part to the full path
001
002 003 004 005 006 007 008 009 010 011 012 013 014 |
$wshell = New-Object -ComObject "WScript.Shell"
"System", "User", "Process", "Volatile" | foreach { "`n$($_)" $envars = $wshell.Environment($($_)) foreach ($envar in $envars){ if ($envar -match "%.*%"){ $s = ($envar -split "%")[1] $exp = $wshell.ExpandEnvironmentStrings("%$s%") $envar.Replace("%$s%", $exp) } else {$envar } } } |
We can take the script from part 2 and modify it slightly. Once we have the environmental variables for each area we loop through them and find those that contain the pattern “%.%”. This is a simple regular expression (I only do simple regex. Maybe I’ll get more adventurous after the March UG group when we have a guest speaker on regex) that looks for two % signs separated by some characters.
If we find that we split on the % and take the second element – push it through the ExpandEnvironemtStrings method and plus the result back into the environmental variable.
Reminder 1
Quick reminder that the UK PowerShell UG meeting for February is coming up soon.
Details from here http://msmvps.com/blogs/richardsiddaway/archive/2011/01/18/uk-user-group-february-live-meeting.aspx
That reminds me – I’d better write the presentation
Science or Art
Infrastructure Architecture = Science or Art. Discuss.
We claim the titles of architect and/or engineer but is what we do as infrastructure architects really based on solid scientific/engineering principles.
I would claim not. Much of what we do is recycling the designs of the past – possibly adapting them as new versions of particular technologies appear. But how many times have you seen an implementation of version 3 of product that is implemented in exactly the same way as version 1 would have been. The reason is usually the very lame “but we’ve always done it that way”. The real reason in many cases is that the people involved haven’t bothered to keep up to date with changes in the technologies they are relying on. This means their organisations aren’t getting the full benefit of those applications.
There are a number of fundamental architectural decisions that in many cases are driven by the existing environment. How many truly green field sites are there these days?
There are a larger number of design decisions which are often based on the products we select.
In this way we are more like the master masons that built the great cathedrals of the Middle Ages. We know what works and we stick with it.
So. Science or Art?
COM pt3–WScript.Shell
One thing that we seem to forget when we use PowerShell is the functionality that was available to us in VBScript through the Windows Scripting Host (WSH) objects. Much of this functionality has been duplicated in PowerShell cmdlets or through .NET classes but there is still a good pile of stuff we can use. I think its time to stir up that pile and have a look at what we can find. The great thing about this functionality is:
- its available on all Windows boxes
- there are many VBScript examples we can reuse
The WScript object is the top of the tree as far as WSH is concerned bit it doesn’t really do much for us. Lets drop down a level and look at the WScript.Shell object.
PS> $wshell = New-Object -ComObject "WScript.Shell"
PS> $wshell
Name MemberType
---- ----------
AppActivate Method
CreateShortcut Method
Exec Method
ExpandEnvironmentStrings Method
LogEvent Method
Popup Method
RegDelete Method
RegRead Method
RegWrite Method
Run Method
SendKeys Method
Environment ParameterizedProperty
CurrentDirectory Property
SpecialFolders Property
The properties look interesting lets start there. Special folders are the desktop etc which we will look at later. Current directory is obvious.
The Environment property is listed as a parameterized property – this means we have to give it a value
001
002 003 004 005 006 |
$wshell = New-Object -ComObject "WScript.Shell"
"System", "User", "Process", "Volatile" | foreach { "`n$($_)" $wshell.Environment($($_)) } |
We have four possibilities which gives us a nice break down of the environmental variables. It supplies a bit more info than
dir env:
The Environment property may look like a method but it isn’t really.
We do have a set of methods to play with
AppActivate
CreateShortcut
Exec
ExpandEnvironmentStrings
LogEvent
Popup
RegDelete
RegRead
RegWrite
Run
SendKeys
which we will start looking at later.
Watching the file arrival
I picked up a question in the ITKE forums about a script to watch the file system
001
002 003 004 005 006 007 008 009 010 011 012 013 014 015 016 |
function eventhandler {
param ($e) Write-Host "File: $($e.SourceEventArgs.FullPath) has arrived" } $folder = "C:\test" $fsw.IncludeSubDirectories = $true $action = {eventhandler $($event)}Register-ObjectEvent -InputObject $fsw -EventName "Created" ` |
The event handler function accepts the event as a parameter and writes out that a file has been received. You can put anything in here eg a mail message could be sent.
The folder and filter (all files) are set and we define the FileSystemWatcher object which we set to include subdirectories
The action is defined and the event is registered.
dot source the script when you run it
COM pt 2–More Shell
$shell = New-Object -ComObject "Shell.Application"
gets us a shell object. We can then see the methods available to us
PS> $shell | gm -MemberType method | Format-Wide -Column 2
AddToRecent BrowseForFolder
CanStartStopService CascadeWindows
ControlPanelItem EjectPC
Explore ExplorerPolicy
FileRun FindComputer
FindFiles FindPrinter
GetSetting GetSystemInformation
Help IsRestricted
IsServiceRunning MinimizeAll
NameSpace Open
RefreshMenu ServiceStart
ServiceStop SetTime
ShellExecute ShowBrowserBar
ShutdownWindows Suspend
TileHorizontally TileVertically
ToggleDesktop TrayProperties
UndoMinimizeALL Windows
WindowsSecurity WindowSwitcher
$shell.MinimizeAll()
$shell.UndoMinimizeAll()
Are fairly obvious in what they do.
$shell.Toggledesktop()
will also minimise all open windows. but it isn’t reversed by UndoMinimizeAll().
$shell.Windows()
may cause some confusion as it represents a collection of all of the open windows that belong to the Shell. It does not show open all open Windows.
$shell.Windows() | select name, type, statustext
return these windows
Windows Internet Explorer
Windows Internet Explorer
Windows Internet Explorer
Windows Internet Explorer
Windows Explorer
Which is right for the shell. I have IE open with 4 active tabs and Windows Explorer. What this doesn’t show is that I also have PowerShell, MSDN library, LiveWriter and LiveMail open.
The lesson is to be careful and read the documentation.
$shell.Explore("folder-name")
could be useful for instance
$shell.Explore("c:\scripts")
will open Windows Explorer pointing at the scripts folder.
COM pt 1
In April I will be doing a User Group Live Meeting on using COM with PowerShell. It is such a wide topic I thought I’d start a few posts to set some background – it also helps my reasearch
COM is the way applications were built for the Windows platform before .NET appeared. Ugh pre-historic stuff that no one cares about I hear you shout.
Opps. Not really
COM is still around a happily surviving today. For instance the Office applications, IE and the Windows Scripting Host functionality are all COM based
We can discover some information about the COM based applications installed in our systems by using
Get-WmiObject -Class Win32_COMApplication | sort Name | Select Name
This will generate a very long list – don’t be surprised if the early entries have a blank name. Unfortunately the name doesn’t always reflect what we need to use to work with the COM object.
For instance the list of applications generates these examples
Shell ChkdskEx Dialog
Shell Computer Accounts
Shell Computer Groups
Shell Execute Hardware Event Handler
Shell FMIFS Wrapper
Shell Hardware Mixed Content Handler
Shell Hardware Mixed Content Handler Cancelled
Shell Security Editor
ShellWindows
if we search for ShellWindows for instance - http://msdn.microsoft.com/en-us/library/bb773974(VS.85).aspx - we see that it represents a collection of open windows belonging to the shell. In many cases COM objects are based on a hierarchy and we have to start at the top of hierarchy.
If you work around in the documentation you will often find a topic that describes the scripting interface or scripting objects. Scripting is somewhere in the title e.g. http://msdn.microsoft.com/en-us/library/bb773177(v=VS.85).aspx
If we hunt around in the list we find the shell object which “Represents the objects in the Shell. Methods are provided to control the Shell and to execute commands within the Shell. There are also methods to obtain other Shell-related objects.”
This sounds promising so we follow it to http://msdn.microsoft.com/en-us/library/bb774094(v=VS.85).aspx where we find in the properties section a reference to the objects’s Application object. Bingo – just what we want.
Unfortunately we don’t get much in the way of examples so we have to go back to what we can work out. If you have seen any COM based PowerShell you know that we use the New-Object cmdlet and if you look in the help you find this
$shell = New-Object -ComObject "Shell.Application"
This structure is what we will often see – the name of the object followed by .Application. If we are working with Word it becomes
$word = New-Object -ComObject "Word.Application"
Let’s go back to the Shell and find out what it does
$shell | gm
It has methods and properties just like .NET so what do we have
$shell.TileVertically()
is fun
$shell.TileHorizontally() supplies a bit of amusement but don’t have too many Windows open when you use them otherwise you will spend a while sorting them out
Some of the other methods look useful so we’ll explore them later
System Restore Points
I’ve just started a series on using the WMI system restore classes to work with remote machines at http://itknowledgeexchange.techtarget.com/powershell/
The functions developed in this series will become the next PSAM module