使用Golang获取Windows的空闲时间(使用GetLastInputInfo或类似方法)

huangapple go评论83阅读模式
英文:

Using Golang to get Windows idle time (GetLastInputInfo or similar)

问题

有没有使用Go语言获取Windows系统空闲时间的示例或方法?
我一直在查看Golang网站上的文档,但我似乎不知道如何访问(和使用)API来获取系统信息,包括空闲时间。

英文:

Is there an example or method of getting a Windows system's idle time using Go?
I've been looking at the documentation at the Golang site but I think I'm missing how to access (and use) the API to get system information including the idle time.

答案1

得分: 26

Go的网站是硬编码为在Linux上显示标准库包的文档。您需要获取godoc并自行运行:

go get golang.org/x/tools/cmd/godoc
godoc --http=:6060

然后在您的Web浏览器中打开http://127.0.0.1:6060/

值得注意的是syscall包,它提供了访问DLL中的函数的功能,包括UTF-16辅助函数和回调生成函数。

快速递归搜索Go树表明它没有特定于GetLastInputInfo()的API,所以除非我漏掉了什么,否则您应该能够直接从DLL调用该函数:

user32 := syscall.MustLoadDLL("user32.dll") // 或者使用NewLazyDLL()进行延迟加载
getLastInputInfo := user32.MustFindProc("GetLastInputInfo") // 或者如果您使用了NewLazyDLL(),可以使用NewProc()
// 或者您可以在上面处理错误,以便提供一些替代方案
r1, _, err := getLastInputInfo.Call(uintptr(arg))
// err始终不为nil;您需要检查r1(返回值)
if r1 == 0 { // 在这种情况下
    panic("获取最后输入信息时出错:" + err.Error())
}

您的情况涉及到一个结构体。据我所知,您只需将结构体扁平化重建(保持字段顺序不变),但必须将原始结构体中的任何int字段转换为int32,否则在64位Windows上会出现问题。请参阅MSDN上的Windows数据类型页面以获取适当的类型等效项。在您的情况下,应该是这样的:

var lastInputInfo struct {
    cbSize uint32
    dwTime uint32
}

因为这个(以及Windows API中的许多结构体)有一个cbSize字段,您需要使用结构体的大小进行初始化:

lastInputInfo.cbSize = uint32(unsafe.Sizeof(lastInputInfo))

现在我们只需要将指向lastInputInfo变量的指针传递给函数:

r1, _, err := getLastInputInfo.Call(
    uintptr(unsafe.Pointer(&lastInputInfo)))

并且记得导入syscallunsafe

DLL/LazyDLL.Call()的所有参数都是uintptrr1返回值也是uintptr。在Windows上不使用_返回值(与使用的ABI有关)。


由于我已经介绍了您在使用Go中的Windows API时需要了解的大部分内容,这些内容无法从阅读syscall文档中获取,我还要说一下(这与上述问题无关),如果一个函数同时具有ANSI版本和Unicode版本,应该使用Unicode版本(带有W后缀)和syscall包中的UTF-16转换函数以获得最佳结果。

我认为这是您(或任何人)在编写Go程序中使用Windows API所需的所有信息。

英文:

Go's website is hardcoded to show the documentation for the standard library packages on Linux. You will need to get godoc and run it yourself:

go get golang.org/x/tools/cmd/godoc
godoc --http=:6060

then open http://127.0.0.1:6060/ in your web browser.

Of note is package syscall, which provides facilities for accessing functions in DLLs, including UTF-16 helpers and callback generation functions.

Doing a quick recursive search of the Go tree says it doesn't have an API for GetLastInputInfo() in particular, so unless I'm missing something, you should be able to call that function from the DLL directly:

user32 := syscall.MustLoadDLL("user32.dll") // or NewLazyDLL() to defer loading
getLastInputInfo := user32.MustFindProc("GetLastInputInfo") // or NewProc() if you used NewLazyDLL()
// or you can handle the errors in the above if you want to provide some alternative
r1, _, err := getLastInputInfo.Call(uintptr(arg))
// err will always be non-nil; you need to check r1 (the return value)
if r1 == 0 { // in this case
    panic("error getting last input info: " + err.Error())
}

Your case involves a structure. As far as I know, you can just recreate the structure flat (keeping fields in the same order), but you must convert any int fields in the original to int32, otherwise things will break on 64-bit Windows. Consult the Windows Data Types page on MSDN for the appropriate type equivalents. In your case, this would be

var lastInputInfo struct {
    cbSize uint32
    dwTime uint32
}

Because this (like so many structs in the Windows API) has a cbSize field that requires you to initialize it with the size of the struct, we must do so too:

lastInputInfo.cbSize = uint32(unsafe.Sizeof(lastInputInfo))

Now we just need to pass a pointer to that lastInputInfo variable to the function:

r1, _, err := getLastInputInfo.Call(
    uintptr(unsafe.Pointer(&lastInputInfo)))

and just remember to import syscall and unsafe.

All args to DLL/LazyDLL.Call() are uintptr, as is the r1 return. The _ return is never used on Windows (it has to do with the ABI used).


Since I went over most of what you need to know to use the Windows API in Go that you can't gather from reading the syscall docs, I will also say (and this is irrelevant to the above question) that if a function has both ANSI and Unicode versions, you should use the Unicode versions (W suffix) and the UTF-16 conversion functions in package syscall for best results.

I think that's all the info you (or anyone, for that matter) will need to use the Windows API in Go programs.

答案2

得分: 3

关于andlabs的答案。这是一个可以直接使用的示例代码:

import (
	"time"
	"unsafe"
	"syscall"
	"fmt"
)

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() time.Duration {
	lastInputInfo.cbSize = uint32(unsafe.Sizeof(lastInputInfo))
	currentTickCount, _, _ := getTickCount.Call()
	r1, _, err := getLastInputInfo.Call(uintptr(unsafe.Pointer(&lastInputInfo)))
	if r1 == 0 {
		panic("error getting last input info: " + err.Error())
	}
	return time.Duration((uint32(currentTickCount) - lastInputInfo.dwTime)) * time.Millisecond
}

func main() {
	t := time.NewTicker(1 * time.Second)
	for range t.C {
		fmt.Println(IdleTime())
	}
}

这段代码每秒打印一次空闲时间。尝试运行它,并且不要触摸鼠标/键盘。

英文:

Regarding for answer from andlabs. This is ready for use example:

import (
	"time"
	"unsafe"
	"syscall"
	"fmt"
)

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() time.Duration {
	lastInputInfo.cbSize = uint32(unsafe.Sizeof(lastInputInfo))
	currentTickCount, _, _ := getTickCount.Call()
	r1, _, err := getLastInputInfo.Call(uintptr(unsafe.Pointer(&lastInputInfo)))
	if r1 == 0 {
			panic("error getting last input info: " + err.Error())
	}
	return time.Duration((uint32(currentTickCount) - lastInputInfo.dwTime)) * time.Millisecond
}

func main() {
	t := time.NewTicker(1 * time.Second)
	for range t.C {
		fmt.Println(IdleTime())
	}
}

This is code print idle time every second. Try run and don't touch mouse/keyboard

huangapple
  • 本文由 发表于 2014年4月9日 06:29:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/22949444.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定