14156

Time oddity

I was looking for a test of time synchronisation on domain controllers and knew that the .NET domain controller object held a system property. So, I cam up with this

$dom = [System.DirectoryServices.ActiveDirectory.Domain]::GetCurrentDomain()
$dom.DomainControllers | Format-Table Name, CurrentTime

What I didn’t realise was that the CurrentTime given by this object reads GMT not your local time. OK, technically its UTC but as I live in the UK its GMT Smile

When you are testing for time synchronisation all you want is differences so the absolute time doesn’t matter so much. If you need the system time as local time

Get-CimInstance -ClassName Win32_OperatingSystem | select LocalDateTime

will find it for you

A count down timer

Last month I started playing with WPF to show how a multi-coloured clock could  be displayed on screen. This was picked up and an easier version was created by Doug Finke using the ShowUI module from codeplex  http://showui.codeplex.com/.

Other links are here

http://msmvps.com/blogs/richardsiddaway/archive/2011/07/08/an-easier-clock.aspx

 

As well as wanting to know the time I often want to set a count down timer.  I did a version of this ages ago using PowerGadgets but  if you don’t have that then we can do something with show UI

Import-Module ShowUI                        
            
function timeleft {            
 $ts = $close - (Get-Date)            
 $time = $ts.ToString() -split "\."            
 $time[0].Substring(3,5)            
}            
            
            
$close = (Get-Date).AddMinutes(35)            
            
$windowAttributes = @{            
    WindowStartupLocation = "CenterScreen"            
    SizeToContent = "WidthAndHeight"            
    WindowStyle = "None"            
    Background = "Transparent "                        
            
    On_MouseRightButtonDown = { Close-Control}            
    On_MouseLeftButtonDown  = { $_.Handled = $true;$window.DragMove() }            
    On_Loaded = {            
        Register-PowerShellCommand -ScriptBlock {            
            $window.Content.Content = timeleft            
        } -Run -In "0:0:0.5"            
    }            
}                        
            
$labelAttributes = @{            
    Content = timeleft            
    FontFamily = "Impact, Arial"            
    FontWeight = 800            
    FontSize = 90            
}                        
            
New-Window @windowAttributes -AllowsTransparency -Show  {            
    Label @labelAttributes -Name Clock -Foreground (            
        LinearGradientBrush $(            
            GradientStop -Color Red    -Offset 1            
            GradientStop -Color Orange -Offset 0.85            
            GradientStop -Color Yellow -Offset 0.7            
            GradientStop -Color Green  -Offset 0.55            
            GradientStop -Color Blue   -Offset 0.4            
            GradientStop -Color Indigo -Offset 0.2            
            GradientStop -Color Violet -Offset 0            
        )            
    )            
}


All I changed was to point the Content to the timeleft function instead of get-date. The function takes the closing time – in this case 35 minute from starting and subtracts the current datetime (from get-date). The resultant timespan is converted to a string and split at the millisecond point. The minutes and seconds are extracted as  a substring to display



By the way – anyone noticed the deliberate(?) mistake with the colours

Using enums

Following on my post about using enums http://msmvps.com/blogs/richardsiddaway/archive/2011/08/02/enums.aspx

I thought it might be fun to see how we can make our own.

If we look at Win32_LogicalDisk

PS > Get-WmiObject Win32_LogicalDisk | ft DeviceID, DriveType -a

DeviceID DriveType
-------- ---------
C:               3
E:               5
F:               5

Now I know that type 3 is a hard drive and type 5 is a CD/DVD but we want to see it in the code. Up to now I’ve used hash tables

$dt = DATA {            
ConvertFrom-StringData -StringData @'
0 = Unknown
2 = Removable Disk 
3 = Local Disk
4 = Network Drive
5 = Compact Disk
6 = RAM Disk
'@            
}             
Get-WmiObject Win32_LogicalDisk |             
select DeviceID,             
@{N="DiskType"; E={$dt["$($_.DriveType)"]}} |            
Format-Table -a


Create a hash table and then use a calculated field to perform the lookup



An alternative is to use an enumeration



Add-Type @"
public enum DiskType : int {
Unknown = 0,
RemovableDisk = 2, 
LocalDisk = 3,
NetworkDrive = 4,
CompactDisk = 5,
RAMDisk = 6 
}
"@            
             
Get-WmiObject Win32_LogicalDisk |             
select DeviceID,             
@{N="DiskType"; E={[disktype]($($_.DriveType))}} |            
Format-Table -a


Notice that the order of values is reversed between the hash table and the enum.



The lookup syntax is slightly easier but the code to set it up is slightly harder. We also need to make sure there aren’t any spaces in the values we are defining.



Hash table or enum which do you want to use? Either give us the answer and both are relatively easy to implement.  It is possible to define multiple enums in one call to Add-Type

