Set a hotkey press event for the form - powershell

Use the following code to set hotkey press event for the form, but when the hotkey is pressed, the system has an alarm sound, why?
In addition, how to set multiple modifier keys, For example ctrl+alt+shift+Q
$form1_KeyDown = [System.Windows.Forms.KeyEventHandler]{
#Event Argument: $_ = [System.Windows.Forms.KeyEventArgs]
if ($_.Alt -and $_.KeyCode -eq 'Q')
{
Write-Host 'Alt-Q pressed'
}
}

I would not know why you get a system beep when pressing a hotkey, but it may have to do with the way you add the KeyEventHandler to the form.
Using $form1.Add_KeyDown({..}) gives me no problem whatsoever:
$form1.Add_KeyDown({
# create a small array to capture the modifier keys used
$modifiers = #()
if ($_.Shift) { $modifiers += "Shift" }
if ($_.Alt) { $modifiers += "Alt" }
if ($_.Control) { $modifiers += "Control" }
# using that array, build part of the output text
$modkeys = ''
if ($modifiers.Count) {
$modkeys = '{0} ' -f ($modifiers -join ' + ')
}
# instead of building up a string 'Shift + Control + Alt', like above, you can also use
# the $_.Modifiers property and replace the commas by a plus sign like this:
# $modkeys = $_.Modifiers -replace ', ', ' + '
# these are the keys you want to react upon as example
# here all keys pressed simply do the same thing, namely display what was pressed,
# so we can shorten the code to simply test if any of the keys in the
# array correspond with the key the user pressed.
if ('Q','A','F1','Escape','NumLock' -contains $_.KeyCode) {
# we create the output string by making use of the '-f' Format operator in POwershell.
# you can read more about that here for instance:
# https://social.technet.microsoft.com/wiki/contents/articles/7855.powershell-using-the-f-format-operator.aspx
Write-Host ('{0}{1} pressed' -f $modkeys, $_.KeyCode)
# The same could have been done with something like:
# Write-Host ($modkeys + ' ' + $_.KeyCode + ' pressed')
# or
# Write-Host "$modkeys $($_.KeyCode) pressed"
}
})

Related

Powershell : Update properties file with key = value

