Jump to content

How to invoke WinAPI_GetMonitorPowerStatus


Go to solution Solved by Andreik,

Recommended Posts

I'm still learning AutoIt and don't quit understand how to invoke Windows api's correctly. But I am trying to write an Autoit script that disables all network connections and logs off a user after the Windows display times out or (better yet after the screen saver  come on).  However, my script, below complains there is no defined function for " _WinAPI_GetMonitorPowerStatus" and no declaration for "$PowerDeviceD0". Is there some file missing in my AutoIt "include" directory?

#RequireAdmin
#include <WinAPI.au3>
#include <Constants.au3>

Global $hWnd = WinWait("[CLASS:Progman]", "", 10)
Global $iLastState = _WinAPI_GetMonitorPowerStatus()

While 1
    $iCurrentState = _WinAPI_GetMonitorPowerStatus()
    If $iCurrentState <> $iLastState Then
        If $iCurrentState = $PowerDeviceD0 Then
            Run("netsh interface set interface ""Local Area Connection"" admin=disable", "", @SW_HIDE)
            Run("shutdown -l -f", "", @SW_HIDE)
        EndIf
        $iLastState = $iCurrentState
    EndIf
    Sleep(1000)
WEnd

 

Link to comment
Share on other sites

Because there is no such API. Where did you find _WinAPI_GetMonitorPowerStatus() in WinAPI.au3 or in help file?

Why not something like this?

$hDummyWin = GUICreate('')
GUIRegisterMsg(0x0218, 'WM_POWERBROADCAST')
HotKeySet('{ESC}', 'Quit')

While True
    Sleep(10)
WEnd

Func WM_POWERBROADCAST($hWnd, $Msg, $wParam, $lParam)
    Switch $wParam
        Case 0x0004 ; PBT_APMSUSPEND
            ; Disable stuffs
        Case 0x0012 ; PBT_APMRESUMEAUTOMATIC
            ; Enable stuffs
    EndSwitch
EndFunc

Func Quit()
    Exit
EndFunc

 

Edited by Andreik

When the words fail... music speaks.

Link to comment
Share on other sites

Func WM_POWERBROADCAST($hWnd, $Msg, $wParam, $lParam)
    Switch $wParam
        Case 0x0004 ; PBT_APMSUSPEND
            Run("netsh interface set interface ""Local Area Connection"" admin=disabled", "", @SW_HIDE)
        Case 0x0012 ; PBT_APMRESUMEAUTOMATIC
            Run("netsh interface set interface ""Local Area Connection"" admin=enabled", "", @SW_HIDE)
    EndSwitch
EndFunc

The loop is to keep your app running.

Edited by Andreik

When the words fail... music speaks.

Link to comment
Share on other sites

I appreciate your effort to help me. But I am trying to write an Autoit script that disables all network connections and logs off a user after the Windows display times out. Executing your code, above, does not do that.

Link to comment
Share on other sites

What do you mean by Windows display times out? If you want just to run some code to disable all network connections when the screen saver is running, this is the way:

#include <WinAPISys.au3>

HotKeySet('{ESC}', 'Quit')

While True
    If IsScreenSaverRunning() Then
        ; Disable here network and logoff
    EndIf
    Sleep(10)
WEnd

Func Quit()
    Exit
EndFunc

Func IsScreenSaverRunning()
    Local $pvParam = DllStructCreate('bool')
    _WinAPI_SystemParametersInfo(0x0072, 0, DllStructGetPtr($pvParam)) ; SPI_GETSCREENSAVERRUNNING
    Return DllStructGetData($pvParam, 1)
EndFunc

If you want to run the code when the screen in turning off, you can do this:

#include <WinAPIConv.au3>

Global Const $tagGUID_CONSOLE_DISPLAY_STATE = '{6FE69556-704A-47A0-8F24-C28D936FDA47}'
Global Const $DISPLAY_OFF = 0x00
Global Const $DISPLAY_ON = 0x01
Global $dStatus = Null, $bExit = False

HotKeySet('{ESC}', 'Quit')
$hDummyWindow = GUICreate('')

$hPSN = RegisterPowerSettingNotification($hDummyWindow)
GUIRegisterMsg(0x0218, 'WM_POWERBROADCAST')

Do
    Switch $dStatus
        Case $DISPLAY_OFF
            $dStatus = Null
            ; Disable here network and logoff
        Case $DISPLAY_ON
            $dStatus = Null
            ; Enable network maybe
    EndSwitch
    Sleep(10)
Until $bExit