Enums

No not something added to your food but .NET enumerations. These are collections of defined values such as the default folder names that we have seen in Outlook. An enumeration (to use the full name) looks a bit like a .NET class and we access it in a similar way.

Add-Type -Assembly Microsoft.Office.Interop.Outlook
[enum]::Getnames("Microsoft.Office.Interop.Outlook.OlDefaultFolders" )

 

shows use the list of names defined in the enum.  Each name is associated with an integer value. Often these start at 0 and increment logically but in enums that have changed over the years or if the developer wanted to do something different the values can vary.

The enum::name syntax can be used to pass the underlying value to a a method or we we can use the value. But  discovering the value isn’t as straight forward 

This code gives one way of doing it

$data = @{}            
Add-Type -Assembly Microsoft.Office.Interop.Outlook            
$olFolders = "Microsoft.Office.Interop.Outlook.OlDefaultFolders" -as [type]            
            
[enum]::Getnames($olFolders) |             
foreach {            
 $data += @{$_ = $olfolders::"$_".value__}            
            
}            
$data.GetEnumerator() | sort Value | Format-Table -AutoSize


 



We just iterate through the enum and get the value__ property of each member



The getenumerator() method of a hash table allows us to sort it prior to display

Moving Windows

In answer to a forum question I started to look at how you could move the PowerShell window from within PowerShell. Its not straight forward as we have to dig into the Win32 APIs

I came up with this code

function move-window {            
param(            
 [int]$newX,            
 [int]$newY            
)             
BEGIN {            
$signature = @'

[DllImport("user32.dll")]
public static extern bool MoveWindow(
    IntPtr hWnd,
    int X,
    int Y,
    int nWidth,
    int nHeight,
    bool bRepaint);

[DllImport("user32.dll")]
public static extern IntPtr GetForegroundWindow();

[DllImport("user32.dll")]
public static extern bool GetWindowRect(
    HandleRef hWnd,
    out RECT lpRect);

public struct RECT
{
    public int Left;        // x position of upper-left corner
    public int Top;         // y position of upper-left corner
    public int Right;       // x position of lower-right corner
    public int Bottom;      // y position of lower-right corner
}

'@            
            
Add-Type -MemberDefinition $signature -Name Wutils -Namespace WindowsUtils             
            
}            
PROCESS{            
 $phandle = [WindowsUtils.Wutils]::GetForegroundWindow()            
            
 $o = New-Object -TypeName System.Object            
 $href = New-Object -TypeName System.RunTime.InteropServices.HandleRef -ArgumentList $o, $phandle            
            
 $rct = New-Object WindowsUtils.Wutils+RECT            
            
 [WindowsUtils.Wutils]::GetWindowRect($href, [ref]$rct)            
             
 $width = $rct.Right - $rct.Left            
 $height = 700            
<#
 $height = $rct.Bottom = $rct.Top
 
 $rct.Right
 $rct.Left
 $rct.Bottom
 $rct.Top
 
 $width
 $height
#>             
 [WindowsUtils.Wutils]::MoveWindow($phandle, $newX, $newY, $width, $height, $true)            
            
}             
}


We start by creating a piece of inline C# that creates a .NET class we can use to call the Win32 API functions. The names of the these functions are self explanatory



The move is accomplished by getting the handle of the foreground window and then creating a handle reference. The current window size is obtained using GetWindowRect – on my Windows 7 machine it doesn’t report the height correctly so I hard code that but calculate the width.



The MoveWindow method can be used to perform the move.  Remember that 0,0 is top left corner of the screen



I’m intrigued as to why the height isn’t reported correctly but haven’t found a good reason

A PowerShell clock

I don’t do much with Windows forms as the vast majority of what I do is at the admin end of the PowerShell spectrum. Every now and then I like to play. A recent post on the forum asking about displaying a clock from PowerShell looked like a good excuse.

I ended up using Windows Presentation Foundation - http://msdn.microsoft.com/en-us/library/ms754130.aspx

save this as clock.xaml

<Window xmlns='http://schemas.microsoft.com/winfx/2006/xaml/presentation'
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        WindowStyle='None' AllowsTransparency='True'
        Topmost='True' Background="Transparent"  ShowInTaskbar='False'
        SizeToContent='WidthAndHeight' WindowStartupLocation='CenterOwner' >
   <Window.Resources>
      <system:String x:Key="Time">12:34.56</system:String>
   </Window.Resources>


   <Grid Height="2.2in">
      <Grid.ColumnDefinitions>
         <ColumnDefinition/>
      </Grid.ColumnDefinitions>
      <Label Name="Clock" Grid.Column="2" Content="{DynamicResource Time}" FontFamily="Impact, Arial" FontWeight="800" FontSize="1in" >
         <Label.Foreground>
            <LinearGradientBrush>
               <GradientStop Color="Red" Offset="1"/>
               <GradientStop Color="Orange" Offset="0.85"/>
               <GradientStop Color="Yellow" Offset="0.7"/>
               <GradientStop Color="Green" Offset="0.55"/>
               <GradientStop Color="Blue" Offset="0.4"/>
               <GradientStop Color="Indigo" Offset="0.2"/>
               <GradientStop Color="Violet" Offset="0"/>
            </LinearGradientBrush>
         </Label.Foreground>
      </Label>
   </Grid>
