I want to make macros using function keys to improve my workflow but the code below doesnt work, i think its quite self explanatory, while the code is running if i press X key, a different text is sent.
$wshell = New-Object -ComObject wscript.shell;
# choose the key you are after
$key = [System.Windows.Input.Key]::LeftCtrl
$isCtrl = [System.Windows.Input.Keyboard]::IsKeyDown($key)
while ($true)
{
if ($isCtrl)
{
$wshell.SendKeys('Thank you for using the service.')
}
}
The code above doesnt work. But if i only use the code below it does send the string as expected.
$wshell = New-Object -ComObject wscript.shell;
sleep 1
$wshell.SendKeys('Digital service desk')
You keep checking the same value inside the loop since $isCtrl is never assigned to after entering the loop.
Change to:
while ($true)
{
$isCtrl = [System.Windows.Input.Keyboard]::IsKeyDown($key)
if ($isCtrl)
{
$wshell.SendKeys('Thank you for using the service.')
}
}
So that you re-check whether control is pushed down every time.
As stated by Mathias R. Jessen i was not updating the value of $isCtrl inside the loop. I'll now post the code if anyone wants to use it.
$wshell = New-Object -ComObject wscript.shell;
Add-Type -AssemblyName WindowsBase
Add-Type -AssemblyName PresentationCore
while ($true)
{
if ([System.Windows.Input.Keyboard]::IsKeyDown([System.Windows.Input.Key]::F2))
{
sleep 1
$wshell.SendKeys('Message after pressing F2')
} elseif ([System.Windows.Input.Keyboard]::IsKeyDown([System.Windows.Input.Key]::F4)){
sleep 1
$wshell.SendKeys('Message after pressing F4')
}
}
Related
I'm certain I'm missing something stupid, but I can't get this figured. I have a script that creates a "Please Wait" popup while the code is running. The issue is that I can't get the popup to close after the script is done.
Here is how the popup gets called:
Function CheckShuffle()
{
#Check Selection
If ($Global:x -eq "" -or $Global:x -eq '0')
{
if ( $null -eq ('System.Windows.MessageBox' -as [type]) )
{
Add-Type -AssemblyName PresentationFramework
}
[System.Windows.MessageBox]::Show('You must select your part first', 'Warning', 'OK')
}
Else
{
OpenSplash
#Shuffle
CloseSplash
}
}
Here is the code for the popup:
Function OpenSplash()
{
$Splash = New-Object system.Windows.Forms.Form
$Splash.StartPosition = 'CenterScreen'
$Splash.Size = New-Object System.Drawing.Size(350,200)
$SplashLabel = New-Object System.Windows.Forms.Label
$Splash.Controls.Add($SplashLabel)
$SplashLabel.Location = New-Object System.Drawing.Point(100,60)
$SplashLabel.Font = [System.Drawing.Font]::new("Microsoft Sans Serif", 16, [System.Drawing.FontStyle]::Bold)
$SplashLabel.Text = "Please Wait"
$SplashLabel.AutoSize = $True
$Splash.Visible = $True
$Splash.Update()
}
I've tried a few ways to close it:
Function CloseSplash()
{
$This.Parent.Close()
}
This one one closes the primary form, not the splash screen.
Function CloseSplash()
{
$Splash.Close()
}
That one throws an error: "You cannot call a method on a null valued expression"
It looks like Powershell is forgetting what $Splash is, but I cannot figure out how to tell it.
Thank you all!
Edit: Worth noting, if I put the code all together instead of calling it, the popup closes as intended.
Jeroen answered the question in a comment, and I don't see how to mark that as the answer, so I'm reposting here!
The solution:
I needed to make $splash global. This was super easy, I just had to make it say:
$Global:Splash
Thanks again Jeroen!
I have cmd script. I want to use timer for 10 second to give a decision to continue cmd script process or to pause it.
I want to put this script at the first line of my cmd script
powershell.exe -ExecutionPolicy Bypass -File %~dp0\Pause_GUI.ps1
It will pop up 10s countdown, after 10s, it will continue the cmd script process by return errorlevel, but if we click button pause, the cmd script will pause, also by return errorlevel.
Anyone can help please
UPDATED
#------------------------------------------- Add in Forms Controls -------------------------------------------#
Add-Type -AssemblyName System.Windows.Forms
#-------------------------------------------------------------------------------------------------------------#
#---------------------------------------- Begins creation of the form ----------------------------------------#
$MainForm = New-Object System.Windows.Forms.Form
$MainForm.Text = "Message"
$MainForm.Width = 500
$MainForm.Height = 200
$MainForm.StartPosition = "CenterScreen"
$MainForm.BackColor = "#e2e2e2"
#-------------------------------------------------------------------------------------------------------------#
#----------------------------------------------- Button Clicks -----------------------------------------------#
$Auto_Button = ({ $global:result=1
$MainForm.Close() })
$Manual_Button = ({ $global:result=0
$MainForm.Close() })
#-------------------------------------------------------------------------------------------------------------#
#-------------------------------------------------- Buttons --------------------------------------------------#
$Automatic = New-Object System.Windows.Forms.Button
$Automatic.Location = New-Object System.Drawing.Size(110,80)
$Automatic.Size = New-Object System.Drawing.Size(120,30)
$Automatic.Text = "Continue After 10s"
$Automatic.BackColor = "#e47104"
$Automatic.Add_Click($Auto_Button)
$MainForm.Controls.Add($Automatic)
$Manual = New-Object System.Windows.Forms.Button
$Manual.Location = New-Object System.Drawing.Size(270,80)
$Manual.Size = New-Object System.Drawing.Size(100,30)
$Manual.Text = "Pause"
$Manual.BackColor = "#e47104"
$Manual.Add_Click($Manual_Button)
$MainForm.Controls.Add($Manual)
#-------------------------------------------------------------------------------------------------------------#
#--------------------------------------------- Displays the Form ---------------------------------------------#
$result=0
$MainForm.ShowDialog()
exit $result
#-------------------------------------------------------------------------------------------------------------#
How to handle the button "Continue after 10s" as a timer? And the GUI will close automatically after 10s
You need a System.Windows.Forms.Timer-object that counts your time and a .tick-event that will trigger when the time has come. However you need to stop (and dispose) the timer or it will keep on triggering the event even when the window is closed. (In Powershell ISE that could cause windows to close as soon as you load them). To grab the timer from within it's own event you need to adress it in the right scope. I used the global-scope for that.
$Auto_Button = ({
$global:Counter = 0
$global:timer = New-Object -type System.Windows.Forms.Timer
$global:timer.Interval = 1000
$global:timer.add_Tick({
if ($Counter -eq 10){
write-host $global:counter
$global:timer.Stop()
$global:timer.Dispose()
$result=1
$MainForm.Close()
$global:Counter++
}else{
write-host $global:counter
$global:Counter++
}
})
$global:timer.Start()
})
Trying to get powershell to start different websites at some time intervals.
Here is a script that works:
function IEWeb {
$ie = New-Object -Comobject 'InternetExplorer.Application'
$ie.visible=$true
Do
{
$ie.navigate('http://p-captas02.int.addom.dk/cap-tas-views/Queue.aspx')
start-sleep 15
$ie.navigate('https://oneview.int.addom.dk/dashboard?dashboard_id=1')
start-sleep 15
$ie.navigate('https://oneview.int.addom.dk/dashboard?time=0&scroll_value=15&dashboard_id=10')
start-sleep 15
}
While ($ie.name -contains 'Internet Explorer')
}#Function
The problem is that it does not work every time
Is there anyone who knows another way of doing it?
It is important that the websites are started in the same tab
I think it would be better to check if IE has not been closed by the user at some point before trying to navigate to the next url. Also, $ie.name is a String, so $ie.name -contains 'Internet Explorer' would be wrong.
Maybe this works better for you.
function IEWeb {
# create an array with the urls you want to revolve
$urls = 'http://p-captas02.int.addom.dk/cap-tas-views/Queue.aspx',
'https://oneview.int.addom.dk/dashboard?dashboard_id=1',
'https://oneview.int.addom.dk/dashboard?time=0&scroll_value=15&dashboard_id=10'
$ie = New-Object -Comobject 'InternetExplorer.Application'
$ie.visible=$true
$index = 0
while ($ie.HWND) { # for as long as the user does not close IE
$ie.navigate($urls[$index])
Start-Sleep 15
# increment the array counter, and revert to index 0 if $urls length is reached
$index = ($index + 1) % $urls.Count
}
try {
# close and release the Com object from memory
$ie.Quit()
[System.Runtime.Interopservices.Marshal]::ReleaseComObject($ie) | Out-Null
[System.GC]::Collect()
[System.GC]::WaitForPendingFinalizers()
}
catch {}
}
IEWeb
I would like to create a GUI that shows a loading bar while a job is running in the background. For simplicity, I've made the job an infinite loop so it should always be running. I've only included necessary parts of the code:
$Label = new-object system.windows.forms.Label
$Label.Font = 'Ariel,12pt'
$Label.Text = ""
$Label.AutoSize = $True
$Label.Location = new-object system.drawing.size(50,10)
$Form.Controls.Add($Label)
$LoadingAnimation = #(".....","0....",".0...","..0..","...0.","....0",".....")
$AnimationCount = 0
$test = start-job -Name Job -ScriptBlock { for($t=1;$t -gt 0; $t++){} }
while ($test.JobStateInfo.State -eq "Running")
{
$Label.Text = $LoadingAnimation[($AnimationCount)]
$AnimationCount++
if ($AnimationCount -eq $LoadingAnimation.Count){$AnimationCount = 0}
Start-Sleep -Milliseconds 200
}
Upon testing this code in the console, just using Write-Host instead of $Label.Text, it works just fine. What needs to be done differently to get this to work in a windows form created by PowerShell?
In PowerShell, you can create all sorts of status, including multi-level, using Write-Progress. Don't forget to call with -Completed when done (a common mistake I see).
Get-Help Write-Progress
After going through the little details of the script, I found the problem. This is how I activated my form:
$Form.Add_Shown({$Form.Activate()})
[void] $Form.ShowDialog()
This caused the script to stop when the form was launched, ShowDialog stops the script to allow interaction. The fix was:
$Form.Add_Shown({$Form.Activate()})
[void] $Form.Show()
Using Form.Show lets the script to continue to run because it doesn't require interaction.
OK so I am newer to using windows forms inside Powershell, and I am having a bit of trouble with the form being all laggy since there is a continuous ping running. Fundamentally all I need to do is display the IP address each time it runs a ping. I tried to search around and it seems like the proper way to do this is with a running job in the background?
I think the way this is currently written that the ping happens in the background but I'm not sure how to update the form (or if its even possible to make it visible to it?)
Here is a sample, any guidance with this would be greatly appreciated.
function global:ContinuousPing{
$global:job = start-job {
while($true){
$pingStatus = Test-Connection google.com -Count 1
$label.text = $pingStatus.IPV4Address.IPAddressToString
#[System.Windows.Forms.Application]::DoEvents()
start-sleep 1
}
}
}
Add-Type -AssemblyName System.Windows.Forms
$pingForm = New-Object System.Windows.Forms.Form
$label = New-Object System.Windows.Forms.Label
$button1 = New-Object System.Windows.Forms.Button
$ping = {
ContinuousPing
while($true){
Receive-Job $job
}
}
$label.Text = "Ping Status"
$button1.Location = New-Object System.Drawing.Point(100,10)
$button1.add_Click($ping)
$pingForm.Controls.Add($label)
$pingForm.Controls.Add($button1)
$pingForm.ShowDialog() | Out-Null
remove-job $job
You need to separate the GUI updating and the ping task. This is easy to in languages that are designed to have GUIs like C# winforms, If you tried it you would be surprised how much easier it is than trying to bend a GUI over PowerShell.
You can try the link Mathias posted, It will do what you require.