Botão de energia do sistema de gancho no Windows

2

Eu tenho um computador sem cabeçalho executando um serviço personalizado que desejo ativar / desativar usando o botão liga / desliga, em vez de ter que me conectar remotamente a cada vez. O computador também faz outras coisas, então desativá-lo não é uma opção.

É possível ligar o botão de energia do sistema no Windows XP & up, de tal forma que meu programa receberia o evento antes que o Windows iniciasse um evento powerdown / sleep (antes que PBT_APMQUERYSUSPEND fosse enviado)?

Eu descobri isso, veja minha própria resposta abaixo!

    
por Duke Nukem 23.08.2013 / 02:07

2 respostas

1

Isso é realmente possível, mas é um pouco trabalhoso e requer duas implementações completamente diferentes, dependendo da versão do Windows. Para ambos os métodos, você precisa definir o seu botão de energia para colocar o computador para dormir em suas opções de energia.

Windows XP e abaixo:

Você precisa sobrescrever a função WndProc da janela principal do seu programa. Em IDEs que não suportam isso nativamente, isso pode ser feito usando SetWindowLong na API user32. Na sua função WndProc personalizada, ouça a mensagem WM_POWERBROADCAST (0x218) . Se você receber uma mensagem com wParam de PBT_APMQUERYSUSPEND (0x0) , chame sua função desejada e retorne BROADCAST_QUERY_DENY (0x424D5144) em vez de chamar a função WndProc de base. Exemplo de código:

//At program start
//GWL_WNDPROC = -4
oldWndProc = SetWindowLong(this.hWnd, GWL_WNDPROC, &MyWndProc)

//In MyWndProc(hWnd, wMsg, wParam, lParam)
//WM_POWERBROADCAST = 0x218
//PBT_APMQUERYSUSPEND = 0x0
//BROADCAST_QUERY_DENY = 0x424D5144
if wMsg = WM_POWERBROADCAST && wParam = PBT_APMQUERYSUSPEND (
    //CALL YOUR FUNCTION HERE!
    return BROADCAST_QUERY_DENY
)
return CallWindowProc(oldWndProc, hWnd, wMsg, wParam, lParam)

//Before exiting
SetWindowLong(Me.hWnd, GWL_WNDPROC, oldWndProc)

Windows Vista e amp; up: (obrigado a Remy Lebeau por me colocar no caminho certo)

Você precisa substituir WndProc , como no XP, mas também chamar SetThreadExecutionState na API do kernel32 para desabilitar o modo de suspensão e RegisterPowerSettingNotification na API user32 para ouvir notificações avançadas de energia. Você estará ouvindo, em particular, a notificação GUID_SYSTEM_AWAYMODE , que é enviada quando o sistema foi solicitado a adormecer, mas não consegue fazê-lo. Para converter facilmente uma string em LPCGUID formada corretamente, você pode usar UuidFromStringA na API rpcrt4.dll. Exemplo de código:

typedef struct UUID{
    int d1, d2, d3, d4
} LPCGUID;

//At program start
//GWL_WNDPROC = -4
//ES_CONTINUOUS = 0x80000000
//ES_SYSTEM_REQUIRED = 0x1
//ES_AWAYMODE_REQUIRED = 0x40
//GUID_SYSTEM_AWAYMODE = "98a7f580-01f7-48aa-9c0f-44352c29e5C0"
LPCGUID uid;
oldWndProc = SetWindowLong(this.hWnd, GWL_WNDPROC, &MyWndProc)
SetThreadExecutionState(ES_CONTINUOUS | ES_SYSTEM_REQUIRED | ES_AWAYMODE_REQUIRED)
UuidFromStringA(*(GUID_SYSTEM_AWAYMODE), uid)
ps = RegisterPowerSettingNotification(this.hWnd, uid, 0)

//In MyWndProc(hWnd, wMsg, wParam, lParam)
//WM_POWERBROADCAST = 0x218
//PBT_POWERSETTINGCHANGE = 0x8013
if wMsg = WM_POWERBROADCAST && wParam = PBT_POWERSETTINGCHANGE (
    //CALL YOUR FUNCTION HERE!
    //You can additionally extract data from the lParam to verify
    //this is the notification you're waiting for (see below)
)
return CallWindowProc(oldWndProc, hWnd, wMsg, wParam, lParam)

//Before exiting
SetWindowLong(Me.hWnd, GWL_WNDPROC, oldWndProc)
UnregisterPowerSettingNotification(ps)

Este método tem o efeito colateral de desligar sua tela física (não um problema em uma máquina sem cabeçalho), e também possivelmente travar sua sessão. Certifique-se de desativar a solicitação de senha depois de dormir para evitar isso. Há algumas informações úteis adicionais sobre RegisterPowerSettingNotification disponível aqui que mostra como extrair informações do lParam na sua função WndProc caso você queira informações adicionais sobre a notificação. Divirta-se;)

    
por 27.08.2013 / 08:28
1

Você pode pesquisar o WMI para o evento de desligamento, mas não há como interrompê-lo. Portanto, não há garantia de que o seu programa receberia o evento antes que qualquer serviço seja encerrado.

    
por 23.08.2013 / 02:18