PowerShell.cz
Get-World | ConvertTo-PowerShell
Get-World | ConvertTo-PowerShell
Jun 19th
Today I had a discussion with my colleague and he mentioned that the only reason why he still uses PowerGUI is that ISE doesn’t support adding of inputs. So we made a deal – if he will not find it on the internet anywhere, I’ll create it. After few minutes he arrived and asked me to do that
Whole solution consists of two functions:

Add-ISEScriptParameter: It asks you (using Read-Host) for parameters to your script. I had a version with MessageBox but then leave the final one without it.
Start-ISEScriptWithParameter: This one will run the actual script with parameter stored in previous step.
I use simple hash table for storing pair <FileName>=<Parameter> in Global scope. You can have different values for different scripts.
function Add-ISEScriptParameter
{
# Enter parameter(s) you want to use as default
$paramText = Read-Host "Enter parameter(s) for $($psISE.CurrentFile.DisplayName)"
# Create hash if not exists
if (-not $Global:ISEScriptParamHash) { $Global:ISEScriptParamHash = @{} }
# Add new parameters to the hash
$Global:ISEScriptParamHash[$psISE.CurrentFile.FullPath] = $paramText
}
function Start-ISEScriptWithParameter
{
# Find name of actual file name
$File = $psISE.CurrentFile.FullPath
# Run actual file with stored parameters
Invoke-Expression -Command "$File $($Global:ISEScriptParamHash[$File])"
}
$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add('Add param', {Add-ISEScriptParameter}, 'Ctrl+Shift+F5')
$psISE.CurrentPowerShellTab.AddOnsMenu.Submenus.Add('Run with param', {Start-ISEScriptWithParameter}, 'Ctrl+F5')
You can see I have three scripts opened. Now I want to add params for one of them. So I switch to it and run Ctrl+Shift+F5. ISE promt me for the value of cmdline and stores it to the hash mentioned.

When I want to run the script with these parameters, I can do that with Ctrl+F5.

To see actual value of hash I can pipe it to Format-Table.
PS> $Global:ISEScriptParamHash | ft -AutoSize Name Value ---- ----- C:\Scripts\matous.ps1 -test 'David' C:\Scripts\Untitled4.ps1 -test 'untitled' C:\Scripts\anothertest.ps1 -n1 'prvni' -n2 'druhy v another'
As usual: there are some points to make it nicer, but this was just quick hit. Another guy converted to ISE
May 6th
As I mentioned in the previous article working with errors was the biggest pain of most of the entries for the first advanced event.
Firstly the description does not say the errors need to be handled in any way, it only mentions you need to clearly display them, PowerShell does that by default. So if you would not use any error handling you would probably still comply with the requirements.
On the other hand when a standard PowerShell cmdlet fails it stops on the first error for each item, reports the error and jumps to the next item. This is what most of you tried to achieve, but not always successfully. Error records got stripped to just error messages informing user, terminating errors became non-terminating and vice versa.
The core of the task is to move files from one location to another. The Move-Item cmdlet is the most reasonable choice, but it has one limitation that must be taken into account: If the destination path (folder) does not exist the operation ends with a non-terminating exception.
For the sake of simplicity let’s assume the source and destination root paths exist because I tested their availability while validating the parameters. There I also made sure the path is a FileSystem path not a registry or other path (believe or not this is more important than the first check). I also assume the remaining parameters are valid.
The goal is to move as much files as I can to the new location. Freeing as much disk space as I can for Dr. Scripto.
My script is going to be laid out like this:
$sourceItem = #[1] Get items recursively and filter them according to the requirements.
foreach ($currentItem in $sourceItem)
{
#[2] Form the path to the resulting directory.
#[3] If the destination directory doesn't exist, create it.
#[4] Move the current item.
}
If any of the last three steps (2, 3 or 4) fails the function should progress to the next item in the list. I am going to force this behavior using ErrorAction on the cmdlets and Try Catch block that will capture only two types of exceptions:
[Management.Automation.MethodInvocationException]
these may raise from .NET calls.
[Management.Automation.ActionPreferenceStopException]
these are raised when cmdlet fails and has the ErrorAction set to Stop.
All these exceptions were originally non-terminating, so after doing the clean-up I write them back to the screen using the Write-Error, keeping all the original information.
The important thing is most of the possible exceptions (like OutOfMemoryException) are still able to pass by unaffected. So if the script fails the terminating exceptions bubble up and possibly terminate the script, the non-terminating exceptions are written to the error stream and the scripts progresses.
Here is the example code:
$Source = 'C:\Windows\logs'
$Destination = 'G:'
$Before = (Get-Date).addDays(-90)
$Filter = '*.log'
$sourceItem = Get-ChildItem -Recurse -Path $Source -File -Filter $Filter |
Where-Object {
$_.LastWriteTime -lt $Before
}
foreach ($currentItem in $sourceItem)
{
$destinationCreated = $false
try
{
$destinationDirectory = $currentItem.Directory.FullName.Replace($Source,$Destination)
if (-not ( Test-Path -Path $destinationDirectory -ErrorAction Stop))
{
$destinationCreated = [bool]( New-Item -Path $destinationDirectory -ItemType Directory -ErrorAction Stop )
}
Move-Item -Path $currentItem.FullName -Destination $destinationDirectory -ErrorAction Stop
}
catch [Management.Automation.MethodInvocationException], [Management.Automation.ActionPreferenceStopException]
{
if ( $destinationCreated )
{
Remove-Item -Path $destinationDirectory -Force
}
Write-Error -ErrorRecord $_
}
}
May 4th
Here are few tips on scripting I’d like to share. Some of them are inspired by the entries for first Scripting Games event. Some of them are just related.
Before I start the rant please keep in mind I understand criticizing is a lot easier than writing the script and making it awesome. Good luck with the next event!
Variables
Use variables and name them properly. Variables are an easy way to label the data you work with. There are few things to focus on:
Apr 16th
Talking about geeky ways to do stuff with Jaap Brasser I’ve stumbled upon this gif, where someone creates what I suppose is C code using MsPaint. Pretty awesome stuff, even if the code won’t compile afterwards because of the bmp header. Wondering how some PowerShell code would look like as a bmp we attempted to write a function to automate the process, and here is my take on it:
function ConvertTo-Bitmap ([String]$Source,
[String]$Destination=([IO.Path]::ChangeExtension($Source,'bmp')))
{
$header = 66,77,58,0,0,0,0,0,0,0,54,0,0,0,40,0,0,0,1,0,0,0,
1,0,0,0,1,0,24,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0
$File = Get-Content $Source -Encoding Byte | Select-Object -Skip 3
$Reminder = $File.Count % 4
$File += ,0 * $Reminder
$size = [Math]::Sqrt($file.Count/4)
$header[22] = $size #height
$header[18] = $size #width
Set-Content -path $Destination -Force -Encoding Byte -Value ([byte[]]($header+$File))
}
Convertto-Bitmap -Source ".\hello.ps1"
Here is an example of “Hello World!” script converted to bmp:

The image is of course zoomed out, here is link to the original.
The logic behind the code is pretty simple. 54 byte long file header and appropriate file extension is what makes a file a bmp file. The header is represented as an array of byte values in the code. The header contains information about the file contents, most importantly its width (offset 18) and height (offset 22). If these values are set too low not all data in the file are visible. If they are too high the file comes out corrupted so I need to calculate the exact value. The header is followed by image data (4byte groups) created from byte values of the letters. All of this is output to a file as byte data and named appropriately.
There are few things I did not take into account, I blindly assume the input values are correct and the input file is utf-8 encoded.
It turned out my logic behing the first version was failed and it in fact truncates some of the data so here is updated version that tries to fit the data to as much square area as it can.
function ConvertTo-Bitmap ([String]$Source,
[String]$Destination=([IO.Path]::ChangeExtension($Source,'bmp')))
{
$header = 66,77,58,0,0,0,0,0,0,0,54,0,0,0,40,0,0,0,1,0,0,0,
1,0,0,0,1,0,24,0,0,0,0,0,4,0,0,0,0,0,0,0,0,0,0,0,
0,0,0,0,0,0,0,0
$File = Get-Content $Source -Encoding Byte | Select-Object -Skip 3
#file needs to be created of 4 byte groups - pixels
$groupSize = 4
#append filler if needed
$filler = 0
$fillerCount = 4 - ($File.Count % $groupSize)
$file += ,$filler * $fillerCount
#calculate the dimensions of the picture
$pixelCount = $file.Count / $groupSize
$dimensions = Get-Dimensions -Number $pixelCount
$header[18] = $dimensions.Width
$header[22] = $dimensions.Height
Set-Content -path $Destination -Force -Encoding Byte -Value ([byte[]]($header+$File))
}
function Get-Dimensions ([int]$Number) {
$dividers = for ($divider= $number ; $divider -gt 0; $divider)
{
$result = $number / $divider
if ($result -eq [int]$result) { $result }
$divider--
}
#make sure I am working with array - important for 1
$dividers = @($dividers)
$dividersCount =$dividers.count
$half = [Math]::Truncate($dividersCount/2)
if ($dividersCount % 2)
{
$height = $dividers[$half]
$width = $dividers[$half]
}
else
{
$height = $dividers[$half]
$width = $dividers[$half-1]
}
new-object -TypeName psObject -Property @{Height = $height; Width = $width}
}
Convertto-Bitmap -Source ".\Hello.ps1"
Apr 4th
Just a quick post on something I saw lot of people wonder about: How do you hide and show console window when using forms with PowerShell?
The p/Invoke calls required can be found in numerous web resources and also in Poweshell in Action book.
Here is prototype of the soulution:
Add-Type -Name Window -Namespace Console -MemberDefinition '
[DllImport("Kernel32.dll")]
public static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
public static extern bool ShowWindow(IntPtr hWnd, Int32 nCmdShow);
'
function Show-Console {
$consolePtr = [Console.Window]::GetConsoleWindow()
#5 show
[Console.Window]::ShowWindow($consolePtr, 5)
}
function Hide-Console {
$consolePtr = [Console.Window]::GetConsoleWindow()
#0 hide
[Console.Window]::ShowWindow($consolePtr, 0)
}
[void] [System.Reflection.Assembly]::LoadWithPartialName("System.Windows.Forms")
$Form = New-Object System.Windows.Forms.Form
$showButton = New-Object System.Windows.Forms.Button
$showButton.Text = 'ShowConsole'
$showButton.Top = 10
$showButton.Left = 10
$showButton.Width = 100
$showButton.add_Click({Show-Console})
$form.controls.Add($showButton)
$hideButton = New-Object System.Windows.Forms.Button
$hideButton.Text = 'HideConsole'
$hideButton.Top = 60
$hideButton.Left = 10
$hideButton.Width = 100
$hideButton.add_Click({hide-Console})
$form.controls.Add($hideButton)
$Form.ShowDialog()
Description of the functions and more possible values for the ShowWindow can be, as usual, found in Microsoft documentation:
GetConsoleWindow function
ShowWindow function
Mar 22nd
Nowadays I am trying to implement something that includes lot of P/Invoke calls, and several DWORD flags are included in the calls. The DWORD is unsigned 32-bit integer and is usually represented like hexadecimal number – 0x000000ff. At first it all seemed peachy until it turned out that the standard PowerShell converts the hexadecimal representation of number to integer (values less than 0 are possible) and not the unsigned integer (negative numbers are not possible). Let’s see an example:
Here is the maximum value of the DWORD represented in hexadecimal format 0xffffffff. I expect it to be 4294967295 (the same as the maximum value of unsigned 32-bit integer [uint32]::MaxValue) but running it in PowerShell console returns -1, a value clearly not belonging to scope of the UInt32 type.
I realized I need to do the conversion myself and found it can be done easily using static methods of the System.Convert class. In the example I specify a hexadecimal representation of the number as a string and the base of the hexadecimal number which is 16.
[Convert]::ToUInt32("0xffffffff",16)
Note: Changing the base value to 2 enables you to work with binary numbers, changing it to 8 enables you to work with octal number, changing it to 10 makes it work with decimal numbers (default).
This is easy, but inconvenient and error-prone so I created function called ConvertFrom-Dword that validates the input values for me and returns the number as unsigned integer:
function ConvertFrom-Dword
{
[CmdletBinding()]
param (
[Parameter(Mandatory=$true,Position=0,ValueFromPipeline=$true)]
[ValidatePattern("^(0x)?(0+)?[0-9|a-f]{1,8}`$")]
[string]$Value
)
#0xffffffff -band 0xFFFFFFFFL is also possible
$base = 16
Write-Verbose "Converting DWORD value $value to decimal."
[Convert]::ToUInt32($Value,$base)
}
ConvertFrom-Dword -Value 0xffffffff
(ConvertFrom-Dword -Value 0xffffffff).getType().fullname
4294967295
System.UInt32
This is easier but still not perfect, but for the time being it will do.
In real life we can for example take the CreateFile function documented here. By the name of the function you would probably say it is used just to create files, but it is also able to open existing file. When you perform this action you can decide if you need the file opened exclusively (lock it) or if you want to share access to the file with other processes. For that purpose there is parameter dwShareMode you can set by specifying correct “flags” (DWORD values).
The description on the linked page says:
The requested sharing mode of the file or device, which can be read, write, both, delete, all of these, or none (refer to the following table).
0
0×00000000
FILE_SHARE_DELETE
0×00000004
FILE_SHARE_READ
0×00000001
FILE_SHARE_WRITE
0×00000002
If you read carefully you probably wonder how you specify the “both” and “all of them” cases if there are no such items in the table. The solution is connecting them by the “-bor” (bit OR) operator. If you want to share both write and read you do: 0×00000001 –bor 0×00000002. Which returns 3 or 0×00000003 in case you use this little trick.
function ConvertTo-Dword ([uint32]$Number)
{
"0x{0:x8}" -f ($Number)
}
ConvertTo-Dword 3
0x00000003
The operation –bor did, may look like a simple adding but it is not because 1 –bor 1 = 1, instead it does operation on binary representation of the number. Look at the items in the table again. Why is 0×00000003 missing?

