News:

Masm32 SDK description, downloads and other helpful links
Message to All Guests

Main Menu

WM_POWERBROADCAST

Started by jj2007, March 24, 2021, 10:17:00 PM

Previous topic - Next topic

jj2007

When I leave my machine alone for some time, it suspends itself; on hitting Escape it wakes up again.

My Check the forum for new posts application displayed then a "download failed" message, because the internet connection was not yet ready. So I investigated the WM_POWERBROADCAST message using the deb macro:
Event Message
  .data?
  ttt$ dd ? ; (we need a string in memory, registers are not enough for deb)
  .code
Let ttt$=fTime$(0, "HH:mm:ss.fff")  ; show exact time
inc msgCount
deb 4, ttt$, chg:msgCount ; this console deb will only be shown if chg:xxx has changed; needs opt_Susy Console
.if uMsg_==WM_POWERBROADCAST && wParam_==PBT_APMRESUMESUSPEND
invoke Sleep, 2000   ; <<<<<<<<< the solution for the "download failed" problem
.endif


Here is what happens when suspending and resuming:
11:33:01.975    354     WM_POWERBROADCAST: suspending OK, we are suspending...
11:33:23.520    355     WM_POWERBROADCAST: suspending nope, this is 20 seconds later!
11:33:24.360    356     WM_TIMECHANGE
11:33:24.380    357     WM_POWERBROADCAST: resume OK


Same but for hibernation:
11:23:48.574    234     WM_POWERBROADCAST: suspending not really... we are hibernating here
11:23:58.398    235     WM_POWERBROADCAST: suspending nope, this is 10 seconds later!
11:23:58.398    236     WM_QUERYOPEN
11:25:26.500    237     WM_GETTEXT
11:25:26.500    238     WM_WINDOWPOSCHANGING
11:25:26.500    239     WM_GETMINMAXINFO
11:25:26.500    240     WM_NCCALCSIZE
11:25:26.520    241     WM_ACTIVATEAPP
11:25:26.580    253     WM_NOTIFY: NM_KEYDOWN
11:25:26.580    254     WM_ACTIVATE
11:25:26.590    255     WM_CTLCOLORSTATIC
11:25:26.600    256     WM_NOTIFY: NM_KEYDOWN
11:25:26.600    257     WM_TIMECHANGE
11:25:26.600    258     WM_PAINT
11:25:27.126    263     WM_POWERBROADCAST: resume ; OK
11:25:27.656    264     WM_POWERBROADCAST: resume ; but why twice?
11:25:27.656    265     WM_SYNCPAINT


As you can see from my comments, it differs slightly from the documentation and/or from the expected behaviour. This is on Windows 7-64, it may look different on other OS versions. Attached the debug version of the forum watcher - exe only, the source is at Check the forum for new posts

LiaoMi

Hi jj2007,

another application example -

Video player. asm. win32 api. assembler. masm32. RadASM.
https://github.com/Maxeltr/AsmMediaPlayer
https://github.com/Maxeltr/AsmMediaPlayer/archive/refs/heads/master.zip
https://docs.microsoft.com/en-us/windows/win32/power/wm-powerbroadcast

.elseif eax==WM_POWERBROADCAST
mov eax,wParam
.if eax==PBT_APMRESUMESUSPEND

.elseif eax==PBT_APMQUERYSUSPEND
.data
szWndProc77 db "WndProc: PBT_APMQUERYSUSPEND",0
.code
invoke WriteLogFile,addr szWndProc77
.elseif eax==PBT_APMSUSPEND
.data
szWndProc777 db "WndProc: PBT_APMSUSPEND",0
.code
invoke WriteLogFile,addr szWndProc777
.endif


WndWarnTurnOffMonitor proc hWin:HWND,uMsg:UINT,wParam:WPARAM,lParam:LPARAM
LOCAL lBuffer [64]:BYTE

mov eax,uMsg

