2 votes

Accrocher le bouton d'alimentation du système dans Windows

J'ai un ordinateur sans tête qui exécute un service personnalisé que je veux activer/désactiver à l'aide du bouton d'alimentation, plutôt que de devoir se connecter à distance à chaque fois. L'ordinateur fait aussi d'autres choses, donc l'éteindre n'est pas une option.

Est-il possible de connecter le bouton d'alimentation du système sous Windows XP et plus, de sorte que mon programme reçoive l'événement suivant avant Windows déclenche un événement de mise hors tension/de veille (avant que PBT_APMQUERYSUSPEND est envoyé) ?

J'ai trouvé la solution, voir ma propre réponse ci-dessous !

2voto

Jason Voegele Points 1063

C'est en effet possible, mais c'est un peu bricolé et cela nécessite deux implémentations complètement différentes selon la version de Windows. Pour les deux méthodes, vous devez configurer votre bouton d'alimentation pour mettre l'ordinateur en veille dans vos options d'alimentation.

Windows XP et inférieur :

Vous devez écraser le fichier de la fenêtre principale de votre programme. WndProc fonction. Dans les IDE qui ne prennent pas en charge cette fonction de manière native, on peut utiliser la fonction suivante SetWindowLong dans l'API user32. Dans votre WndProc écoutez un WM_POWERBROADCAST (0x218) message. Si vous recevez un message avec wParam de PBT_APMQUERYSUSPEND (0x0) appelle la fonction souhaitée et retourne ensuite BROADCAST_QUERY_DENY (0x424D5144) au lieu d'appeler la base WndProc fonction. Exemple de code :

//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 et plus : (merci à Remy Lebeau de m'avoir mis sur la bonne voie)

Vous devez remplacer WndProc comme pour XP, mais aussi appeler SetThreadExecutionState dans l'API de kernel32 pour désactiver le mode veille et RegisterPowerSettingNotification dans l'API user32 pour écouter les notifications d'alimentation avancées. Vous écouterez, en particulier, la fonction GUID_SYSTEM_AWAYMODE qui est envoyée lorsqu'il a été demandé au système de se mettre en veille mais qu'il n'a pas pu le faire. Pour convertir facilement une chaîne de caractères en un LPCGUID vous pouvez utiliser UuidFromStringA dans l'API rpcrt4.dll. Exemple de code :

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)

Cette méthode a pour effet secondaire d'éteindre votre écran physique (ce qui n'est pas un problème sur une machine sans tête), et aussi de verrouiller éventuellement votre session. Assurez-vous de désactiver la demande de mot de passe après le sommeil pour éviter cela. Vous trouverez d'autres informations utiles sur RegisterPowerSettingNotification disponible sur aquí qui montre comment extraire des informations du lParam en su WndProc au cas où vous souhaiteriez obtenir des informations supplémentaires sur la notification. Amusez-vous bien ;)

1voto

Keltari Points 67159

Vous pouvez interroger WMI pour l'événement d'arrêt, mais il n'y a aucun moyen de l'arrêter. Il n'y a donc aucune garantie que votre programme recevra l'événement avant que les services ne soient arrêtés.

SistemesEz.com

SystemesEZ est une communauté de sysadmins où vous pouvez résoudre vos problèmes et vos doutes. Vous pouvez consulter les questions des autres sysadmins, poser vos propres questions ou résoudre celles des autres.

Powered by:

X