Now I see I need some more instruments to work easily with this type of data, back to console…
Mar 1st
Recently I have come across Microsoft Team Foundation Server Service preview. The service is currently offered for free and supports both TFS and Git version control systems. So I have signed up
The only thing that reminded to make use of it was to connect PowerGUI to the service which turned out to be quite trivial. So I will show you how to do step by step:
Some basic documentation on connecting PowerGUI to TFS is provided on the PowerGUI Wiki. To be able to connect to the TFS Service you need newer MSSCCI Provider installed. Both 32-bit and 64-bit version are available on Microsoft pages, but my PowerGUI x64 seems to detect only the 32-bit version.
When you have downloaded and installed the client you need to restart your PowerGUI editor and go to Tools > Options… > Version Control and select Team foundation Server MSSCCI provider as your current provider.
Then, in the main editor window, go to new Version Control menu and choose Get files from Version Control. New windows appears, where you can choose which provider you want to use. You probably have none there yet so you need to add one by clicking Servers… > Add… This will get you to this menu:

Here you have to specify URL to your TFSS account followed by “/DefaultCollection”. Click OK and you should be prompted to login to your Microsoft account:

Logging in takes few moments and then you are hopefully presented with this window:
Just click Cancel to return to the selector:

Click OK here and you are good to go.
In the next step you need to choose local folder to store the data and also choose server path to the project you work on.

Next you need to choose file you will edit, I have chosen to name it hello.ps1.

If you are yet to create one go to your browser, navigate to your TFSS web page and create new Team project.
Now you have your file opened and you can CheckOut, make your changes and CheckIn.
Feb 25th
One of the things that I always wondered about, is how you find out if strict mode is set in PowerShell session. I have put the problem aside until answer was posted to the Powershell.com forum. Finding out where and how the value is stored was the easy part. I just opened the .NET Reflector and looked at the definition of the SetStrictModeCommand. Getting the value from PowerShell session was much harder. Few days of casual digging and it turns out the answer can be retrieved using Reflection. As I am new to the whole Reflection thing it was pretty hard to get the basic principles but fortunately this POSHCode article came to rescue. It contains basically all I needed to extract the info.
function Get-Field
{
[CmdletBinding()]
param (
[Parameter(Position=0,Mandatory=$true)]
$InputObject
)
$type = $InputObject.gettype()
$publicNonPublic = [Reflection.BindingFlags]::Public -bor [Reflection.BindingFlags]::NonPublic
$instance = $publicNonPublic -bor [Reflection.BindingFlags]::Instance
$getField = $instance -bor [Reflection.BindingFlags]::GetField
$fields = $type.GetFields($instance)
$result = @{}
$fields | Foreach-Object { $result[$_.Name] = $type.InvokeMember($_.Name, $getField, $null, $InputObject, $null) }
$result
}
function Get-StrictMode
{
###if strict mode is set return its version, otherwise return nothing
$context = (Get-Field $ExecutionContext)._context
$sessionState = (Get-Field $context)._engineSessionState
$scopes = Get-Field $sessionState
$globalScope = Get-Field ($scopes._globalScope)
#different hosts refer to the field differently trying to fix it
try
{
$version = $globalScope."k__BackingField"
}
catch [System.Management.Automation.RuntimeException]
{
$errorId = $_.Exception.ErrorRecord.FullyQualifiedErrorId
if (-not ($errorId -eq "PropertyNotFoundStrict"))
{
throw $_
}
}
try
{
$version = $globalScope.StrictModeVersion
}
catch [System.Management.Automation.RuntimeException]
{
$errorId = $_.Exception.ErrorRecord.FullyQualifiedErrorId
if (-not ($errorId -eq "PropertyNotFoundStrict"))
{
throw $_
}
}
##Major version is grater than 0 if the strict mode is set
#test if the variable is set first to avoid strict mode error
if ($version)
{
if ($Version.Major -gt 0)
{
$version
}
}
}
Set-StrictMode -Version latest
Get-strictMode
Set-StrictMode -Version 2
Get-strictMode
Set-StrictMode -Version 1
Get-strictMode
Set-StrictMode -Off
Get-strictMode
The code is not perfect because only the state from global scope is retrieved. Ideally it should be retrieved from the current scope, but unfortunately I am unable to do so (Suggestions please.). In the current state the strict mode appears as not set unless you set it in to the global scope. This returns incorrect results:
&{
Set-StrictMode -Version 2
Get-StrictMode
}
Jan 19th
Playing around with exceptions I got really tired of walking up the type tree using .getType().BaseType.GetType().fullname so I decided to create a simple Resolve-Type function. The functions output is not very well formatted, I may improve on this later.
function Resolve-Type
{
param(
[Parameter(ParameterSetName="Type",Position=0)]
[String]
$TypeName,
[Parameter(ParameterSetName="Object",Position=0,ValueFromPipeline=$true)]
[Object]
$InputObject
)
process {
switch ($PsCmdlet.ParameterSetName)
{
"Type" {
if ($TypeName -as [Type])
{
$TypeInfo = [Type]$TypeName
}
else
{
Write-Error 'The specified type is not correct. Make sure the assembly is loaded.'
}
}
"Object" { $TypeInfo = $inputObject.GetType() }
}
Function ResolveCore ([System.Reflection.TypeInfo]$TypeInfo)
{
$TypeInfo.fullname
if ( $TypeInfo.BaseType)
{
$base = $TypeInfo.BaseType.asType()
ResolveCore $base
}
}
#call the recursive function
ResolveCore -TypeInfo $TypeInfo
}
}
Resolve-Type -TypeName IO.File
Example 1: System.IO.File System.Object Example 2: System.Management.Automation.Internal.Host.InternalHost System.Management.Automation.Host.PSHost System.Object Example 3: System.Management.Automation.ItemNotFoundException System.Management.Automation.SessionStateException System.Management.Automation.RuntimeException System.SystemException System.Exception System.Object Example 4: System.String System.Object
Jan 14th
Lurking through the PowerShell inner workings facing problem that you have no clue what caused is not a rare occasion. Tracing the command/expression usually gives you at least a peek on the problem’s domain but there is one slight problem with the Trace-Command cmdlet. The Name parameter produces wast amount of output when used with asterisk wildcard. In this article I used it with success because I was pretty sure what I was looking for. Going through pages of debug output is not my favorite thing to do, so I looked what trace sources produce the most of the amount with the less usefull information and narrowed it down to the ConsoleHost provider and sometimes the Type* providers. Now only if there was a way to get all the sources excluding the listed ones. Looking at the definition of the Name parametes on the Trace-Command cmdlet I see it accepts array of strings as input. From now it is easy:
#filter out unneeded sources
[string]$pattern = 'consolehost|type'
$debugSources = Get-TraceSource | ?{$_.name -notmatch $pattern} | select -ExpandProperty name
#trace the expression
[scriptblock]$expression = {Get-Process | Select-Object -First 1}
Trace-Command -name $DebugSources -pshost -Expression $expression
Fling it into a function to be able to reuse it and finish with this:
function Trace-CommandRestricted ([scriptblock]$expression,[string[]]$Exclude)
{
$patternResult = $Exclude -join '|'
#filter out unneeded sources
$debugSources = Get-TraceSource | ?{$_.name -notmatch $patternResult} | select -ExpandProperty name
Trace-Command -name $DebugSources -pshost -Expression $expression
}
$expression = {Get-Process | Select-Object -First 1}
Trace-CommandRestricted $expression consolehost,type,param
DEBUG: CommandDiscovery Information: 0 : Looking up command: Get-Process
DEBUG: CommandDiscovery Information: 0 : Cmdlet found: Get-Process Microsoft.PowerShell.Commands.GetProcessCommand
DEBUG: CommandDiscovery Information: 0 : Looking up command: Select-Object
DEBUG: CommandDiscovery Information: 0 : Cmdlet found: Select-Object Microsoft.PowerShell.Commands.SelectObjectCommand
Handles NPM(K) PM(K) WS(K) VM(M) CPU(s) Id ProcessName
------- ------ ----- ----- ----- ------ -- -----------
63 3 908 80 33 1444 armsvc