.if eax==WM_INITDIALOG
invoke GetDlgItem, hWin,IDC_STC14
mov hSTC14,eax
invoke GetDlgItem, hWin,IDC_STC15
mov hSTC15,eax
;invoke SetWindowText,hSTC14,addr szWarnTurnOffMonitor
invoke SetTimer,hWin,IDC_TIMER3,500,NULL
mov hTimer3,eax
mov CountForTurnOffMonitor,5
.elseif eax==WM_SIZE
mov eax,lParam
mov edx,eax
shr edx,16
and eax,0FFFFh
invoke MoveWindow,hSTC14,0,0,eax,edx,TRUE
.elseif eax==WM_TIMER
.if fTurnOffMonitor==1
invoke KillTimer,hWin,hTimer3
;mov hTimer3,0
invoke SendMessage,HWND_BROADCAST,WM_SYSCOMMAND, SC_MONITORPOWER,1
invoke SendMessage,hWin,WM_CLOSE,0,0
.else
mov edx,CountForTurnOffMonitor
invoke dw2a,edx,addr lBuffer
invoke SetWindowText,hSTC15,addr lBuffer
sub CountForTurnOffMonitor,1
jnz @f
mov fTurnOffMonitor,1
@@:
.endif

.elseif eax==WM_CLOSE
;mov hSTC14,0
;mov hSTC15,0
mov fTurnOffMonitor,0
mov fWndWarnTurnOffMonitorIs,0
invoke EndDialog,hWin,0
.else
mov eax,FALSE
ret
.endif
mov eax,TRUE
ret
WndWarnTurnOffMonitor endp


C++
QuoteDetecting Suspend and Resume Events
A suspend and resume cycle will destroy the enclave, and that will be detected by the next ECALL. However, we shouldn't rely on this mechanism to perform enclave recovery, because we need to act as soon as the system wakes up from the sleep state. That means we need an event listener to receive the power state change messages that are generated by Windows.

The best place to capture these is in the user interface layer. In addition to performing the enclave recovery, we must be able to lock the password vault if the system was in the sleep state longer than maximum sleep time set in the user options. When the vault is locked, the user interface also needs to be updated to reflect the new vault state.

One limitation of the Windows Presentation Foundation* is that it does not provide event hooks for power-related messages. The workaround is to hook in to the message handler for the underlying window handle. Our main application window and all of our dialog windows need a listener so that we can gracefully close each one.

The hook procedure for the main window is shown in Figure 8.

private IntPtr Main_Power_Hook(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    UInt16 pmsg;

    // C# doesn't have definitions for power messages, so we'll get them via C++/CLI. It returns a
    // simple UInt16 that defines only the things we care about.
    pmsg= PowerManagement.message(msg, wParam, lParam);

    if ( pmsg == PowerManagementMessage.Suspend )
    {
        mgr.suspend();
        handled = true;
    } else if (pmsg == PowerManagementMessage.Resume)
    {
        int vstate = mgr.resume();

        if (vstate == ResumeVaultState.Locked) lockVault();
        handled = true;
    }

    return IntPtr.Zero;
}
Figure 8. Message hook for the main window.

To get at the messages, the handler must dip down to native code. This is done using the new PowerManagement class, which defines a static function called message, shown in Figure 9. It returns one of four values:

PWR_MSG_NONE

The message was not a power event.

PWR_MSG_OTHER

The message was power-related, but not a suspend or resume message.

PWR_MSG_RESUME

The system has woken up from a low-power or sleep state.

PWR_MSG_SUSPEND

The system is suspending to a low-power state.

UINT16 PowerManagement::message(int msg, IntPtr wParam, IntPtr lParam)
{
   INT32 subcode;

   // We only care about power-related messages

   if (msg != WM_POWERBROADCAST) return PWR_MSG_NONE;

   subcode = wParam.ToInt32();

   if ( subcode == PBT_APMRESUMEAUTOMATIC ) return PWR_MSG_RESUME;
   else if (subcode == PBT_APMSUSPEND ) return PWR_MSG_SUSPEND;

   // Don't care about other power events.

   return PWR_MSG_OTHER;
}
Figure 9. The message listener.

We actually listen for both suspend and resume messages here, but the suspend handler does very little work. When a system is transitioning to a sleep state, an application has less than 2 seconds to act on the power message. All we do with the sleep message is stop the heartbeat. This isn't strictly necessary, and is just a precaution against having a heartbeat execute while the system is suspending.

The resume message is handled by calling the resume method in PasswordManagerCore. It's job is to figure out whether the vault should be locked or unlocked. It does this by checking the current system time against the saved vault state (if any). If there's no state, or if the system has slept longer than the maximum allowed, it returns ResumeVaultState.Locked.

jj2007

QuoteA suspend and resume cycle will destroy the enclave, and that will be detected by the next ECALL. However, we shouldn't rely on this mechanism to perform enclave recovery

Sounds Chinese - can you explain a bit what they mean, and what your code does?

LiaoMi

