Monthly Archives: January 2011

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 "\\"
0..$($data.length -1) | foreach
 {
 
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  Smile

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"
$filter = "*"
$fsw = New-Object -TypeName System.IO.FileSystemWatcher `
-ArgumentList $folder, 
$filter
$fsw
.IncludeSubDirectories = 
$true

$action
 = {eventhandler $($event)}

Register-ObjectEvent -InputObject $fsw -EventName "Created" `
 -SourceIdentifier "File System Creation" -Action $action

 

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