Hey, Scripting Guy! Interacting with the Windows PowerShell Console Window
About the author: Tibor Soós is a Windows PowerShell MVP who lives in Hungary.
I started to learn programming in 1982 at the age of 12. My father bought a little Z80 based computer, named Mickey ’80, which was produced by a computer manufacturing plant of an agricultural collective farm. Strange things happened in Hungary, behind the iron curtain, during those times. That computer had a BASIC interpreter, but was not capable of any graphics. It could only draw monochrome characters and some symbols on the monitor. But it was perfect for a beginner like me.
I first met Windows PowerShell 25 years later, and when I looked at the console window, I felt nostalgic. The character-based Windows PowerShell window reminded me of the monitor of the Mickey ’80. So I decided to bring my first simple computer games I wrote in BASIC to life. Those games are mostly based on three capabilities of that computer: the capability to write characters to a specific position of the screen, the ability to check and return the character of the key that was pressed on the keyboard, and the capability to read a character from a specific position on the screen. Unfortunately, none of these features are included in Windows PowerShell, so it was a challenge for me to explore the capabilities of the Windows PowerShell environment and the .NET Framework to find the necessary components to build the required functions.
These functions will not only serve my childish goals. Printing data to a specific position on the console window can be very handy in any script that runs for a longer period of time, and if you want to see some status information in the same position of the window over time without the scrolling of the screen. Checking if a keyboard button is pressed or not and giving back the character of the key can be useful for those scripts, which you want to control by the keyboard but without interrupting its run. For example, doing a bulk change of thousands of files, you just want to see the actual file that your script is working on by a key press. It’s like getting a snapshot of the status by hitting a key. Using the third function in an IT environment needs some more imagination that I don’t possess.
Let’s see the first function:
function WriteTo-Pos ([string] $str, [int] $x = 0, [int] $y = 0,
[string] $bgc = [console]::BackgroundColor,
[string] $fgc = [Console]::ForegroundColor)
if($x -ge 0 -and $y -ge 0 -and $x -le [Console]::WindowWidth -and
$y -le [Console]::WindowHeight)
$saveY = [console]::CursorTop
$offY = [console]::WindowTop
Write-Host -Object $str -BackgroundColor $bgc `
-ForegroundColor $fgc -NoNewline
The main idea behind this is the [console] class. More precisely it is called [system.console], but in Windows PowerShell we can omit the system part. The static properties and methods of this class represent many of the aspects of the Windows PowerShell console window. The only gotcha is that the coordinates of the characters in the window are relative to the whole window buffer, not just to the visible part of the window. So I had to use an offset.
Understanding this, the definition of the WriteTo-Pos function is quite straightforward. The function has a $str string parameter that is the data that should be written to the screen. The coordinates are $x and $y, where the da
ta should be written within the visible part of the screen. The default is the upper left corner of the screen, which is the $x=0 and $y=0 position. There is also an option to set the background and foreground color of the text that will be written.
In the body of the definition, I check if the coordinates fit in the window. After that I save the current cursor position, which is relative to the windows buffer, and I calculate the offset of the y coordinate to the WindowsTop static property of the [console] class.
Then I set the position of the cursor to the proper place within the visible part of console window, and write the $str there in the given colors. With the NoNewLine switch I ensure that the window content will not scroll. At the end, I reset the cursor position back to the saved value.
With this function we can draw wonderful shapes to the console window. For example, this function below produces a circle. The trick here was to find out the aspect ratio of the selected console font, which is stored in the $deform variable and is calculated by the current video resolution data read from WMI and the maximum console window dimensions:
function Draw-Circle ($x, $y, $r)
$step = [int] ([Math]::Atan(1/$r)*30)
$px = (Get-WmiObject -Class Win32_VideoController
$py = (Get-WmiObject -Class Win32_VideoController