使用windows自带的当系统空闲一段时间后进入睡眠模式不太可靠,用golang写了个小工具定时使用getLastInputInfo获取上次鼠标和键盘的活动时间,超过一段时间未操作进入睡眠状态,当在全屏状态时不进入睡眠状态,如在播放视频时,全屏排除锁屏界面和win+d快捷键进入的桌面。
获取是否有程序在全屏使用的golang cgo实现,windows下编译运行需安装gcc,可安装TDM-GCC。
package main
/*
#include <windows.h>
#include <stdio.h>
int CheckFullscreen() {
int bFullScreen = 0;
HWND hWnd = GetForegroundWindow();
RECT rcApp, rcDesk;
GetWindowRect(GetDesktopWindow(), &rcDesk);
if (hWnd!=GetDesktopWindow() && hWnd!=GetShellWindow()) {
GetWindowRect(hWnd, &rcApp);
if (rcApp.left<=rcDesk.left && rcApp.top<=rcDesk.top && rcApp.right>=rcDesk.right && rcApp.bottom>=rcDesk.bottom) {
char wnd_name[256];
char wnd_title[256];
GetWindowText(hWnd,wnd_title,sizeof(wnd_title));
GetClassName(hWnd, wnd_name, sizeof(wnd_name));
printf("title: %s\n", wnd_title);
printf("name: %s\n", wnd_name);
if (strcmp(wnd_name, "Windows.UI.Core.CoreWindow") != 0 && strcmp(wnd_name, "WorkerW") != 0){
bFullScreen =1;
}
}
}
return bFullScreen;
}
*/
import "C"
import (
"flag"
"fmt"
"os/exec"
"strings"
"syscall"
"time"
"unsafe"
)
var (
user32 = syscall.MustLoadDLL("user32.dll")
kernel32 = syscall.MustLoadDLL("kernel32.dll")
getLastInputInfo = user32.MustFindProc("GetLastInputInfo")
getTickCount = kernel32.MustFindProc("GetTickCount")
lastInputInfo struct {
cbSize uint32
dwTime uint32
}
)
func IdleTime() uint32 {
lastInputInfo.cbSize = uint32(unsafe.Sizeof(lastInputInfo))
currentTickCount, _, _ := getTickCount.Call()
r1, _, err := getLastInputInfo.Call(uintptr(unsafe.Pointer(&lastInputInfo)))
if r1 == 0 {
fmt.Println("error getting last input info: " + err.Error())
return 0
}
return (uint32(currentTickCount) - lastInputInfo.dwTime)
}
func Execute(command string) (bool, string, error) {
// splitting head => g++ parts => rest of the command
parts := strings.Fields(command)
head := parts[0]
parts = parts[1:len(parts)]
out, err := exec.Command(head, parts...).Output()
if err != nil {
return false, "", err
}
return true, string(out), nil
}
func sleepCommandLineImplementation(cmd string) {
if cmd == "" {
cmd = "C:\\Windows\\System32\\rundll32.exe powrprof.dll,SetSuspendState 0,1,0"
}
fmt.Println("Sleep implementation [windows], sleep command is [", cmd, "]")
_, _, err := Execute(cmd)
if err != nil {
fmt.Println("Can't execute command [" + cmd + "] : " + err.Error())
} else {
fmt.Println("Command correctly executed")
}
}
func sleepDLLImplementation() {
var mod = syscall.NewLazyDLL("Powrprof.dll")
var proc = mod.NewProc("SetSuspendState")
// DLL API : public static extern bool SetSuspendState(bool hiberate, bool forceCritical, bool disableWakeEvent);
// ex. : uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr("Done Title"))),
ret, _, _ := proc.Call(0,
uintptr(0), // hibernate
uintptr(1), // forceCritical
uintptr(0)) // disableWakeEvent
fmt.Println("Command executed, result code [" + fmt.Sprint(ret) + "]")
}
func main() {
var timeout,interval int
var execute string
flag.IntVar(&timeout, "t", 15, "minute")
flag.IntVar(&interval, "i", 60, "second")
flag.StringVar(&execute, "c", "api", "api or cli")
flag.Parse()
fmt.Println("timeout", timeout, "minute")
fmt.Println("interval", interval, "second")
fmt.Println("execute", execute)
//间隔不能太小,不然通过网络唤醒间隔时间内无操作会再次进入睡眠
t := time.NewTicker(time.Duration(interval) * time.Second)
for range t.C {
idle := IdleTime()
fmt.Println(time.Now().Format("2006-01-02 15:04:05") ,"idle time", idle)
fmt.Println(time.Now().Format("2006-01-02 15:04:05") ,"full screen", C.CheckFullscreen())
if idle > uint32(timeout)*60*1000 && C.CheckFullscreen() != 1 {
if execute == "api"{
sleepDLLImplementation()
} else {
sleepCommandLineImplementation("rundll32.exe powrprof.dll,SetSuspendState 0,1,0")
//sleepCommandLineImplementation("")
}
}
}
}
如进入睡眠时为休眠,可关闭休眠:
powercfg -h off
参考:
https://bbs.csdn.net/topics/390838652
https://stackoverflow.com/questions/22949444/using-golang-to-get-windows-idle-time-getlastinputinfo-or-similar
https://github.com/SR-G/sleep-on-lan
https://www.cnblogs.com/yeshou/p/5197765.html