【问题标题】:Using Golang to get Windows idle time (GetLastInputInfo or similar)使用 Golang 获取 Windows 空闲时间(GetLastInputInfo 或类似)
【发布时间】:2014-04-08 22:29:43
【问题描述】:

有没有使用 Go 获取 Windows 系统空闲时间的示例或方法?
我一直在查看 Golang 站点上的文档,但我想我错过了如何访问(和使用)API 来获取系统信息,包括空闲时间。

【问题讨论】:

    标签: winapi go idle-timer


    【解决方案1】:

    Go 的网站经过硬编码以显示 Linux 上标准库包的文档。你需要自己获取 godoc 并运行它:

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

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

    值得注意的是包 syscall,它提供了访问 DLL 中的函数的工具,包括 UTF-16 帮助程序和回调生成函数。

    对 Go 树进行快速递归搜索表明它没有专门针对 GetLastInputInfo() 的 API,因此除非我遗漏了什么,否则您应该能够直接从 DLL 调用该函数:

    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())
    }
    

    您的案例涉及结构。据我所知,您可以重新创建平面结构(保持字段顺序相同),但您必须将原始中的任何 int 字段转换为 int32,否则 things will break on 64-bit Windows .请咨询Windows Data Types page on MSDN 以获取相应的等效类型。在你的情况下,这将是

    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 返回也是如此。 _ return 在 Windows 上从不使用(它与使用的 ABI 有关)。


    由于我回顾了在 Go 中使用 Windows API 所需了解的大部分内容,而这些内容是通过阅读 syscall 文档无法收集到的,我还要说(这与上述问题无关)如果函数同时具有 ANSI 和 Unicode 版本,则应使用 Unicode 版本(W 后缀)和包 syscall 中的 UTF-16 转换函数以获得最佳效果。

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

    【讨论】:

    • 我什至不使用 Windows,但这是非常有用的。
    • 当我尝试采样时,我不断地为 r1 返回 1。该结果是说自空闲以来已经有 1 个计时器滴答,还是只是返回一个布尔值 true?我应该得到 dwTime 吗? (感谢@andlabs 提供的信息丰富的解释。我认为我走在正确的道路上,只是缺少对我要提取的信息的一些关键理解。)
    • @BartSilverstrim (抱歉延迟响应)是的,这里的r1是GetLastInputInfo()的返回值,according to MSDN代表函数是否成功。您需要检查 r1,如果非零,则在调用后将 dwTime 从您的结构中取出,这是您真正想要的值。 (另见_Out_注解,它表示函数在那里返回值。)如果r1 == 0,err将包含错误信息(即为你调用GetLastError())。
    • @andlabs 感谢您的澄清!除此之外,我一直在研究应用程序,虽然在从 dwTime 获取实际滴答计数之前我没有考虑检查 r1 的结果,但我做了实验并从中获得了结果。你非常乐于助人,再次感谢你!我必须添加一条注释才能返回并稍后添加该检查以确保完整性。
    【解决方案2】:

    关于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())
        }
    }
    

    这是每秒的代码打印空闲时间。尝试运行,不要触摸鼠标/键盘

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-05
      • 1970-01-01
      • 2011-04-24
      • 1970-01-01
      • 1970-01-01
      • 2011-09-23
      相关资源
      最近更新 更多