UnregisterPowerSettingNotification($hPSN)

Func RegisterPowerSettingNotification($hWnd)
    Local $tGUID = _WinAPI_GUIDFromString($tagGUID_CONSOLE_DISPLAY_STATE)
    Local $aCall = DllCall('user32.dll', 'handle', 'RegisterPowerSettingNotification', 'handle', $hWnd, 'ptr', DllStructGetPtr($tGUID), 'dword', 0)
    Return $aCall[0]
EndFunc

Func UnregisterPowerSettingNotification($hHandle)
    If $hHandle <> Null Then DllCall('user32.dll', 'bool', 'UnregisterPowerSettingNotification', 'handle', $hHandle)
EndFunc

Func WM_POWERBROADCAST($hWnd, $iMsg, $wParam, $lParam)
    Local $tPOWERBROADCAST_SETTING = DllStructCreate('ulong Data1;ushort Data2;ushort Data3;byte Data4[8]; dword DataLength; BYTE Data[1]', $lParam)
    Local $tGUID = _WinAPI_StringFromGUID(DllStructGetPtr($tPOWERBROADCAST_SETTING))
    If $tGUID = $tagGUID_CONSOLE_DISPLAY_STATE Then $dStatus = DllStructGetData($tPOWERBROADCAST_SETTING, 'Data')
EndFunc

Func Quit()
    $bExit = True
EndFunc

If you want to run the code when the device enters to sleep mode, use the code from my previous post. Just decide what you want to do.

Edited by Andreik

When the words fail... music speaks.

Link to comment
Share on other sites

Yes, I meant when the screen goes blank after a set period of minutes. However, I  tried your second script and nothing happens. Does this script need to be running all the time to detect the console display state? I'm beginning to think that Microsoft just doesn't want any automatic disconnection of network connections, especially on Windows 11. I created a script that initially worked when triggered by Windows Task manager. But it would not run a second time and I basically ended up having the same problem these guys had. And even manually invoking

"netsh interface set interface ""Local Area Connection"" admin=disabled"

my PC somehow turns networking back on overnight. Not sure what's going on. Definitely not a virus. This is a clean install. I'll  mark tthis issue as closed. Thanks again for trying to help me.

Edited by copyleft
update
Link to comment
Share on other sites

27 minutes ago, copyleft said:

As my knowledge of AutoIt is evidently not as extensive as yours,

...I should not butt in but, you Run() and Run(). Should RunWait() and Run(). That is a failure in logic.

9 hours ago, copyleft said:

Does this script need to be running all the time

Another logic failure.

9 hours ago, copyleft said:

my PC somehow turns networking back on overnight. Not sure what's going on.

Then that's that. But you should log every operation on your code while debugging.
If some other Program is doing something else ( like turning the network back on ), then you'll have to investigate that.

33 minutes ago, copyleft said:

I am marking this as solved, even though it isn't.

Don't quit. But from a member since 2004, your line of questions are quite unexpected. Again, don't quit. Tho, why do you wanna turn the network off ?, what do you gain from that ?

Follow the link to my code contribution ( and other things too ).
FAQ - Please Read Before Posting.
autoit_scripter_blue_userbar.png

Link to comment
Share on other sites

I will give you another example but I would kindly suggest to learn basic AutoIt.

#include <WinAPIConv.au3>

Global Const $tagGUID_CONSOLE_DISPLAY_STATE = '{6FE69556-704A-47A0-8F24-C28D936FDA47}'
Global Const $DISPLAY_OFF = 0x00
Global Const $DISPLAY_ON = 0x01
Global $dStatus = Null, $bExit = False

HotKeySet('{ESC}', 'Quit')
$hDummyWindow = GUICreate('')

$hPSN = RegisterPowerSettingNotification($hDummyWindow)

GUIRegisterMsg(0x0218, 'WM_POWERBROADCAST')

Do
    Switch $dStatus
        Case $DISPLAY_OFF
            $dStatus = Null
            ConsoleWrite('Your display just turned off. Run your damn code here' & @CRLF)
            MouseMove(Random(0, @DesktopWidth, 1), Random(0, @DesktopHeight, 1))
        Case $DISPLAY_ON
            $dStatus = Null
            ConsoleWrite('Your display just turned on.' & @CRLF)
    EndSwitch
    Sleep(10)
Until $bExit

UnregisterPowerSettingNotification($hPSN)