My requirement is, I have a properties file say C:\google\configuration\backup\configuration.properties
with content shown below
backup.path = C:\\ProgramData\\google\\backup
backup.volume.guid = \\\\?\\Volume{49e5d325-8065-49f4-bf0d-r4be94cc1feb}\\
backup.max.count = 10
I have a method that takes key and value as input.
function Script:change_or_replace_value([string]$key, [string]$value) {
$origional_file_content = Get-Content $CONF_FILE_LOCATION
$key_value_map = ConvertFrom-StringData($origional_file_content -join [Environment]::NewLine)
$old_value = $key_value_map.$key
$Old_file_pattern = "$key = $old_value"
$new_file_pattern = "$key = $value"
$origional_file_content | ForEach-Object {$_ -Replace $Old_file_pattern, $new_file_pattern} | Set-Content $NEW_FILE_LOCATION
}
If key is "backup.volume.guid" and value is "\\?\Volume{111111-222-222-444-r4be94cc1feb}\"
method should replace the text
backup.path = C:\\ProgramData\\google\\backup
backup.volume.guid = \\\\?\\Volume{111111-222-222-444-r4be94cc1feb}\\
backup.max.count = 10
If key is "backup.volume.guid" and value is "" method should remove the line
backup.path = C:\\ProgramData\\google\\backup
backup.max.count = 10
If the value is empty delete the line else replace the text for the given key.
It contains special character like \ or other characters
How to delete the content if the key exists and value is an empty string
Your current approach has two problems, based on your attempt to update the properties by string manipulation via the file content as a single string:
In the ForEach-Object script block you'd need a different command to eliminate a line, because the -replace operator always returns something: if the regex pattern doesn't match the input, the input string is passed through.
You're missing an additional string-replacement step: ConvertFrom-StringData considers \ an escape character, so any pair of \\ in the input file turns into a single \ in the resulting hashtable. Therefore, you'll also have to double the \\ in $oldvalue and $value in order for the string replacement on the original file content to work.
Also, -replace, because it expects regex (regular expression) as the search operand, requires metacharachters such as \ to be escaped by \-escaping them; you could do that with [regex]::Escape($Old_file_pattern).
I suggest a different approach that avoids these problems, namely:
Directly modify the hashtable that ConvertFrom-StringData returns.
Then serialize the updated hashtable to the output file, using string formatting.
As part of the string formatting, ouble the \ in the values again by using the [string] type's .Replace() method, which operates on literal strings and is simpler (and faster) in this case; however, you could also use the somewhat counter-intuitive -replace '\\', '\\'
# Assign your real path here.
$OCUM_CONF_FILE_LOCATION = 'in.properties'
# Only for demonstration here: create a sample input file.
#'
backup.path = C:\\ProgramData\\google\\backup
backup.volume.guid = \\\\?\\Volume{49e5d325-8065-49f4-bf0d-r4be94cc1feb}\\
backup.max.count = 10
'# > $OCUM_CONF_FILE_LOCATION
# Function which either updates, adds, or removes an entry.
# NOTE:
# * This function updates input file $OCUM_CONF_FILE_LOCATION *in place*.
# To be safe, be sure to have a backup copy before you try this.
# * Set-Content's default character encoding is used to save the updated file.
# Use the -Encoding parameter as needed.
function Update-PropertiesFile ([string]$key, [string]$value) {
$ht = ConvertFrom-StringData (Get-Content -Raw $OCUM_CONF_FILE_LOCATION)
if ($ht.Contains($key)) { # update or delete existing entry
if ('' -eq $value) { $ht.Remove($key) }
else { $ht[$key] = $value }
} elseif ('' -eq $value) { # entry to remove not found
Write-Warning "No entry with key '$key' found; nothing to remove."
return
} else { # new entry
$ht[$key] = $value
}
# Serialize the updated hashtable back to the input file.
Set-Content $OCUM_CONF_FILE_LOCATION -Value $(
foreach ($key in $ht.Keys) {
'{0} = {1}' -f $key, $ht[$key].Replace('\', '\\')
}
)
}

Powershell ReadKey() case sensitive

In my script I need to get 1 character from user and process it immediately whithout waiting for an Enter.
Additionally, I want to treat the character case-sensitive.
write-host("Please press a or A:")
$choice = ($host.UI.RawUI.ReadKey(('NoEcho,IncludeKeyUp,IncludeKeyDown'))).character
if($choice -ceq "a")
{
write-host("You pressed a");
}
elseif($choice -ceq "A")
{
write-host("You pressed A");
}
else
{
Write-Host("You pressed neither a nor A")
}
Pause
The issue of this code is when I try to press "A", it shows "You pressed neither a nor A".
The reason is to type "A" I have to press Shift first, Powershell detects Shift pressed and it process immediately without waiting for an A.
Anyone has idea how to solve this?
Try the following code snippet:
if ( $Host.Name -eq 'ConsoleHost' ) {
Write-Host "Please press a or A:"
Do {
$choice = $host.UI.RawUI.ReadKey(14)
} until ( $choice.VirtualKeyCode -in #( 48..90) )
if ( $choice.Character -ceq "a") {
Write-Host "You pressed a";
}
elseif ( $choice.Character -ceq "A") {
Write-Host "You pressed A";
}
else {
Write-Host "You pressed neither a nor A ($($choice.Character))";
}
} else {
# e.g. Windows PowerShell ISE Host:
# the "ReadKey" method or operation is not implemented.
Write-Host '$Host.Name -neq ConsoleHost' -ForegroundColor Magenta
}
As currently written, $choice.VirtualKeyCode -in #( 48..90) condition allows some (limited) subset of printable characters. Adjust it with respect to Keys Enum…
Does the below make up for what is expected,
($key = $Host.UI.RawUI.ReadKey()) | % { if ($_.VirtualKeyCode -eq '16') {
$key = $Host.UI.RawUI.ReadKey()
}
$Choice = $key.Character
if ($Choice -ceq "a"){
"`rYou pressed 'a'"
}
elseif ($Choice -ceq "A"){
"`rYou pressed 'A'"
}
else {
"`rYou neither pressed 'a' nor 'A'"
}
}
The simplest solution is to only react to keypresses that result in a printable character, and then evaluate which character was pressed via a switch statement.
# Wait for a printable character to be pressed.
while (($char=($host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')).Character) -eq 0) {}
# Evaluate it.
switch -CaseSensitive ($char) {
'a' { 'You pressed a' }
'A' { 'You pressed A' }
default { 'You pressed neither a nor A' }
}
Note: While modifier keys Shift, Control, and Alt by themselves do not count as keypresses, combinations with a printable character do; therefore, for instance, Alt-a is treated the same as 'a', and Control-a as control character START OF HEADING, U+0001.
If you want to avoid that, use the following variation instead:
# Wait for a printable character to be pressed, but only if not combined
# with Ctrl or Alt.
while (($key=$host.UI.RawUI.ReadKey('NoEcho,IncludeKeyDown')).Character -eq 0 -or
$key.ControlKeyState -notin 0, 'ShiftPressed') {}
$char = $key.Character
Note: This works on Windows only - on Unix-like platforms, the .ControlKeyState property is apparently always 0.
However, if you use [Console]::ReadKey() instead, you can make it work on Unix-like platforms too - which assumes that you're willing to assume that your script always runs in a console (terminal), and not other kinds of PowerShell hosts.
# Wait for a printable character to be pressed, but only if not combined
# with Ctrl or Alt.
while (($key = [Console]::ReadKey($true)).KeyChar -eq 0 -or
$key.Modifiers -notin 0, 'Shift') {}
$char = $key.KeyChar

How can I escape a read-host by pressing escape?

just wondering if its possible to escape a read-host in a while loop by pressing escape.
I've tried doing an do-else loop but it will only recognize button presses outside of the read-host.
This is basically what I have
#Import Active Directory Module
Import-Module ActiveDirectory
#Get standard variables
$_Date=Get-Date -Format "MM/dd/yyyy"
$_Server=Read-Host "Enter the domain you want to search"
#Request credentials
$_Creds=Get-Credential
while($true){
#Requests user input username
$_Name=Read-Host "Enter account name you wish to disable"
#rest of code
}
I want to be able to escape it if I want to change the domain
Using Read-Host you cannot do this, but you might consider using a graphical input dialog instead of prompting in the console. After all, the Get-Credential cmdlet also displays a GUI.
If that is an option for you, it can be done using something like this:
function Show-InputBox {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true, Position = 0)]
[string]$Message,
[string]$Title = [System.IO.Path]::GetFileNameWithoutExtension($MyInvocation.PSCommandPath),
[string]$defaultText = ''
)
Add-Type -AssemblyName 'Microsoft.VisualBasic'
return [Microsoft.VisualBasic.Interaction]::InputBox($Message, $Title, $defaultText)
}
while($true) {
$_Name = Show-InputBox "Enter account name you wish to disable"
if ([string]::IsNullOrWhiteSpace($_Name)) {
# the box was cancelled, so exit the loop
break
}
# proceed with the rest of the code
}
If the user presses the Esc key, clicks Cancel, or leaves the input blank, you can exit the while loop, otherwise proceed with the code.
You cannot do it with Read-Host, but you can do it via the PSReadLine module (ships with Windows PowerShell version 5 or higher on Windows 10 / Windows Server 2016) and PowerShell Core) and its PSConsoleHostReadline function:
Important:
As of PSReadLine v2.0.0-beta3, the solution below is a hack, because the PSConsoleHostReadline only supports prompting for PowerShell statements, not open-ended user input.
This GitHub feature suggestion asks for the function to be optionally usable for general-purpose user input as well, which would allow for a greatly customizable end-user prompting experience. Make your voice heard there, if you'd like see this suggestion implemented.
The hack should work in your case, since the usernames you're prompting for should be syntactically valid PowerShell statements.
However, supporting arbitrary input is problematic for two reasons:
Inapplicable syntax coloring will be applied - you could, however, temporarily set all configurable colors to the same color, but that would be cumbersome.
More importantly, if an input happens to be something that amounts to a syntactically incomplete PowerShell statement, PSConsoleHostReadline won't accept the input and instead continue to prompt (on a new line); for instance, input a| would cause this problem.
Also:
Whatever input is submitted is invariably added to the command history.
While you can currently remove a temporarily installed keyboard handler on exiting a script, there is no robust way to restore a previously active one - see this GitHub issue.
# Set up a key handler that cancels the prompt on pressing ESC (as Ctrl-C would).
Set-PSReadLineKeyHandler -Key Escape -Function CancelLine
try {
# Prompt the user:
Write-Host -NoNewline 'Enter account name you wish to disable: '
$_Name = PSConsoleHostReadLine
# If ESC was pressed, $_Name will contain the empty string.
# Note that you won't be able to distinguish that from the user
# just pressing ENTER.
$canceled = $_Name -eq ''
# ... act on potential cancellation
} finally {
# Remove the custom Escape key handler.
Remove-PSReadlineKeyHandler -Key Escape
}
I wrote this function that works for me.
# Returns the string "x" when Escape key is pressed, or whatever is indicated with -CancelString
# Pass -MaxLen n to define max string length
function Read-HostPlus()
{
param
(
$CancelString = "x",
$MaxLen = 60
)
$result = ""
$cursor = New-Object System.Management.Automation.Host.Coordinates
while ($true)
{
While (!$host.UI.RawUI.KeyAvailable ){}
$key = $host.UI.RawUI.ReadKey("NoEcho, IncludeKeyDown")
switch ($key.virtualkeycode)
{
27 { While (!$host.UI.RawUI.KeyAvailable ){}; return $CancelString }
13 { While (!$host.UI.RawUI.KeyAvailable ){}; return $result }
8
{
if ($result.length -gt 0)
{
$cursor = $host.UI.RawUI.CursorPosition
$width = $host.UI.RawUI.MaxWindowSize.Width
if ( $cursor.x -gt 0) { $cursor.x-- }
else { $cursor.x = $width -1; $cursor.y-- }
$Host.UI.RawUI.CursorPosition = $cursor ; write-host " " ; $Host.UI.RawUI.CursorPosition = $cursor
$result = $result.substring(0,$result.length - 1 )
}
}
Default
{
$key_char = $key.character
if( [byte][char]$key_char -ne 0 -and [byte][char]$key_char -gt 31 -and ($result + $key_char).Length -le $MaxLen )
{
$result += $key_char
$cursor.x = $host.UI.RawUI.CursorPosition.X
Write-Host $key_char -NoNewline
if ($cursor.X -eq $host.UI.RawUI.MaxWindowSize.Width-1 ) {write-host " `b" -NoNewline }
}
}
}
}
}

how to create a display with arrays

I want to create table where it offers the user some options like change the ip to static and dynamic and i have a hash table like this. I just wanted to know the other ways to create this table without having to spam the $box variable
$Box=#("г","="," ","¬","¦","-","L","¦","¦")
Write-Host $Box[0]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[3]
Write-Host $Box[4]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Title " "$box[2] $box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[4]
Write-host $box[8]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[7]
write-host $Box[4]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[4]
Write-host $box[4]$box[2]$box[2]$box[2]$box[2]$box[2] $MenuItems[0] $box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[4]
Write-host $Box[4]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[4]
Write-host $box[4]$box[2]$box[2]$box[2]$box[2]$box[2]$MenuItems[1]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[4]
Write-host $Box[4]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$Box[2]$box[2]$box[2]$box[4]
Write-host $box[4]$box[2]$box[2]$box[2]$box[2]$box[2]$MenuItems[2]$Box[2]$box[2]" "$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[2]$box[4]
Write-host $box[6]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[1]$box[5]
$UserInput = Read-Host "Please make a selection [1-3]"
switch($UserInput)
{
1 {Set-DHCP}
2 {Set-StaticIP}
3 {exit}
default {Main}
}
You can consolidate that code a lot by using .NET string formatting. For example, the first two lines would look like the following:
$Lines = #(); # Create an empty array
$Lines += ('{0}' + '{1}'*33 + '{2}') -f $Box[0], $box[1], $box[3]; # Format and add the first line
$Lines += ('{0}' + '{1}'*9 + '{2} ' + '{1}'*10 + '{0}') -f $Box[4], $box[2], $Title; # Format and add the second line
...
...
...
Write-Host -Object ($Lines -join "`n"); # Write out all lines, joined by a line separator.

Powershell - Remove space between two variables

I have two array's which contain a selection of strings with information taken from a text file. I then use a For Loop to loop through both arrays and print out the strings together, which happen to create a folder destination and file name.
Get-Content .\PostBackupCheck-TextFile.txt | ForEach-Object { $a = $_ -split ' ' ; $locationArray += "$($a[0]):\$($a[1])\" ; $imageArray += "$($a[2])_$($a[3])_VOL_b00$($a[4])_i000.spi" }
The above takes a text file, splits it into separate parts, stores some into the locationArray and other information in the imageArray, like this:
locationArray[0] would be L:\Place\
imageArray[0] would be SERVERNAME_C_VOL_b001_i005.spi
Then I run a For Loop:
for ($i=0; $i -le $imageArray.Length - 1; $i++)
{Write-Host $locationArray[$i]$imageArray[$i]}
But it places a space between the L:\Place\ and the SERVERNAME_C_VOL_b001_i005.spi
So it becomes: L:\Place\ SERVERNAME_C_VOL_b001_i005.spi
Instead, it should be: L:\Place\SERVERNAME_C_VOL_b001_i005.spi
How can I fix it?
Option #1 - for best readability:
{Write-Host ("{0}{1}" -f $locationArray[$i], $imageArray[$i]) }
Option #2 - slightly confusing, less readable:
{Write-Host "$($locationArray[$i])$($imageArray[$i])" }
Option #3 - more readable than #2, but more lines:
{
$location = $locationArray[$i];
$image = $imageArray[$i];
Write-Host "$location$image";
}