Quote from: jj2007 on March 26, 2021, 06:10:42 AM
QuoteA suspend and resume cycle will destroy the enclave, and that will be detected by the next ECALL. However, we shouldn't rely on this mechanism to perform enclave recovery

Sounds Chinese - can you explain a bit what they mean, and what your code does?

:biggrin: the example is intended to show one of the methods of use (Detecting Suspend and Resume Events), look at the code, but dont read the text  :tongue:
https://software.intel.com/content/www/us/en/develop/articles/intel-sgx-tutorial-part-9-power-events-and-data-sealing.html

jj2007

What I found out is that the WM_POWERBROADCAST message behaves erratically - see below. Equally disturbing is that it kicks in after a timer callback event. In this listing, the suspension happened at 23:47:22.282; at this point, a download attempt fails, but it does so before the WM_POWERBROADCAST: suspending message arrives :sad:
It resumes at 23:52:25.510 (cbtimer); WM_POWERBROADCAST comes ages later at 23:52:32.174

23:47:14.222    160     WM_ACTIVATE
23:47:14.222    161     WM_ACTIVATEAPP
23:47:14.232    162     WM_KILLFOCUS
23:47:14.232    163     WM_IME_SETCONTEXT
cbTimer at 23:47:16.252
Checking new posts at 23:47:16.252
Ticks   eax             5990
String OK       $Forum$:40      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML ..
23:47:19.242    168     WM_IME_NOTIFY
cbTimer at 23:47:22.262
Checking new posts at 23:47:22.262
Ticks   eax             6006
Bad string: [#D-U] at 23:47:22.282
23:47:22.282    172     WM_POWERBROADCAST: suspending
23:47:22.282    173     WM_CTLCOLORSTATIC
cbTimer at 23:52:25.510
Checking new posts at 23:52:25.520
Ticks   eax             303251
Sleep 5000
String OK       $Forum$:40      <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML ..
23:52:32.134    176     WM_NOTIFY: NM_KEYDOWN
23:52:32.144    177     WM_WINDOWPOSCHANGING
23:52:32.144    178     WM_NCCALCSIZE
23:52:32.144    179     WM_NCPAINT
23:52:32.154    180     WM_GETTEXT
23:52:32.154    181     WM_ERASEBKGND
23:52:32.154    182     WM_WINDOWPOSCHANGED
23:52:32.164    183     WM_SIZE
23:52:32.164    184     WM_SYNCPAINT
23:52:32.164    185     WM_NCPAINT
23:52:32.164    186     WM_GETTEXT
23:52:32.174    187     WM_ERASEBKGND
23:52:32.174    188     WM_POWERBROADCAST: resume
Sleep 2000 in Event Message
23:52:34.185    189     WM_POWERBROADCAST: resume

LiaoMi



if (m.Msg == WM_POWERBROADCAST)
{
if (m.WParam.ToInt32() == PBT_APMSUSPEND) https://docs.microsoft.com/en-us/windows/win32/power/pbt-apmsuspend
{
// Falling asleep code
}
else if (m.WParam.ToInt32() == PBT_APMRESUMESUSPEND) https://docs.microsoft.com/en-us/windows/win32/power/pbt-apmresumesuspend
{
// Code to execute on wakeup
}
}

jj2007

Yes, that's the theory. In practice, it's damn different, see above.

daydreamer

there is advanced energy setting to choose how long network is active for example running a webserver or like your check newpost
,maybe also some api to control it?
my none asm creations
https://masm32.com/board/index.php?topic=6937.msg74303#msg74303
I am an Invoker
"An Invoker is a mage who specializes in the manipulation of raw and elemental energies."
Like SIMD coding

LiaoMi

Quote from: jj2007 on March 26, 2021, 10:46:30 AM
Yes, that's the theory. In practice, it's damn different, see above.

Hi jj2007,

as far as I can tell, these are different synchronous processes, the network connection doesn't have to work when we wake up from sleep. If the network card is on the USB port, it will take longer, it will take additional time for initialization or antivirus. I don't see in your log - PBT_APMSUSPEND and PBT_APMRESUMESUSPEND. Therefore, it is not clear what specific time your macro outputs  :undecided:

"WM_POWERBROADCAST messages do not distinguish between different low-power states. An application can determine only that the system is entering or has resumed from a low-power state; it cannot determine the specific power state. The system records details about power state transitions in the Windows System event log."

Since this is multiple synchronization, then in these events (PBT_APMSUSPEND and PBT_APMRESUMESUSPEND) you should check for the presence of the Internet - Check for an active Internet connection https://www.codeproject.com/Tips/67577/Check-for-an-active-Internet-connection

#include <iphlpapi.h>
#pragma comment(lib, "iphlpapi")

bool IsInternetAvailable()
{
    bool bIsInternetAvailable = false;
    // Get the required buffer size
    DWORD dwBufferSize = 0;
    if (ERROR_INSUFFICIENT_BUFFER == GetIpForwardTable(NULL, &dwBufferSize, false))
    {
        BYTE *pByte = new BYTE[dwBufferSize];
        if (pByte)
        {
            PMIB_IPFORWARDTABLE pRoutingTable = reinterpret_cast<PMIB_IPFORWARDTABLE>(pByte);
            // Attempt to fill buffer with routing table information
            if (NO_ERROR == GetIpForwardTable(pRoutingTable, &dwBufferSize, false))
            {
                DWORD dwRowCount = pRoutingTable->dwNumEntries; // Get row count
                // Look for default route to gateway
                for (DWORD dwIndex = 0; dwIndex < dwRowCount; ++dwIndex)
                {
                    if (pRoutingTable->table[dwIndex].dwForwardDest == 0)
                    { // Default route designated by 0.0.0.0 in table
                        bIsInternetAvailable = true; // Found it
                        break; // Short circuit loop
                    }
                }
            }
            delete[] pByte; // Clean up. Just say "No" to memory leaks
        }
    }
    return bIsInternetAvailable;
}


and

IsNetworkAlive function (sensapi.h)
https://docs.microsoft.com/en-us/windows/win32/api/sensapi/nf-sensapi-isnetworkalive

In addition, you can use other methods of synchronization, it all depends on the idea and implementation. The handling of such events should be included in the concept.

Alternative solution - PowerRegisterSuspendResumeNotification function (powerbase.h)
https://docs.microsoft.com/en-us/windows/win32/api/powerbase/nf-powerbase-powerregistersuspendresumenotification?redirectedfrom=MSDN

Specification
https://uefi.org/specifications
ACPI Specification Version 6.4 (released January 2021)  (PDF)  (HTML) - https://uefi.org/sites/default/files/resources/ACPI_Spec_6_4_Jan22.pdf
Advanced Configuration and Power Interface - https://en.wikipedia.org/wiki/Advanced_Configuration_and_Power_Interface

jj2007

Quote from: LiaoMi on March 26, 2021, 09:50:16 PMas far as I can tell, these are different synchronous processes, the network connection doesn't have to work when we wake up from sleep.

Hi LiaoMi,

What I found has little to do with the network connection:
11:33:01.975    354     WM_POWERBROADCAST: suspending OK, we are suspending...
11:33:23.520    355     WM_POWERBROADCAST: suspending nope, this is 20 seconds later!
11:33:24.360    356     WM_TIMECHANGE
11:33:24.380    357     WM_POWERBROADCAST: resume OK


There are two WM_POWERBROADCAST: suspending messages, and the second one happens when the OS is actually waking up, as you can see from the time stamp.

Besides, in later tests I demonstrate (above) that a SetTimer callback kicks in before the OS sends WM_POWERBROADCAST: resume, which is unexpected, to say the least.

Once I sorted out these undocumented problems, I had no problem to get the download right - waiting two seconds is enough. Thanks for pointing me to IsNetworkAlive, it works like a charm, returning zero if the cable and Wifi are disconnected, and 1 if I am connected:

include \masm32\MasmBasic\MasmBasic.inc         ; download
  Init
  Dll "SensApi"
  Declare void IsNetworkAlive, 1
  .Repeat
        push eax
        IsNetworkAlive(esp)
        pop edx
        deb 4, "Test", eax, edx         ; NETWORK_ALIVE_x in edx
        invoke Sleep, 1000
  .Until KeyPressed(VK_ESCAPE)
EndOfCode


Test attached. Interesting, though, that I always get 1, i.e. LAN active. Wifi != LAN IMHO.

#define NETWORK_ALIVE_LAN   0x00000001
#define NETWORK_ALIVE_WAN   0x00000002
#define NETWORK_ALIVE_AOL   0x00000004
#define NETWORK_ALIVE_INTERNET   0x00000008


Btw M$ recommends Network List Manager ("Starting with applications designed for Windows Vista and Windows Server 2008, developers should consider using the Network List Manager instead of this function."):
QuoteDeveloper audience

Network List Manager is designed for use by COM programmers.

In short: why use a simple solution if you can have a complicated one, too :tongue: