Sending Objects to Notepad

In a previous tip we showed how you can send text to a fresh Notepad instance. Today, you get an enhanced version of Out-Notepad: you can pipe anything to Notepad now. If it is not a string, Out-Notepad uses the internal PowerShell ETS to convert it to text and show it appropriately:

#requires -Version 2
function Out-Notepad
{
  param
  (
    [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
    [Object]
    [AllowEmptyString()] 
    $Object,

    [Int]
    $Width = 150
  )

  begin
  {
    $al = New-Object System.Collections.ArrayList
  }

  process
  {
    $null = $al.Add($Object)
  }
  end
  {
    $text = $al | 
    Format-Table -AutoSize -Wrap | 
    Out-String -Width $Width

    $process = Start-Process notepad -PassThru
    $null = $process.WaitForInputIdle()


    $sig = '
      [DllImport("user32.dll", EntryPoint = "FindWindowEx")]public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
      [DllImport("User32.dll")]public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
    '

    $type = Add-Type -MemberDefinition $sig -Name APISendMessage2 -PassThru
    $hwnd = $process.MainWindowHandle
    [IntPtr]$child = $type::FindWindowEx($hwnd, [IntPtr]::Zero, "Edit", $null)
    $null = $type::SendMessage($child, 0x000C, 0, $text)
  }
}

You can pipe anything to Out-Notepad. If it is text already, it is displayed as-is:

 
PS C:\> Get-Content $env:windir\system32\drivers\etc\hosts | Out-Notepad
 

If you pipe object results, Out-Notepad converts them to text and makes sure nothing is cut off. You may want to play with the -Width parameter to choose the page width that works best for you:

 
PS C:\> Get-EventLog -LogName System -EntryType Error, Warning -Newest 10 | Out-Notepad -Width 130 
 

And you may want to maximize Notepad or disable word wrap to see the correct formatting.

 

Throughout this month, we'd like to point you to three awesome community-driven global PowerShell events taking place this year:

Europe: April 20-22: 3-day PowerShell Conference EU in Hannover, Germany, with more than 30+ speakers including Jeffrey Snover and Bruce Payette, and 60+ sessions: www.psconf.eu.

Asia: October 21-22: 2-day PowerShell Conference Asia in Singapore. Watch latest announcements at www.psconf.asia

North America: April 4-6: 3-day PowerShell and DevOps Global Summit in Bellevue, WA, USA with 20+ speakers including many PowerShell Team members: http://ift.tt/1H0kaCO

All events have limited seats available so you may want to register early.

Twitter This Tip! ReTweet this Tip!

Friday, February 12, 2016
Posted by AsifSaif

Send Text to Notepad

Notepad can be used to display text results. Typically, you would need to save text results to file, then have Notepad open that file. There is a better way, though: launch an empty Notepad, and send the text via Windows messages directly to the untitled Notepad editor.

Here is a function called Out-Notepad. Whatever text you submit to this function: it will be shown in an untitled instance of Notepad:

#requires -Version 2
function Out-Notepad
{
  param
  (
    [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
    [String]
    [AllowEmptyString()] 
    $Text
  )

  begin
  {
    $sb = New-Object System.Text.StringBuilder
  }

  process
  {
    $null = $sb.AppendLine($Text)
  }
  end
  {
    $text = $sb.ToString()

    $process = Start-Process notepad -PassThru
    $null = $process.WaitForInputIdle()


    $sig = '
      [DllImport("user32.dll", EntryPoint = "FindWindowEx")]public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
      [DllImport("User32.dll")]public static extern int SendMessage(IntPtr hWnd, int uMsg, int wParam, string lParam);
    '

    $type = Add-Type -MemberDefinition $sig -Name APISendMessage -PassThru
    $hwnd = $process.MainWindowHandle
    [IntPtr]$child = $type::FindWindowEx($hwnd, [IntPtr]::Zero, "Edit", $null)
    $null = $type::SendMessage($child, 0x000C, 0, $text)
  }
}

And here are a couple of sample calls:

 
PS C:\> Get-Content $env:windir\system32\drivers\etc\hosts | Out-Notepad

PS C:\> Get-Process | Out-String | Out-Notepad 
 

 

Throughout this month, we'd like to point you to three awesome community-driven global PowerShell events taking place this year:

Europe: April 20-22: 3-day PowerShell Conference EU in Hannover, Germany, with more than 30+ speakers including Jeffrey Snover and Bruce Payette, and 60+ sessions: www.psconf.eu.

Asia: October 21-22: 2-day PowerShell Conference Asia in Singapore. Watch latest announcements at www.psconf.asia

North America: April 4-6: 3-day PowerShell and DevOps Global Summit in Bellevue, WA, USA with 20+ speakers including many PowerShell Team members: http://ift.tt/1H0kaCO

All events have limited seats available so you may want to register early.

Twitter This Tip! ReTweet this Tip!

Thursday, February 11, 2016
Posted by AsifSaif

Magic Underscore Variable

Here is a very special (and very underdocumented) way to use PowerShell parameters. Have a look at this function:

#requires -Version 2

function Test-DollarUnderscore
{
  param
  (
    [Parameter(Mandatory=$true, ValueFromPipelineByPropertyName=$true)]
    [string]
    $Test
  )

  process
  {
    "received: $Test"
  }
}

It does not seem to be very unusual at first. You can assign values to the -Test parameter, and the function returns them:

 
PS C:\> Test-DollarUnderscore -Test 'Some Data'
received: Some Data 
 

But now check out what happens when you pipe data into the function:

 
PS C:\> 1..4 | Test-DollarUnderscore -Test { "I am receiving $_" }
received: I am receiving 1
received: I am receiving 2
received: I am receiving 3
received: I am receiving 4 
 

The -Test parameter suddenly and automagically accepts script blocks (although the assigned type was a string), and inside of the script block, you have access to the incoming pipeline element.

You get this very special parameter support when you set ValueFromPipelineByPropertyName=$true with a mandatory paramater, and the incoming data has no property that matches the parameter.

 

Throughout this month, we'd like to point you to two awesome community-driven global PowerShell events taking place this year:

Europe: April 20-22: 3-day PowerShell Conference EU in Hannover, Germany, with more than 30+ speakers including Jeffrey Snover and Bruce Payette, and 60+ sessions (www.psconf.eu).

Asia: October 21-22: 2-day PowerShell Conference Asia in Singapore. Watch latest annoncements at www.psconf.asia

Both events have limited seats available so you may want to register early.

Twitter This Tip! ReTweet this Tip!

Wednesday, February 10, 2016
Posted by AsifSaif

Converting Currencies

PowerShell is an extremely powerful language and can access web services and web pages. If you combine that with dynamic parameters, you get a professional currency converter with real-time exchange rate support.

Here is the ConvertTo-Euro function that takes values from other currencies and converts them to EUR. The function has the -Currency parameter that is populated dynamically by the currencies supported by the European Central Bank.

function ConvertTo-Euro
{
  [CmdletBinding()]
  param(
    [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
    [Double]
    $Value
  )

  dynamicparam
  {
    $Bucket = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameterDictionary

    $Attributes = New-Object -TypeName System.Collections.ObjectModel.Collection[System.Attribute]    
    $AttribParameter = New-Object System.Management.Automation.ParameterAttribute
    $AttribParameter.Mandatory = $true
    $Attributes.Add($AttribParameter)
    
    if ($script:currencies -eq $null)
    {
      $url = 'http://ift.tt/1hdt40r'
      $result = Invoke-RestMethod  -Uri $url
      $script:currencies = $result.Envelope.Cube.Cube.Cube.currency
    }
    
    $AttribValidateSet = New-Object System.Management.Automation.ValidateSetAttribute($script:currencies)
    $Attributes.Add($AttribValidateSet)

    $Parameter = New-Object -TypeName System.Management.Automation.RuntimeDefinedParameter('Currency',[String], $Attributes)
    $Bucket.Add('Currency', $Parameter)

    $Bucket
  }

  begin
  {
    foreach ($key in $PSBoundParameters.Keys)
    {
      if ($MyInvocation.MyCommand.Parameters.$key.isDynamic)
      {
        Set-Variable -Name $key -Value $PSBoundParameters.$key
      }
    }
  
    $url = 'http://ift.tt/1hdt40r'
    $rates = Invoke-RestMethod  -Uri $url
    $rate = $rates.Envelope.Cube.Cube.Cube | 
    Where-Object { $_.currency -eq $Currency} |
    Select-Object -ExpandProperty Rate
  }

  process
  {
    $result = [Ordered]@{
      Value = $Value
      Currency = $Currency
      Rate = $rate
      Euro = ($Value / $rate)
      Date = Get-Date
    }
    
    New-Object -TypeName PSObject -Property $result
  }
}

The function illustrates how a dynamic parameter can be populated by dynamic data, and how this data is cached so IntelliSense won't trigger a new retrieval all the time.

Here is some sample output you can expect (provided you have Internet access):

 
PS C:\> 100, 66.9 | ConvertTo-Euro -Currency DKK


Value    : 100
Currency : DKK
Rate     : 7.4622
Euro     : 13,4008737369677
Date     : 26.01.2016 21:32:44

Value    : 66,9
Currency : DKK
Rate     : 7.4622
Euro     : 8,96518453003136
Date     : 26.01.2016 21:32:45




PS C:\>  ConvertTo-Euro -Currency USD -Value 99.78


Value    : 99,78
Currency : USD
Rate     : 1.0837
Euro     : 92,0734520623789
Date     : 26.01.2016 21:33:01
 

 

Throughout this month, we'd like to point you to three awesome community-driven global PowerShell events taking place this year:

Europe: April 20-22: 3-day PowerShell Conference EU in Hannover, Germany, with more than 30+ speakers including Jeffrey Snover and Bruce Payette, and 60+ sessions: www.psconf.eu.

Asia: October 21-22: 2-day PowerShell Conference Asia in Singapore. Watch latest announcements at www.psconf.asia

North America: April 4-6: 3-day PowerShell and DevOps Global Summit in Bellevue, WA, USA with 20+ speakers including many PowerShell Team members: http://ift.tt/1H0kaCO

All events have limited seats available so you may want to register early.

Twitter This Tip! ReTweet this Tip!

Tuesday, February 9, 2016
Posted by AsifSaif

Counting Pages in a Word Document

Let's assume you have a bunch of Word files and would like to know how much pages they contain. Here is a function that takes the path to one Word file and determines its page count:

#requires -Version 1

# adjust path to point to an existing Word file:
$Path = "C:\...\SomeChapter.doc"
$word = New-Object -ComObject Word.Application
$word.Visible = $true
$binding = 'System.Reflection.BindingFlags' -as [type]
$doc = $word.Documents.Open($Path)
$doc.Repaginate()
$prop = $doc.BuiltInDocumentProperties(14)
$pages = [System.__ComObject].invokemember('value',$binding::GetProperty,$null,$prop,$null)
$doc.Close(0)
$word.Quit()
"$Path has $Pages pages."

If it works for you, turn it into a function so you can use PowerShell to sum up the pages of many Word documents.

 

Throughout this month, we'd like to point you to two awesome community-driven global PowerShell events taking place this year:

Europe: April 20-22: 3-day PowerShell Conference EU in Hannover, Germany, with more than 30+ speakers including Jeffrey Snover and Bruce Payette, and 60+ sessions (www.psconf.eu).

Asia: October 21-22: 2-day PowerShell Conference Asia in Singapore. Watch latest annoncements at www.psconf.asia

Both events have limited seats available so you may want to register early.

Twitter This Tip! ReTweet this Tip!

Monday, February 8, 2016
Posted by AsifSaif

Bringing Window in the Foreground

PowerShell can use Add-Type to access internal Windows API functions. This way, it is easy to bring any process window into the foreground. Here is the function you need:

#requires -Version 2
function Show-Process($Process, [Switch]$Maximize)
{
  $sig = '
    [DllImport("user32.dll")] public static extern bool ShowWindowAsync(IntPtr hWnd, int nCmdShow);
    [DllImport("user32.dll")] public static extern int SetForegroundWindow(IntPtr hwnd);
  '
  
  if ($Maximize) { $Mode = 3 } else { $Mode = 4 }
  $type = Add-Type -MemberDefinition $sig -Name WindowAPI -PassThru
  $hwnd = $process.MainWindowHandle
  $null = $type::ShowWindowAsync($hwnd, $Mode)
  $null = $type::SetForegroundWindow($hwnd) 
}

To test-drive Show-Process, here is a sample code on how to use it:

# launch Notepad minimized, then make it visible
$notepad = Start-Process notepad -WindowStyle Minimized -PassThru
Start-Sleep -Seconds 2
Show-Process -Process $notepad
# switch back to PowerShell, maximized
Start-Sleep -Seconds 2
Show-Process -Process (Get-Process -Id $PID) -Maximize
# switch back to Notepad, maximized
Start-Sleep -Seconds 2
Show-Process -Process $notepad -Maximize
# switch back to PowerShell, normal window
Start-Sleep -Seconds 2
Show-Process -Process (Get-Process -Id $PID)

 

Throughout this month, we'd like to point you to three awesome community-driven global PowerShell events taking place this year:

Europe: April 20-22: 3-day PowerShell Conference EU in Hannover, Germany, with more than 30+ speakers including Jeffrey Snover and Bruce Payette, and 60+ sessions: www.psconf.eu.

Asia: October 21-22: 2-day PowerShell Conference Asia in Singapore. Watch latest announcements at www.psconf.asia

North America: April 4-6: 3-day PowerShell and DevOps Global Summit in Bellevue, WA, USA with 20+ speakers including many PowerShell Team members: http://ift.tt/1H0kaCO

All events have limited seats available so you may want to register early.

Twitter This Tip! ReTweet this Tip!

Friday, February 5, 2016
Posted by AsifSaif

Process Data (Part 3)

In parts 1 and 2, you learned how a PowerShell function can process information that was submitted to parameters or piped via the pipeline. In our third part, we'd like to show how a function can receive text lines and produces one string from it.

Here is a function that accepts text lines both via parameter and pipeline. The function uses a StringBuilder object to collect all incoming text lines, and then produces one string from all lines received:

#requires -Version 2
function Collect-Text
{
  param
  (
    [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
    [String[]]
    [AllowEmptyString()] 
    $Text
  )

  begin
  {
    $sb = New-Object System.Text.StringBuilder
  }

  process
  {
   foreach ($line in $Text)
   {
     $null = $sb.AppendLine($line)
   }
  }
  end
  {
    $result = $sb.ToString()
    $result
  }
}

Note how you can submit text lines either via parameter or via pipeline:

  
PS C:\> Collect-Text -Text 'Line 1', '', 'Line 2'
Line 1

Line 2


PS C:\> 'Line 1', '', 'Line 2' | Collect-Text 
Line 1

Line 2 
 

Take a look at the parameter: it uses the attribute [AllowEmptyString()]. This makes sure the parameter can also accept empty strings. Empty strings are not allowed for mandatory parameters without this attribute.

 

Throughout this month, we'd like to point you to three awesome community-driven global PowerShell events taking place this year:

Europe: April 20-22: 3-day PowerShell Conference EU in Hannover, Germany, with more than 30+ speakers including Jeffrey Snover and Bruce Payette, and 60+ sessions: www.psconf.eu.

Asia: October 21-22: 2-day PowerShell Conference Asia in Singapore. Watch latest announcements at www.psconf.asia

North America: April 4-6: 3-day PowerShell and DevOps Global Summit in Bellevue, WA, USA with 20+ speakers including many PowerShell Team members: http://ift.tt/1H0kaCO

All events have limited seats available so you may want to register early.

Twitter This Tip! ReTweet this Tip!

Thursday, February 4, 2016
Posted by AsifSaif

Process Data (Part 2)

In part 1 we showed how a PowerShell function can receive input both from a parameter and via the pipeline, and process it in real-time. This is the most efficient way as it minimizes memory consumption.

Sometimes, however, it is necessary to first collect all data, and once all data is received, process all of it in one step. Here is a function that collects all received data and starts to process it only when all data has arrived:

#requires -Version 2
function Collect-Data
{
  param
  (
    [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
    [Object]
    [AllowEmptyString()] 
    $Object
  )

  begin
  {
    $bucket = New-Object System.Collections.ArrayList
  }

  process
  {
    $null = $bucket.Add($Object)
  }
  end
  {
    $count = $bucket.Count
    Write-Host "Received $count objects." -ForegroundColor Yellow
    $bucket | Out-String
  }
}

Note how Collect-Data can receive information both via parameter and pipeline:

  
PS C:\>  Collect-Data -Object 1,2,3
Received 3 objects. 
1
2
3
 
 
PS C:\> 1..3 |  Collect-Data
Received 3 objects. 
1
2
3 
 

There are two things worth mentioning: never use a plain array to collect information. Rather, use an ArrayList object because it adds new elements much faster. And try to avoid the $input automatic variable that sometimes is used for a similar purpose. $input works with pipeline input only and ignores values submitted to parameters.

 

Throughout this month, we'd like to point you to three awesome community-driven global PowerShell events taking place this year:

Europe: April 20-22: 3-day PowerShell Conference EU in Hannover, Germany, with more than 30+ speakers including Jeffrey Snover and Bruce Payette, and 60+ sessions: www.psconf.eu.

Asia: October 21-22: 2-day PowerShell Conference Asia in Singapore. Watch latest announcements at www.psconf.asia

North America: April 4-6: 3-day PowerShell and DevOps Global Summit in Bellevue, WA, USA with 20+ speakers including many PowerShell Team members: http://ift.tt/1H0kaCO

All events have limited seats available so you may want to register early.

Twitter This Tip! ReTweet this Tip!

Wednesday, February 3, 2016
Posted by AsifSaif

Processing Data (Part 1)

This is the first of the three tips showing you how a PowerShell function can accept data via pipeline or parameter.

In part 1, the function processes the incoming information in real-time. This minimizes memory consumption and provides rapid results:

#requires -Version 2
function Process-Data
{
  param
  (
    [Parameter(Mandatory=$true, ValueFromPipeline=$true)]
    [Object[]]
    $Object
  )

  process
  {
    foreach ($element in $Object)
    {
      "Processing received element $element..."    
    }
  }
}

Note how you can call the function via parameter:

  
PS C:\> Process-Data -Object 1
Processing received element 1...

PS C:\> Process-Data -Object 1,2,3,4
Processing received element 1...
Processing received element 2...
Processing received element 3...
Processing received element 4... 
 

You can also pipe information via pipeline:

  
PS C:\> 1..4 | Process-Data
Processing received element 1...
Processing received element 2...
Processing received element 3...
Processing received element 4... 
 

 

Throughout this month, we'd like to point you to three awesome community-driven global PowerShell events taking place this year:

Europe: April 20-22: 3-day PowerShell Conference EU in Hannover, Germany, with more than 30+ speakers including Jeffrey Snover and Bruce Payette, and 60+ sessions: www.psconf.eu.

Asia: October 21-22: 2-day PowerShell Conference Asia in Singapore. Watch latest announcements at www.psconf.asia

North America: April 4-6: 3-day PowerShell and DevOps Global Summit in Bellevue, WA, USA with 20+ speakers including many PowerShell Team members: http://ift.tt/1H0kaCO

All events have limited seats available so you may want to register early.

Twitter This Tip! ReTweet this Tip!

Tuesday, February 2, 2016
Posted by AsifSaif

Get UI Information for Processes

PowerShell can use UIAutomation calls to find out useful UI information about any process. You can find out whether a process accepts keyboard input, whether it is currently visible (and what its window dimensions are), and whether it is a native Win32 application or uses WPF--among other things.

Here is an example that examines two processes: the PowerShell window, and a minimized Notepad:

#requires -Version 2
function Get-ProcessInfoUI($Process)
{
  foreach ($proc in $Process)
  {
    Add-Type -AssemblyName UIAutomationClient
    [System.Windows.Automation.AutomationElement]::FromHandle($proc.MainWindowHandle).Current |
    Select-Object -Property Name, HasKeyboardFocus, IsOffscreen, BoundingRectangle, NativeWindowHandle, ProcessId, Orientation, FrameworkId
  }
}

# get current PowerShell process:
$ise = Get-Process -Id $pid
# start new Notepad and wait until loaded:
$notepad = Start-Process notepad -WindowStyle Minimized -PassThru
$null = $notepad.WaitForInputIdle()

# get UI information for these processes
Get-ProcessInfoUI $ise, $notepad | Out-GridView -Title 'UI Information'

The output is shown in a grid view window but can also be output to your console:

  
Name               : Windows PowerShell ISE-Mainwindow
HasKeyboardFocus   : False
IsOffscreen        : False
BoundingRectangle  : -9;-9;1618;868
NativeWindowHandle : 131848
ProcessId          : 4428
Orientation        : None
FrameworkId        : WPF

Name               : Untitled - Editor
HasKeyboardFocus   : False
IsOffscreen        : False
BoundingRectangle  : Empty
NativeWindowHandle : 593514
ProcessId          : 2924
Orientation        : None
FrameworkId        : Win32 
 

 

Throughout this month, we'd like to point you to three awesome community-driven global PowerShell events taking place this year:

Europe: April 20-22: 3-day PowerShell Conference EU in Hannover, Germany, with more than 30+ speakers including Jeffrey Snover and Bruce Payette, and 60+ sessions: www.psconf.eu.

Asia: October 21-22: 2-day PowerShell Conference Asia in Singapore. Watch latest announcements at www.psconf.asia

North America: April 4-6: 3-day PowerShell and DevOps Global Summit in Bellevue, WA, USA with 20+ speakers including many PowerShell Team members: http://ift.tt/1H0kaCO

All events have limited seats available so you may want to register early.

Twitter This Tip! ReTweet this Tip!

Monday, February 1, 2016
Posted by AsifSaif

Recent Certifications

Popular Post

Recent Tweets

(c) 2011 - Asif R. Saif. Powered by Blogger.

- Copyright © Asif Saif -