</Window>

save this as clock.ps1

#Import the WPF assemblies            
 Add-Type -Assembly PresentationFramework            
 Add-Type -Assembly PresentationCore            
             
$clock = [Windows.Markup.XamlReader]::Load(             
         (New-Object System.Xml.XmlNodeReader (            
             [Xml](Get-Content "C:\scripts\wpf\clock.xaml") ) ) )            
             
## Create a script block which will update the UI            
 $counter = 0;            
 $updateBlock = {            
    # Update the clock            
    $clock.Resources["Time"] = [DateTime]::Now.ToString("T")            
 }            
             
## Hook up some event handlers             
$clock.Add_SourceInitialized( {            
    ## Before the window's even displayed ...            
    ## We'll create a timer            
    $timer = new-object System.Windows.Threading.DispatcherTimer            
    ## Which will fire 4 times every second            
    $timer.Interval = [TimeSpan]"0:0:0.25"            
    ## And will invoke the $updateBlock            
    $timer.Add_Tick( $updateBlock )            
    ## Now start the timer running            
    $timer.Start()            
    if( $timer.IsEnabled ) {            
       Write-Host "Clock is running. Don't forget: RIGHT-CLICK to close it."            
    } else {            
       $clock.Close()            
       Write-Error "Timer didn't start"            
    }            
 } )            
             
$clock.Add_MouseLeftButtonDown( {             
   $_.Handled = $true            
    $clock.DragMove() # WPF Magic!            
 } )            
 $clock.Add_MouseRightButtonDown( {             
   $_.Handled = $true            
    $timer.Stop()  # we'd like to stop that timer now, thanks.            
    $clock.Close() # and close the windows            
 } )            
             
## Lets go ahead and invoke that update block             
&$updateBlock            
 ## And then show the window            
 $clock.ShowDialog()


 



A rainbow clock is displayed that updates every second. Right click it to close. Change the size using the FontSize property.



Run from ISE or PowerShell started in STA mode.



This has been adapted from original posts by Oisin and Jaykul



http://nivot.org/nivot2/post/2008/05/23/BackgroundTimerPowerShellWPFWidget.aspx



http://huddledmasses.org/wpf-from-powershell-updating-windows/

Registry 7

One other aspect we need to cover is deleting individual values

001
002
003
$reg = [Microsoft.Win32.Registry]::LocalMachine
$key = $reg.OpenSubKey("Software\PSAM PSAdmins\Test", $true)
$key.DeleteValue("oops")

Open the key for writing and use the DeleteValue() method.

Registry 6

Having created some keys lets add some values

001
002
003
004
005
006
007
008
009
010
011
$reg = [Microsoft.Win32.Registry]::LocalMachine
$key = $reg.OpenSubKey("Software\ITKE PSAdmins", $true)
$key.SetValue("Dword Entry", 567 ,"Dword") 
$key.SetValue("String Entry", "My new string" ,"String") 
$key.SetValue("Expanded String Entry", "%COMPUTERNAME%" ,"ExpandString")

[string[]]$multi = "Z","Y","X"
$key.SetValue("Multi-string Entry", $multi ,"MultiString")

$key = $reg.OpenSubKey("Software\PSAM PSAdmins\Test", $true)
$key.SetValue("Oops", "Didn't mean to do this" ,"String")

This is very similar to the script we used to change the values – open the key for writing and use SetValue().  Just make sure we use the right data type.

Registry 5

Having seen how to read, change and delete keys – lets add some

 

001
002
003
$reg = [Microsoft.Win32.Registry]::LocalMachine
$key = $reg.CreateSubKey("Software\ITKE PSAdmins")
$key2 = $reg.CreateSubKey("Software\PSAM PSAdmins\Test")

Set the hive and call the CreateSubKey method.  Note the second example where we create a further depth of subkeys.  It isn’t necessary to create each step in the chain we can create the lot in one go

Registry 4

Now is the time to delete a registry key

001
002
$reg = [Microsoft.Win32.Registry]::LocalMachine
$key = $reg.DeleteSubKey("Software\ITKE PSAdmins")

If there is a tree of subkeys then we can use the DeleteSubKeyTree() method