Func RegisterPowerSettingNotification($hWnd)
    Local $tGUID = _WinAPI_GUIDFromString($tagGUID_CONSOLE_DISPLAY_STATE)
    Local $aCall = DllCall('user32.dll', 'handle', 'RegisterPowerSettingNotification', 'handle', $hWnd, 'ptr', DllStructGetPtr($tGUID), 'dword', 0)
    Return $aCall[0]
EndFunc

Func UnregisterPowerSettingNotification($hHandle)
    If $hHandle <> Null Then DllCall('user32.dll', 'bool', 'UnregisterPowerSettingNotification', 'handle', $hHandle)
EndFunc

Func WM_POWERBROADCAST($hWnd, $iMsg, $wParam, $lParam)
    Local $tPOWERBROADCAST_SETTING = DllStructCreate('ulong Data1;ushort Data2;ushort Data3;byte Data4[8]; dword DataLength; BYTE Data[1]', $lParam)
    Local $tGUID = _WinAPI_StringFromGUID(DllStructGetPtr($tPOWERBROADCAST_SETTING))
    If $tGUID = $tagGUID_CONSOLE_DISPLAY_STATE Then $dStatus = DllStructGetData($tPOWERBROADCAST_SETTING, 'Data')
EndFunc

Func Quit()
    $bExit = True
EndFunc

Set your screen to turn off after 1 minute, wait and then enjoy the magic (I mean, watch the console). :)

When the words fail... music speaks.

Link to comment
Share on other sites

WinAPI is difficult for me and I've been here a while. I'll try explaining what Andreik wrote, but first, let's look at what's fundamentally wrong with your original code...

On 7/1/2023 at 12:22 PM, copyleft said:
Run("netsh interface set interface ""Local Area Connection"" admin=disable", "", @SW_HIDE)
Run("shutdown -l -f", "", @SW_HIDE)

These two lines of code look harmless enough, however, they won't do what you're expecting. The Run function starts a task and immediately moves on without waiting for it to finish, so netsh and shutdown happen at (basically) the same time. As argumentum mentioned, you'll want to swap Run with RunWait. RunWait, well, it waits for the command to finish before moving on, so you'll actually disable your network interface before shutting down.

With that out of the way, let's add some comments to Andreik's code to make it ... erm... readable? 😁 I've made a couple of minor changes as well

#include <WinAPIConv.au3>
#include <WindowsConstants.au3> ; To get rid of the magic number -- $WM_POWERBROADCAST

; A constant pulled from https://learn.microsoft.com/en-us/windows/win32/power/power-setting-guids
Global Const $tagGUID_CONSOLE_DISPLAY_STATE = '{6FE69556-704A-47A0-8F24-C28D936FDA47}'
; Also constants, also defined in link above
Global Const $DISPLAY_OFF = 0x00
Global Const $DISPLAY_ON = 0x01

; Global variables, available anywhere in the script. It's a good idea to limit the number of these as much as possible
; Also, when following the stricter conventions, they start with $__g_<name>... helps you realize that they're global
Global $__g_dStatus = Null, $__g_bExit = False

; If the user presses Escape at any point, run the quit function (can be on any screen)
HotKeySet('{ESC}', 'Quit')

Func Quit()
    ; Set the global variable so we can quit the script elsewhere
    $__g_bExit = True
EndFunc

; Let's add a main function... this helps to avoid "local" variables that are actually global
Main()

