在当前的计算机操作系统中,屏幕顶层是指当前正在显示的窗口或应用程序。在Windows系统中,通过Win32 API可以获取和管理顶层窗口。在本文中,我们将介绍如何使用golang实现一个可以获取和显示屏幕顶层窗口的程序。

  1. 获取顶层窗口列表

在golang中,使用syscall包可以调用系统级函数。我们可以使用FindWindowEx函数来获取顶层窗口的句柄列表。例如:

hwnd := uintptr(0)
var list []uintptr
for {
    hwnd = uintptr(C.FindWindowEx(
        0,
        hwnd,
        nil,
        nil,
    ))
    if hwnd == 0 {
        break
    }
    list = append(list, hwnd)
}

其中,FindWindowEx函数的参数含义如下:

  • 第一个参数是查找开始的父窗口句柄,如果为0,则从桌面开始查找。
  • 第二个参数是上一个窗口的句柄,如果为0,则从第一个窗口开始查找。
  • 第三个参数是窗口类名,nil表示不限制窗口类名。
  • 第四个参数是窗口标题,nil表示不限制窗口标题。

返回值为找到的窗口句柄,如果没有找到,则返回0。

  1. 获取顶层窗口信息

获取到窗口句柄之后,我们可以调用GetWindowRect函数来获取窗口的位置和大小信息。例如:

var rect syscall.Rect
C.GetWindowRect(
    C.HWND(hwnd),
    (*C.RECT)(unsafe.Pointer(&rect)),
)

其中,GetWindowRect函数的参数含义如下:

  • 第一个参数是窗口句柄。
  • 第二个参数是指向窗口位置和大小信息的指针。
  1. 显示顶层窗口信息

通过获取到的窗口位置和大小信息,我们可以使用golang的图形库显示该窗口的缩略图。例如:

thumb, err := goscreenshot.CaptureWindowRect(rect)
if err != nil {
    log.Println(err)
    continue
}
screen, err := png.Decode(bytes.NewReader(thumb))
if err != nil {
    log.Println(err)
    continue
}
win := Window{
    Title:   title,
    X:       rect.Left,
    Y:       rect.Top,
    Width:   rect.Right - rect.Left,
    Height:  rect.Bottom - rect.Top,
    Picture: screen,
}
viewer.Show(win)

其中,CaptureWindowRect函数通过golang的screenshot包实现了窗口截图的功能。然后用golang的image/png包读取图像数据,并将其显示在窗口中。最后定义了Window结构并使用viewer.Show方法来显示窗口信息。

  1. 完整代码

完整的代码如下:

package main

import (
    "bytes"
    "image/png"
    "log"
    "unsafe"

    "github.com/lxn/walk"
    . "github.com/lxn/walk/declarative"
    "golang.org/x/sys/windows"
)

var (
    C = windows.NewLazySystemDLL("user32.dll")
)

// 窗口信息结构
type Window struct {
    Title   string      // 窗口标题
    X       int32       // 窗口左上角X坐标
    Y       int32       // 窗口左上角Y坐标
    Width   int32       // 窗口宽度
    Height  int32       // 窗口高度
    Picture image.Image // 窗口截图
}

func main() {

    // 创建窗口
    var mw *walk.MainWindow
    var tv *walk.TableView
    var viewer *walk.ImageView
    MainWindow{
        Title:   "Screen Viewer",
        MinSize: Size{640, 480},
        Layout:  VBox{},
        Children: []Widget{
            TableView{
                AssignTo:         &tv,
                AlternatingRowBG: true,
                Columns: []TableViewColumn{
                    {Title: "Title"},
                    {Title: "X"},
                    {Title: "Y"},
                    {Title: "Width"},
                    {Title: "Height"},
                },
            },
            ImageView{
                AssignTo: &viewer,
            },
        },
    }.Create(&mw)

    // 获取顶层窗口列表
    hwnd := uintptr(0)
    var list []uintptr
    for {
        hwnd = uintptr(C.FindWindowEx(
            0,
            hwnd,
            nil,
            nil,
        ))
        if hwnd == 0 {
            break
        }
        list = append(list, hwnd)
    }

    // 遍历窗口列表并显示窗口信息
    var data []Window
    for _, hwnd := range list {
        var rect syscall.Rect
        C.GetWindowRect(
            C.HWND(hwnd),
            (*C.RECT)(unsafe.Pointer(&rect)),
        )
        title := getWindowText(hwnd)
        if title == "" {
            continue
        }
        thumb, err := goscreenshot.CaptureWindowRect(rect)
        if err != nil {
            log.Println(err)
            continue
        }
        screen, err := png.Decode(bytes.NewReader(thumb))
        if err != nil {
            log.Println(err)
            continue
        }
        win := Window{
            Title:   title,
            X:       rect.Left,
            Y:       rect.Top,
            Width:   rect.Right - rect.Left,
            Height:  rect.Bottom - rect.Top,
            Picture: screen,
        }
        data = append(data, win)
        tv.PublishRowsReset()
    }

    // 设置模型
    model, _ := NewWindowModel(data)
    tv.SetModel(model)

    // 开始消息循环
    mw.Run()
}

// 获取窗口标题
func getWindowText(hwnd uintptr) string {
    var buf [256]uint16
    C.GetWindowText(
        C.HWND(hwnd),
        (*C.CHAR)(unsafe.Pointer(&buf)),
        256,
    )
    return syscall.UTF16ToString(buf[:])
}

// 窗口模型
type WindowModel struct {
    walk.TableModelBase
    items []Window
}

func NewWindowModel(items []Window) (*WindowModel, error) {
    m := new(WindowModel)

    m.items = items

    return m, nil
}

func (m *WindowModel) RowCount() int {
    return len(m.items)
}

func (m *WindowModel) Value(row, col int) interface{} {
    item := m.items[row]

    switch col {
    case 0:
        return item.Title

    case 1:
        return item.X

    case 2:
        return item.Y

    case 3:
        return item.Width

    case 4:
        return item.Height

    }

    panic("unexpected col")
}
  1. 总结

本文介绍了如何使用golang实现获取和显示屏幕顶层窗口的程序。通过调用Windows API函数、实现窗口截图和图像显示等操作,我们可以很方便得编写一个简单的屏幕截图工具。同时,在golang的图形库和Windows API的配合下,我们可以编写出跨平台的应用程序。