Skip to main content

Refreshing Icon Cache

Sometimes, Windows Explorer does not show correct icons. When you update to PowerShell 5.0, for example, both PowerShell and PowerShell ISE got new and modern icons. If you still see the old icons, it may be time to refresh the icon cache.

Here is a function that can do this for you. It utilizes raw Windows API calls and forces the Windows Explorer to rebuild its icon cache.


function Update-ExplorerIcon {
  [CmdletBinding()]
  param()

  $code = @'
private static readonly IntPtr HWND_BROADCAST = new IntPtr(0xffff); 
private const int WM_SETTINGCHANGE = 0x1a; 
private const int SMTO_ABORTIFHUNG = 0x0002; 
 

[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError=true, CharSet=CharSet.Auto)]
 static extern bool SendNotifyMessage(IntPtr hWnd, uint Msg, UIntPtr wParam,
   IntPtr lParam);

[System.Runtime.InteropServices.DllImport("user32.dll", SetLastError = true)] 
  private static extern IntPtr SendMessageTimeout ( IntPtr hWnd, int Msg, IntPtr wParam, string lParam, uint fuFlags, uint uTimeout, IntPtr lpdwResult ); 
 
 
[System.Runtime.InteropServices.DllImport("Shell32.dll")] 
private static extern int SHChangeNotify(int eventId, int flags, IntPtr item1, IntPtr item2);


public static void Refresh()  {
    SHChangeNotify(0x8000000, 0x1000, IntPtr.Zero, IntPtr.Zero);
    SendMessageTimeout(HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, null, SMTO_ABORTIFHUNG, 100, IntPtr.Zero); 
}
'@

  Add-Type -MemberDefinition $code -Namespace MyWinAPI -Name Explorer 
  [MyWinAPI.Explorer]::Refresh()

}

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: https://eventloom.com/event/home/PSNA16

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

Twitter This Tip! ReTweet this Tip!


Posted Jan 19 2016, 06:00 AM by ps1

Formatting Text Output

If you need to return multiple items in a nicely formatted text report, here is a simple trick: get yourself an ordered hash table (supported in PowerShell 3.0 or better), assign the items you want to output, and then convert it into an object. This object can then easily be formatted as text:


$info = [Ordered]@{}

$info.'BIOS Serial' = (Get-WmiObject Win32_BIOS).SerialNumber
$info.'Currently logged-in user' = $env:username
$info.'Date of day' = Get-Date
$info.Remark = 'Some remark'

New-Object PSObject -Property $info | Format-List | Out-String

The result looks like this:


 
BIOS Serial              : 5TQLM32
Currently logged-in user : Tobias
Date of day              : 05.01.2016 11:52:02
Remark                   : Some remark 
 

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: https://eventloom.com/event/home/PSNA16

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

Twitter This Tip! ReTweet this Tip!


Posted Jan 18 2016, 06:00 AM by ps1

Pinging Multiple Systems Fast

Test-Connection can ping multiple computers only sequentially, and it does not let you specify a timeout. So when you need to check a large number of systems, it is very slow.

A much faster way is to use the underlying WMI and instruct it to ping multiple systems simultaneously:


$ComputerName = 'powertheshell.com', 'powershellmagazine.com', 'powershell.com'
$Timeout = 2000
$filter = 'Address="' + ($ComputerName -join """ and Timeout=$Timeout or Address=""") + """ and Timeout=$Timeout"

Get-WmiObject -Class Win32_PingStatus -Filter $filter |
  Select-Object -Property Address, ProtocolAddress, ResponseTime, Timeout

The result comes in almost instantaneously:


 
Address                ProtocolAddress             ResponseTime Timeout
-------                ---------------             ------------ -------
powertheshell.com      2400:cb00:2048:1::6818:7c40           27    2000
powershellmagazine.com 206.217.196.220                      117    2000
powershell.com         65.38.114.170                        161    2000 
 

When you look at the constructed WMI filter, it looks like this:


 
PS C:> $Filter
Address="powertheshell.com" and Timeout=2000 or Address="powershellmagazine.com" and Timeout=2000 or Address="powershell.com" and Timeout=2000 
 

As you see, you can set the timeout for each individual computer.

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!


Posted Jan 15 2016, 06:00 AM by ps1

Test-Connection with Timeout

The Test-Connection cmdlet implements a simple ping to check whether a system responds to an ICMP request. Unfortunately, you cannot specify a timeout. Test-Connection defaults to a static timeout of 4 seconds:


 
PS C:> Test-Connection -ComputerName powershellmagazine.com -Count 1 | Select-Object Address, ResponseTime, Timeout


Address                ResponseTime Timeout
-------                ------------ -------
powershellmagazine.com          118    4000 
 

To use a timeout, simply use the underlying WMI query like this:


$ComputerName = 'powershellmagazine.com'
$Count = 1
$Timeout = 1000
$Filter = 'Address="{0}" and Timeout={1}' -f $ComputerName, $Timeout
Get-WmiObject -Class Win32_PingStatus -Filter $Filter |
  Select-Object Address, ResponseTime, Timeout

Here is the result:


 
Address                ResponseTime Timeout
-------                ------------ -------
powershellmagazine.com          118    1000 
 

To ping multiple times, simply use a loop.

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!


Posted Jan 14 2016, 06:00 AM by ps1

Pinging Computers

In Windows 8/Server 2012 and better, there is a new cmdlet called Test-NetConnection which lets you check whether a system responds to a given port.


 
PS C:> Test-NetConnection -ComputerName powershellmagazine.com -CommonTCPPort HTTP


ComputerName           : powershellmagazine.com
RemoteAddress          : 206.217.196.220
RemotePort             : 80
InterfaceAlias         : Wi-Fi
SourceAddress          : 192.168.2.105
PingSucceeded          : True
PingReplyDetails (RTT) : 117 ms
TcpTestSucceeded       : True




PS C:> Test-NetConnection -ComputerName powershellmagazine.com -CommonTCPPort WINRM
WARNING: TCP connect to  powershellmagazine.com:5985 failed


ComputerName           : powershellmagazine.com
RemoteAddress          : 206.217.196.220
RemotePort             : 5985
InterfaceAlias         : Wi-Fi
SourceAddress          : 192.168.2.105
PingSucceeded          : True
PingReplyDetails (RTT) : 118 ms
TcpTestSucceeded       : False




PS C:> Test-NetConnection -ComputerName powershellmagazine.com -Port 445
WARNING: TCP connect to  powershellmagazine.com:445 failed


ComputerName           : powershellmagazine.com
RemoteAddress          : 206.217.196.220
RemotePort             : 445
InterfaceAlias         : Wi-Fi
SourceAddress          : 192.168.2.105
PingSucceeded          : True
PingReplyDetails (RTT) : 118 ms
TcpTestSucceeded       : False 
 

Either use -CommonTCPPort and specify a commonly used port by friendly name, or use -Port and specify a port number. Note that you will always get a response. Check TcpTestSucceeded for $true to determine whether the port responded.

If you do not specify a port, the cmdlet uses a classic ICMP ping request.

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!


Posted Jan 13 2016, 06:00 AM by ps1

Correct Encoding with PowerShell Remoting

When you run a native console command via PowerShell remoting, special characters like German Umlauts will be damaged because remoting uses a rather limited encoding.


$command = { systeminfo.exe /FO CSV | ConvertFrom-Csv  }
Invoke-Command -ScriptBlock $command -ComputerName Server01

To correct this, you can run the native console command inside a background job on the remote machine. When you do this, the command is executed by a regular PowerShell, and encoding is corrected:


$command = { systeminfo.exe /FO CSV | ConvertFrom-Csv  }

$remotecode = 
{ 
    param($Code)
    $job = Start-Job ([ScriptBlock]::Create($Code)) -Name Job1
    $null = Wait-Job $job 
    Receive-Job -Name Job1
    Remove-Job -Name Job1
}  

Invoke-Command -ComputerName Server01 -ScriptBlock $remotecode -ArgumentList $command

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!


Posted Jan 12 2016, 06:00 AM by ps1

Finding Current File System Path

PowerShell supports not just the file system, so you can set the current path to a different provider (Set-Location). Here is a trick that always gets you the current file system location no matter which provider is currently active:


 
PS C:> cd hkcu:

PS HKCU:> $ExecutionContext.SessionState.Path

CurrentLocation CurrentFileSystemLocation
--------------- -------------------------
HKCU:          C:                      



PS HKCU:> $ExecutionContext.SessionState.Path.CurrentFileSystemLocation

Path
----
C: 



PS HKCU:> $ExecutionContext.SessionState.Path.CurrentFileSystemLocation.Path
C: 
 

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!


Posted Jan 11 2016, 06:00 AM by ps1

Creating New Objects by Hash Table Conversion

Beginning in PowerShell 3.0, you can create pre-initialized objects by using a hash table. Simply add the properties you want to preinitialize, then convert the hash table to the desired type.

Here is a practical example:


#requires -Version 3

$preInit = @{
  Rate = -10
  Volume = 100
}

Add-Type -AssemblyName System.Speech
$speaker = [System.Speech.Synthesis.SpeechSynthesizer] $preInit
$null = $Speaker.SpeakAsync(Oh boy, that was a New Years party. I guess I need a little break.”)

When you run this code, PowerShell creates a new System.Speech object and preinitializes the values for rate and volume. When you output text to speech using the SpeakAsync() method, the text is spoken very slowly. Rate accepts values between -10 and 10.

Twitter This Tip! ReTweet this Tip!


Posted Jan 08 2016, 06:00 AM by ps1

Use Get-CimInstance with DCOM

PowerShell 3.0 added an alternative to Get-WmiObject: Get-CimInstance seems to work very similar and can retrieve information from the internal WMI service:


 
PS C:> Get-WmiObject -Class Win32_BIOS

SMBIOSBIOSVersion : A03
Manufacturer      : Dell Inc.
Name              : A03
SerialNumber      : 5TQLM32
Version           : DELL   - 1072009

PS C:> Get-CimInstance -Class Win32_BIOS

SMBIOSBIOSVersion : A03
Manufacturer      : Dell Inc.
Name              : A03
SerialNumber      : 5TQLM32
Version           : DELL   - 1072009 
 

While Get-WmiObject still works, Get-CimInstance is definitely the way to go. This cmdlet supports IntelliSense completion for WMI classes (in PowerShell ISE), and the returned data has a more readable format: dates for example appear as human readible dates whereas Get-WmiObject displays the internal raw WMI date format.

The most important difference, though, is how they work across remote connections. Get-WmiObject always uses the old DCOM protocol whereas Get-CimInstance defaults to the new WSMan protocol, yet is flexible and can still fall back to DCOM when needed.

Here is a sample function that retrieves BIOS information remotely via Get-CimInstance. The function defaults to DCOM, and with the -Protocol parameter you can choose the protocol you'd like to use:


#requires -Version 3
function Get-BIOS
{
    param
    (
        $ComputerName = $env:COMPUTERNAME,
        
        [Microsoft.Management.Infrastructure.CimCmdlets.ProtocolType]
        $Protocol = 'DCOM'
    )
    $option = New-CimSessionOption -Protocol $protocol
    $session = New-CimSession -ComputerName $ComputerName -SessionOption $option
    Get-CimInstance -CimSession $session -ClassName Win32_BIOS
}

Twitter This Tip! ReTweet this Tip!


Posted Jan 07 2016, 06:00 AM by ps1