Func Main()
    ; Creates a GUI to use to register WinAPI events with. This is quite common with WinAPI. I'd guess it's because events in a GUI are easy to watch for?
    Local $hDummyWindow = GUICreate('')

    ; Register the PowerSettingNotification event to our GUI (this function returns a handle, we'll keep it to unregister later)
    Local $hPSN = RegisterPowerSettingNotification($hDummyWindow)

    ; Now we tell our GUI that we want to run OnPowerChange when we receive the message
    GUIRegisterMsg($WM_POWERBROADCAST, 'OnPowerChange')

    ; Now we wait for the global variable to be updated with the power info
    Do
        Switch $__g_dStatus
            Case $DISPLAY_OFF
                $__g_dStatus = Null
                ; Maybe we don't have to swear at people in the console? Just a thought
                ConsoleWrite('Your display just turned off. Run your code here' & @CRLF)
                ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
                ; Here you'd call your code to edit the network interface and shutdown
                ; ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
            Case $DISPLAY_ON
                $__g_dStatus = Null
                ConsoleWrite('Your display just turned on.' & @CRLF)
        EndSwitch
        ; We try to add at least a small sleep call to loops so we don't eat all of the CPU
        Sleep(10)
    Until $__g_bExit

    ; Here we unregister the GUI for power notifications... probably not necessary since you're shutting down, but it's good practice
    UnregisterPowerSettingNotification($hPSN)

EndFunc

Func RegisterPowerSettingNotification($hWnd)
    Local $tGUID = _WinAPI_GUIDFromString($tagGUID_CONSOLE_DISPLAY_STATE)
    Local $aCall = DllCall('user32.dll', 'handle', 'RegisterPowerSettingNotification', 'handle', $hWnd, 'ptr', DllStructGetPtr($tGUID), 'dword', 0)
    Return $aCall[0]
EndFunc

Func UnregisterPowerSettingNotification($hHandle)
    If $hHandle <> Null Then DllCall('user32.dll', 'bool', 'UnregisterPowerSettingNotification', 'handle', $hHandle)
EndFunc

; This function is called by windows internals. It sets $__g_dStatus so other functions can see it.
; !!!!!! Do NOT put your code in here. See GUIRegisterMsg in the help file for reasons. !!!!!!
Func OnPowerChange($hWnd, $iMsg, $wParam, $lParam)
    ; Create a struct from the data in $lParam. The structure is defined here: https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-powerbroadcast_setting
    ; Note: (this is the confusing part to me) a GUID is defined elsewhere and you have to replace GUID with its definition: https://learn.microsoft.com/en-us/windows/win32/api/guiddef/ns-guiddef-guid
    Local $tPOWERBROADCAST_SETTING = DllStructCreate('ulong Data1;ushort Data2;ushort Data3;byte Data4[8]; dword DataLength; BYTE Data[1]', $lParam)
    ; Get a pointer to the structure and get the string from it (!? Again, getting a bit lost here, but we're basically getting the message)
    Local $tGUID = _WinAPI_StringFromGUID(DllStructGetPtr($tPOWERBROADCAST_SETTING))
    ; If the message matches the Power Setting that we're looking for, then set the global variable to Data (note that there are unique names in the DLL struct above and data is last)
    If $tGUID = $tagGUID_CONSOLE_DISPLAY_STATE Then $__g_dStatus = DllStructGetData($tPOWERBROADCAST_SETTING, 'Data')
EndFunc

This is a more difficult topic, so don't be discouraged if you don't get it right away or struggle with writing WinAPI code at first. 

All my code provided is Public Domain... but it may not work. ;) Use it, change it, break it, whatever you want.

Spoiler

My Humble Contributions:
Personal Function Documentation - A personal HelpFile for your functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager
InputBoxes - Simple Input boxes for various variable types

Link to comment
Share on other sites

12 hours ago, Andreik said:

kindly suggest to learn basic AutoIt.

12 hours ago, Andreik said:
Run your damn code here

Yeah, real "kind". And isn't "learning basic AutoIt" what this forum is all about? 🙄

All my code provided is Public Domain... but it may not work. ;) Use it, change it, break it, whatever you want.

Spoiler

My Humble Contributions:
Personal Function Documentation - A personal HelpFile for your functions
Acro.au3 UDF - Automating Acrobat Pro
ToDo Finder - Find #ToDo: lines in your scripts
UI-SimpleWrappers UDF - Use UI Automation more Simply-er
KeePass UDF - Automate KeePass, a password manager
InputBoxes - Simple Input boxes for various variable types

Link to comment
Share on other sites

If you can't figure out to replace a comment to your desired functionality which it's pretty basic, you need to learn basics. And regarding with what he is asking, yes, you need to learn basics before to get into more complicated APIs. I gave him plenty basic pieces of code and he couldn't figure out, so your comment it's kinda useless. A forum community will assist you in learning but do your own part of the learning.

Edited by Andreik

When the words fail... music speaks.

Link to comment
Share on other sites

These negative forces solved your problem and constantly pointed you in the right direction but back in 2004 I suppose scripts had to run to actually do something, right? :whistle:

Since the OP's question has been answered I think a moderator can lock this thread. I feel it might end bad for some sensitive people around.

PS: @seadoggie01 just type damn in the search box of the forum and you'll get 139 pages of results, so don't be a drama queen. ;)

When the words fail... music speaks.

Link to comment
Share on other sites

  • Developers

@All, lets get back to solving issues and leave the emotions somewhere else... so either comment on the topic at hand or simply press the ignore button please. 

SciTE4AutoIt3 Full installer Download page   - Beta files       Read before posting     How to post scriptsource   Forum etiquette  Forum Rules 
 
Live for the present,
Dream of the future,
Learn from the past.
  :)

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
 Share

  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...