How can I detect when a system is shutting down or logging off with AutoIt?
I need to run a function if the computer is shutdown or logged off while my script is still running.
You need to handle the Message WM_QUERYENDSESSION
Sample Code:
; Define Windows Message Codes
$WM_QUERYENDSESSION = 0x11
; Define Callback Parameter Codes
$ENDSESSION_CLOSEAPP = 0x00000001
$ENDSESSION_CRITICAL = 0x40000000
$ENDSESSION_LOGOFF = 0x80000000
; Register a Callback on the Message to the Function "onShutDown"
GUIRegisterMsg($WM_QUERYENDSESSION, "onShutDown")
; #FUNCTION# ========================================================
; Name...........: onShutDown Example Function
; Description ...: Application receives the WM_QUERYENDSESSION message
;
; Parameters ....:
; $hWndGUI - A handle to the window
; $MsgID - The WM_QUERYENDSESSION identifier
; $wParam - This parameter is reserved for future use
; $lParam - This parameter can be one or more of the following values.
; |0 - The system is shutting down or restarting
; (it is not possible to determine which event is occurring)
; |$ENDSESSION_CLOSEAPP - Application is using a ressource or file that needs to be freed
; |$ENDSESSION_CRITICAL - Application is forced to shut down
; |$ENDSESSION_LOGOFF - The user is logging off
;
; Return values .:
; |True - Allow to Shutdown
; |False - Prevent Shutdown
; Remarks .......: Applications should respect the user's intentions and return TRUE. By default any application returns **TRUE** for this message.
; If shutting down would corrupt the system or media that is being burned, the application can return **FALSE**.
; However, it is good practice to respect the user's actions.
;
Func onShutDown($hWndGUI, $MsgID, $wParam, $lParam)
Return True ; allow Shutdown ( or return "False" to prevent Shutdown )
EndFunc
Also See : List of Windows Message Codes
You can implement OnAutoItExitRegister() in such a way that you can assume the PC is shutting down.
OnAutoItExitRegister("DetectShutdown")
GUICreate("My GUI") ; will create a dialog box that when displayed is centered
GUISetState(#SW_SHOW) ; will display an empty dialog box
; Run the GUI until the dialog is closed
While True
$msg = GUIGetMsg()
If $msg = $GUI_EVENT_CLOSE Then
OnAutoItExitUnRegister("DetectShutdown")
Exit
EndIf
WEnd
Func DetectShutdown()
MsgBox(0, "", "Shutdown Detected!")
EndFunc
How can I detect when a system is shutting down or logging off with AutoIt?
As per Documentation - Function Reference - OnAutoItExitRegister() :
The mode of exit can be retrieved with #exitMethod.
Also detects user logoff and Windows shutdown.
I need to run a function if the computer is shutdown or logged off while my script is still running.
Example (determination of-, and reaction to exit methods):
#include <AutoItConstants.au3>; #exitMethod constants.
Global Enum $EXIT_OK, _
$EXIT_ONAUTOITEXITREGISTER, _
$EXIT_HOTKEYSET
Global Const $g_iDelayMain = 250
Global Const $g_iDelayExit = 1000 * 5
Global Const $g_sKeyExit = '{ESC}'
Global Const $g_sConsole = '%s-%s-%s %s:%s:%s.%s - #exitCode = %i (%s) #exitMethod = %i (%s)\n'
Global Const $g_aExit = [ _
'OK', _
'OnAutoItExitRegister() failed', _
'HotKeySet() failed' _
]
Global Const $g_aMethod = [ _
'end of script reached', _
'Exit function executed', _
'systray exit function activated', _
'Windows logoff', _
'Windows shutdown' _
]
Global $g_bStateExit = False
Main()
Func Main()
If Not OnAutoItExitRegister('_ReportExit') Then Exit $EXIT_ONAUTOITEXITREGISTER
If Not HotKeySet($g_sKeyExit, 'SetStateExit') Then Exit $EXIT_HOTKEYSET
While Not $g_bStateExit
Sleep($g_iDelayMain)
WEnd
Exit $EXIT_OK
EndFunc
Func _ReportExit()
Local Const $sLine = StringFormat($g_sConsole, _
#YEAR, #MON, #MDAY, #HOUR, #MIN, #SEC, #MSEC, _
#exitCode, $g_aExit[#exitCode], _
#exitMethod, $g_aMethod[#exitMethod] _
)
ConsoleWrite($sLine)
Sleep($g_iDelayExit)
Switch #exitMethod
; Case $EXITCLOSE_NORMAL
; Your code here.
; Case $EXITCLOSE_BYEXIT
; Your code here.
; Case $EXITCLOSE_BYCLICK
; Your code here.
Case $EXITCLOSE_BYLOGOFF
; Your code here.
Case $EXITCLOSE_BYSHUTDOWN
; Your code here.
EndSwitch
EndFunc
Func SetStateExit()
$g_bStateExit = True
EndFunc
Related
So I wrote this to simulate a program that would have a start and stop feature, and has a tab design. Right now there is a tab that has a RichEdit object intended to be a running log.
As you can see, after we "start" the program I put just some milliseconds of sleep to simulate running instructions. I created a function to check for requests that would be called on a larger scale randomly throughout code to ping the GUI per say.
#include <GUIConstantsEx.au3>
#include <GuiRichEdit.au3>
#include <WindowsConstants.au3>
Global Const $h_numOfTabs = 2
Global Enum $H_TAB_1, $H_TAB_2, $H_TAB_END
Global $hGui, $h_logRichEdit, $iMsg, $h_tabs, $h_startButton, $h_stopButton
Example()
Func Example()
$hGui = GUICreate("Example (" & StringTrimRight(#ScriptName, StringLen(".exe")) & ")", 400, 550, -1, -1)
; ADD START AND STOP BUTTONS
$h_startButton = GUICtrlCreateButton( "Start", 50, 450 )
$h_stopButton = GUICtrlCreateButton( "Stop", 150, 450 )
$h_tabs = GUICtrlCreateTab( 5, 5, 390,375 )
; LOG TAB
GUICtrlCreateTabItem( "Log" )
$h_logRichEdit = _GUICtrlRichEdit_Create ( $hGui, "", 8, 30, 384, 347, BitOR( $ES_MULTILINE, $WS_VSCROLL, $ES_AUTOVSCROLL, $ES_READONLY ) )
; STATS TAB
GUICtrlCreateTabItem( "Stats" )
; Close TABS
GUICtrlCreateTabItem( "" )
GUISetState( #SW_SHOW ) ; initialize the gui
While True
CheckRequests()
WEnd
EndFunc ;==>Example
Func Start()
while true
Sleep(100)
CheckRequests()
WEnd
EndFunc
Func Stop()
EndFunc
Func CheckRequests()
$iMsg = GUIGetMsg()
while $iMsg <> 0
Select
Case $iMsg = $GUI_EVENT_CLOSE
_GUICtrlRichEdit_Destroy($h_logRichEdit) ; needed unless script crashes
; GUIDelete() ; is OK too
Exit
Case $iMsg = $h_tabs
Switch GUICtrlRead( $h_tabs )
Case $H_TAB_1
ControlShow( $hGui, "", $h_logRichEdit )
Case Else
ControlHide( $hGui, "", $h_logRichEdit )
EndSwitch
Case $iMsg = $h_startButton
Start()
Case $iMsg = $h_stopButton
Stop()
EndSelect
$iMsg = GUIGetMsg()
WEnd
EndFunc
At about 500ms sleep, the lag when switching tabs is visible.
My question: On a larger scale, is this how we would handle/update things that are specific to a tab while running a larger program? If not, what would be a more efficient way of updating tab specific properties while running a larger overall program.
I have also seen a design recently where all the tabs and related components were their own GUI's but I am not sure the relevance of everything being its own GUI and if it pertains to this question.
Any help or clarification is greatly appreciated, I am new to AutoIT and trying to figure out some do's and dont's as well as efficiency.
#include <GUIConstantsEx.au3>
#include <GuiRichEdit.au3>
#include <WindowsConstants.au3>
Global Const $h_numOfTabs = 2
Global Enum $H_TAB_1, $H_TAB_2, $H_TAB_END
Global $hGui, $h_logRichEdit, $iMsg, $h_tabs, $h_startButton, $h_stopButton
Example()
Func Example()
$hGui = GUICreate("Example (" & StringTrimRight(#ScriptName, StringLen(".exe")) & ")", 400, 550, -1, -1)
; ADD START AND STOP BUTTONS
$h_startButton = GUICtrlCreateButton( "Start", 50, 450 )
$h_stopButton = GUICtrlCreateButton( "Stop", 150, 450 )
$h_tabs = GUICtrlCreateTab( 5, 5, 390,375 )
; LOG TAB
GUICtrlCreateTabItem( "Log" )
$h_logRichEdit = _GUICtrlRichEdit_Create ( $hGui, "", 8, 30, 384, 347, BitOR( $ES_MULTILINE, $WS_VSCROLL, $ES_AUTOVSCROLL, $ES_READONLY ) )
_GUICtrlRichEdit_AppendText($h_logRichEdit, "{\rtf {Showing \b1 Rich Text \b0}")
; STATS TAB
GUICtrlCreateTabItem( "Stats" )
; Close TABS
GUICtrlCreateTabItem( "" )
; Register Windows message.
GUIRegisterMsg($WM_NOTIFY, 'WM_NOTIFY')
GUISetState( #SW_SHOW ) ; initialize the gui
GuiMessageLoop()
EndFunc
Func GuiMessageLoop()
While 1
$iMsg = GUIGetMsg()
Switch $iMsg
Case $GUI_EVENT_CLOSE
_GUICtrlRichEdit_Destroy($h_logRichEdit) ; needed unless script crashes
; GUIDelete() ; is OK too
Exit
Case $h_startButton
Start()
Case $h_stopButton
Stop()
EndSwitch
WEnd
EndFunc
Func Start()
ConsoleWrite('# Sleep' & #CRLF)
Sleep(5000)
ConsoleWrite('# Awake' & #CRLF)
EndFunc
Func Stop()
EndFunc
Func WM_NOTIFY($hWnd, $iMsg, $wParam, $lParam)
Local $iLoWord = _WinAPI_LoWord($lParam)
Local $iHiWord = _WinAPI_HiWord($lParam)
ConsoleWrite('- WM_NOTIFY' & ' ' & $iLoWord & ' ' & $iHiWord & #CRLF)
Switch GUICtrlRead( $h_tabs )
Case $H_TAB_1
ControlShow( $hGui, "", $h_logRichEdit )
Case Else
ControlHide( $hGui, "", $h_logRichEdit )
EndSwitch
Return $GUI_RUNDEFMSG
EndFunc
Try this example. It uses GuiRegisterMessage to capture
WM_NOTIFY Windows messages. WM_NOTIFY is not an ideal
message code for this, though have not found something
more suitable for only changing Tabs.
The code to hide and show the Rich Edit control has been
moved into the WM_NOTIFY function so that each time a
message is received, the Tab hide and show code executes.
ConsoleWrite is used to show what the events of interest
are doing during testing. If your editor has a output
console pane, then you may be able to view the writes to
the console pane.
If an event such as function Start takes a long time,
then multi-processing may help to allow the Gui to
remain responsive with the message loop.
Want a do don't, avoid (cyclic) recursive function
calls and avoid getting trapped in loops.
Additional References:
Windows Message Codes
AutoIt Wiki about Tabs
I am automating a software installation in Windows7 using AutoIt.
During the installation, in between if a error window appears. I want to click ENTER.
If the error window not appears then I should NOT do anything. Simply its should go to the next section.
I have tried "WinActive and WinWaitActive" But its waiting for the window to appear. If window not appears its not going to the next screen.
Any idea how to handle this situation?
Do a while loop:
$w = 0
While($w = 0)
If(WinActive("ERROR WINDOW"))Then
Send("{ENTER}")
$w = 1
ElseIf(ControlGetText("YOUR WINDOW", "", "[CLASS:Static; INSTANCE:2]") <> "SOME TEXT") Then
$w = 1
;and something else
EndIf
Sleep(1000)
WEnd
AdlibRegister() is the right choice. From the help file:
"... typically to check for unforeseen errors. For example, you could use adlib in a script which causes an error window to pop up unpredictably."
Each 100 ms (may be adjusted) the function is called to check the appearing of your error dialog:
Global $sErrorWindow = 'ErrorDialogName'
Global $iDelayHowOftenDoTheFunctionCall = 100
AdlibRegister('_isErrorWindowDisplayed', $iDelayHowOftenDoTheFunctionCall)
Func _isErrorWindowDisplayed()
If WinActive($sErrorWindow) <> 0 Then
WinActivate($sErrorWindow) ; just to be sure that the ENTER command is on the correct window/dialog
; either do
Send('{ENTER}')
; or
ControlClick('title', 'text', 'controlID')
EndIf
EndFunc
; do your software installation processing here
; ...
; ...
; don't forget to unregister the function at the end
AdlibUnRegister('_isErrorWindowDisplayed')
I want to be able to prevent regular shutdown on windows 7. I found this AutoIt script but its not clicking the CANCEL button on force shutdown dialog really.
Someone can test it and tell me whats wrong exactly?
$WM_QUERYENDSESSION = 0x11
GUIRegisterMsg($WM_QUERYENDSESSION, "Cancel_Shutdown")
$Hwnd = GUICreate("PreventShutdownGUI")
;~ GUISetSTate(#SW_SHOW)
_ShutdownBlockReasonCreate($hwnd, "Sorry, AutoIt > Windows")
TrayTip("Shutdown Blocker - Started", "Denies shutdowns from now on.", 20)
While Sleep(100)
WEnd
_ShutdownBlockReasonDestroy($hwnd)
Func _ShutdownBlockReasonCreate($Hwnd, $wStr)
; http://msdn.microsoft.com/en-us/library/ms...28VS.85%29.aspx
; Prog#ndy
Local $aResult = DllCall("User32.dll", "int", "ShutdownBlockReasonCreate", "hwnd", $Hwnd, "wstr", $wStr)
If #error Then Return SetError(1,0,0)
Return $aResult[0]
EndFunc
Func _ShutdownBlockReasonDestroy($Hwnd)
Local $aResult = DllCall("User32.dll", "int", "ShutdownBlockReasonDestroy", "hwnd", $Hwnd)
If #error Then Return SetError(1,0,0)
Return $aResult[0]
EndFunc
Func Cancel_Shutdown($hWndGUI, $MsgID, $WParam, $LParam)
; This HAS to be here to capture the endsession...
AdlibRegister("Cancel_ShutdownResolver",50)
Return False
EndFunc
Func Cancel_ShutdownResolver()
Local $iOpt = Opt("WinTitleMatchMode",4),$hwnd = WinGetHandle("[CLASS:BlockedShutdownResolver]")
Opt("WinTitleMatchMode",$iOpt)
If $hwnd Then
If #OSVersion = "WIN_7" Then ControlClick($hwnd,"","[CLASS:Button; INSTANCE:1]")
If #OSVersion <> "WIN_7" Then ControlClick($hwnd,"","[CLASS:Button; INSTANCE:2]")
AdlibUnRegister("Cancel_ShutdownResolver")
TrayTip("Shutdown Blocker - Shutdown denied", "Denied shutdown successfull!", 5)
_DeineFunktion() ; <================ Hier musst du deine Funktion einfügen
EndIf
EndFunc
Func _DeineFunktion()
MsgBox(0,"","Das hier ist deine Funktion ;) - Danach musst du erneut _ShutdownBlockReasonDestroy($hwnd) aufrufen, und einen neuen Shutdown machen.")
EndFunc
Looks to me like this is a way of preventing an autoit function getting killed..
_deineFunction() (German for your function) is the function you don't want killed.
My clipboard controller could have several items copied to the clipboard when using a hotkey (CTRL + SHIFT + Q), instead of only one item, and pastes all at once (CTRL + SHIFT + W), or paste any of the first 10 items directly (CTRL + SHIFT + 1 … 9). Another option is to clear the clipboard (CTRL + SHIFT + -).
It works just for several copy and pastes, but then trying to make a copy operation nothing is added to the buffer. I couldn't find a reason for this.
Code (problem should be in the addToClipboard() or getAll()) :
#include <GUIConstantsEx.au3>
#include <WindowsConstants.au3>
#include <array.au3>
Global $clipBoard[50]=[""]
Global $counter = 0
HotKeySet("^+q","addToClipboard")
HotKeySet("^+-","emptyAll")
HotKeySet("^+w","getAll")
HotKeySet("^+1","get1")
HotKeySet("^+2","get2")
HotKeySet("^+3","get3")
HotKeySet("^+4","get4")
HotKeySet("^+5","get5")
HotKeySet("^+6","get6")
HotKeySet("^+7","get7")
HotKeySet("^+8","get8")
HotKeySet("^+9","get9")
$hGUI = GuiCreate("Clipboard Controller", 100, 100,Default,Default,$WS_SIZEBOX)
GUISetState()
Func addToClipboard()
Send ("^c")
$copied = ClipGet()
$clipBoard[Mod($counter,50)] = $copied
$counter +=1
EndFunc
Func getByIndex($i)
$statement = $clipBoard[$i]
ClipPut($statement)
Send("^v")
EndFunc
Func getAll()
$statement =""
For $i In $clipBoard
If $i <> "" Then
$statement &= $i & #CRLF
EndIf
Next
ClipPut($statement)
Send("^v")
EndFunc
Func emptyAll()
For $i=0 To 49
$clipBoard[$i]=""
Next
ClipPut("")
EndFunc
Func get1()
getByIndex(0)
EndFunc
Func get2()
getByIndex(1)
EndFunc
Func get3()
getByIndex(2)
EndFunc
Func get4()
getByIndex(3)
EndFunc
Func get5()
getByIndex(4)
EndFunc
Func get6()
getByIndex(5)
EndFunc
Func get7()
getByIndex(6)
EndFunc
Func get8()
getByIndex(7)
EndFunc
Func get9()
getByIndex(8)
EndFunc
While 1
Switch GUIGetMsg()
Case $GUI_EVENT_CLOSE
Exit
EndSwitch
WEnd
Problem is an old trap...
It takes a small amount of time to copy to the clip board
especially large items..try a sleep after the Send
Func addToClipboard()
Send ("^c")
sleep(1000) ; try different values
$copied = ClipGet()
$clipBoard[Mod($counter,50)] = $copied
$counter +=1
EndFunc
anyway like your script..idea
The problem is the code for addToClipboard is running while the user still has the keys pressed down. As a result, the Send command designed to send just Ctrl+C is in effect sending Ctrl+Shift+C, so the text is never copied.
The solution is to wait for the user to raise those keys, using the _IsPressed function, then once all keys are released, execute the code. It may also be wise to disable the hotkey when you enter the function (and re-enable when you leave) so that holding the hotkey down for a long time doesn't keep triggering the function.
An alternative would be to send the WM_COPY message directly to the control with focus. This is not guaranteed to work for every control (though I'd be very surprised if it didn't). This would be a far more reliable method.
hope this is the end of the problem , I found another way to set/get data from clipboard , functions : _ClipBoard_SetData () & _ClipBoard_GetData() from library <Clipboard.au3> , after trying them it worked well , after all it seems like the problem was in setting and getting data from the clipboard..
will come later isA to assure whether its finally correct or not
I would like to know if its possible to WinWaitActive for WindowWithThisTitle and WindowWithThatTitle at the same time. I'm executing a command and there could be a window telling me that the connection failed or a user/pass dialog coming up.
Is there another way doing it as this?
WinWaitActive("Title1", "", 5)
If(WinExists("Title1")) Then
MsgBox(0, "", "Do something")
Else
If(WinExists("Title2")) Then
MsgBox(0, "", "Do something else")
EndIf
EndIf
Because I don't want to have the timeout which could be more than 15 seconds.
A simpler solution might be to use a REGEX title in your WinWaitActive as defined here
You would then have something like this:
$hWnd = WinWaitActive("[REGEXPTITLE:(WindowWithThisTitle|WindowWithThatTitle)]")
If WinGetTitle($hWnd) = "WindowWithThisTitle" then
DoSomething()
Else
DoSomethingElse()
EndIf
How about something like this.
$stillLooking = True
While $stillLooking
$activeWindowTitle = WinGetTitle(WinActive(""))
If $activeWindowTitle == "Title1" Then
MsgBox(0, "", "Do something")
$stillLooking = False
ElseIf $activeWindowTitle == "Title2" Then
MsgBox(0, "", "Do something else")
$stillLooking = False
EndIf
sleep(5)
WEnd
Because I don't want to have the
timeout which could be more than 15
seconds.
WinWaitActive() doesn't have a timeout unless you specify one. You gave it a five second timeout but you could leave that off and it would wait forever.
You can use this Functions for two windows ..
; #FUNCTION# ====================================================================================================================
; Name...........: _2WinWait
; Description ...: Wait For Tow Windows .
; Syntax.........: _2WinWait ($FirstTitle,$SecondTitle,[$FirstText = "" ,[$SecondText = ""]] )
; Parameters ....: $FirstTitle - Title Of First Wondow
; $SecondTitle - Title Of Second Wondow
; $FirstText - Text Of First Wondow
; $SecondText - Text Of Second Wondow
; Return values .: Success - None
; Failure - Returns a 0 => If Your Titles Is Wrong
; Author ........: Ashalshaikh : Ahmad Alshaikh
; Remarks .......:
; Related .......:
; Link ..........;
; Example .......; No
; ===============================================================================================================================
Func _2WinWait ($FirstTitle,$SecondTitle,$FirstText = "" ,$SecondText = "" )
If $FirstTitle = "" Or $SecondTitle = "" Then
Return 0
Else
Do
Until WinExists ($FirstTitle,$FirstText) Or WinExists ($SecondTitle,$SecondText)
EndIf
EndFunc
; #FUNCTION# ====================================================================================================================
; Name...........: _2WinWait_Any
; Description ...: Wait For Tow Windows And Return Any Window Id Exists .
; Syntax.........: _2WinWait_Any ($FirstTitle,$SecondTitle,[$FirstText = "" ,[$SecondText = ""]] )
; Parameters ....: $FirstTitle - Title Of First Wondow
; $SecondTitle - Title Of Second Wondow
; $FirstText - Text Of First Wondow
; $SecondText - Text Of Second Wondow
; Return values .: Success - Number Of Window ==> 1= First Window , 2= Second Window
; Failure - Returns a 0 => If Your Titles Is Wrong
; Author ........: Ashalshaikh : Ahmad Alshaikh
; Remarks .......:
; Related .......:
; Link ..........;
; Example .......; No
; ===============================================================================================================================
Func _2WinWait_Any ($FirstTitle,$SecondTitle,$FirstText = "" ,$SecondText = "" )
If $FirstTitle = "" Or $SecondTitle = "" Then
Return 0
Else
Do
Until WinExists ($FirstTitle,$FirstText) Or WinExists ($SecondTitle,$SecondText)
If WinExists ($FirstTitle,$FirstTexit) Then
Return 1
Else
Return 2
EndIf
EndIf
EndFunc
for more with examples
I'm fairly new to autoit and the programming world in general and I had this same dilemma. Luckily I figured out a straight fwd way to do it:
Do
$var1 = 0
If WinGetState("Document Reference","") Then
$var1 = 1
ElseIf WinGetState("Customer Search","") Then
$var1 = 1
EndIf
Until $var1 = 1
So it'll stay in the loop until it finds the window and sets $var1 to 1. There's probably easier ways (I'm sure developers are gasping at this) but this is straight fwd enough for me.
You can create an infinite while loop with if statements in there:
#include <MsgBoxConstants.au3>
Example()
Func Example()
While 1
; Test if the window exists and display the results.
If WinExists("Windows Security") Then
Local $hWnd = WinWaitActive("Windows Security", "", 2000)
ControlSetText($hWnd, "", "[CLASS:Edit; INSTANCE:1]", "hel233")
ControlClick("Windows Security","","[CLASS:Button; INSTANCE:2]")
Sleep(5000)
EndIf
; Test if the window exists and display the results.
If WinExists("Spread the Word") Then
'The line below will wait until the window is active, but we don't need that
'Local $hWnd = WinWaitActive("Spread the Word", "", 2000)
WinClose("Spread the Word")
Sleep(5000)
EndIf
wend
EndFunc