详情

简单记录go实现窗口吸附

头像
日期:04-03w 标签:编程 w 总字数:2914
简单记录go语言实现windows桌面应用窗口吸附,类似于“快捷短语软件”可吸附在聊天软件的窗口右侧进行吸附跟随移动..
使用Wails框架实现桌面应用开发,通过Go的syscall和windows包直接调用Windows API

使用了user32.dll中的多个API:

  • EnumWindows – 枚举所有顶层窗口
  • GetWindowTextW – 获取窗口标题
  • GetWindowRect – 获取窗口位置和大小
  • IsWindowVisible – 检查窗口是否可见
使用golang.org/x/sys/windows包进行系统调用,在StartAttach方法中使用了goroutine持续跟踪窗口位置,实现了窗口枚举、标题获取、位置跟踪等功能 ◦ 可以附加到其他窗口并跟随其移动。

效果gif图:

app.go文件代码:

package main

import (
"context"
"syscall"
"time"
"unsafe"
"github.com/wailsapp/wails/v2/pkg/runtime" // Wails 框架提供,用于操作窗口等
"golang.org/x/sys/windows"                 // 提供对 Windows 系统调用的支持
)

// 窗口的矩形区域
type RECT struct {
Left   int32 // 矩形左边界
Top    int32 // 矩形上边界
Right  int32 // 矩形右边界
Bottom int32 // 矩形下边界
}

// 定义 Windows 系统相关函数的变量
var (
user32              = windows.NewLazySystemDLL("user32.dll") // 加载 user32.dll,用于调用 Windows 窗口相关函数
enumWindowsProc     = user32.NewProc("EnumWindows")          // 枚举窗口函数
getWindowTextProc   = user32.NewProc("GetWindowTextW")       // 获取窗口标题函数
getWindowRectProc   = user32.NewProc("GetWindowRect")        // 获取窗口矩形区域函数
isWindowVisibleProc = user32.NewProc("IsWindowVisible")      // 判断窗口是否可见函数
)

// 存储窗口信息
type WindowInfo struct {
Handle int    // 窗口句柄
Title  string // 窗口标题
}

// 存储应用程序的状态
type App struct {
ctx        context.Context // 上下文
targetHWND uintptr         // 目标窗口句柄
running    bool            // 是否正在运行
}

// 创建一个新的 App 实例
func NewApp() *App {
return &App{}
}

// Startup 方法,在应用程序启动时调用
func (a *App) Startup(ctx context.Context) {
a.ctx = ctx // 设置上下文
}

// GetWindows 方法,获取所有可见窗口的信息
func (a *App) GetWindows() []WindowInfo {
var windows []WindowInfo // 用于存储窗口信息的切片
// 定义回调函数,用于枚举窗口时调用
cb := syscall.NewCallback(func(hwnd uintptr, _ uintptr) uintptr {
if isWindowVisible(hwnd) { // 判断窗口是否可见
title := getWindowText(hwnd) // 获取窗口标题
if len(title) > 0 {          // 如果标题不为空
windows = append(windows, WindowInfo{ // 将窗口信息添加到切片中
Handle: int(hwnd),
Title:  title,
})
}
}
return 1 // 返回 1 表示继续枚举
})
// 调用 EnumWindows 函数枚举窗口
enumWindowsProc.Call(cb, 0)
return windows // 返回窗口信息切片
}

// StartAttach 方法,开始附加到目标窗口
func (a *App) StartAttach(hwnd int) {
a.targetHWND = uintptr(hwnd) // 设置目标窗口句柄
a.running = true             // 设置为正在运行

// 实时更新窗口位置和大小
go func() {
for a.running { // 当正在运行时
if rect, err := getWindowRect(a.targetHWND); err == nil { // 获取目标窗口矩形区域
// 分两步设置位置和大小
runtime.WindowSetPosition(a.ctx, int(rect.Right), int(rect.Top)) // 设置窗口位置
runtime.WindowSetSize(a.ctx, 300, int(rect.Bottom-rect.Top))     // 设置窗口大小
}
time.Sleep(100 * time.Millisecond) // 每 100 毫秒更新一次
}
}()
}

// StopAttach 方法,停止附加到目标窗口
func (a *App) StopAttach() {
a.running = false // 设置为停止运行
}

// getWindowRect 函数,获取窗口的矩形区域
func getWindowRect(hwnd uintptr) (*RECT, error) {
var rect RECT                          // 创建 RECT 结构体实例
ret, _, err := getWindowRectProc.Call( // 调用 GetWindowRect 函数
hwnd,
uintptr(unsafe.Pointer(&rect)),
)
if ret == 0 { // 如果返回值为 0,表示失败
return nil, err
}
return &rect, nil // 返回矩形区域和 nil 错误
}

// isWindowVisible 函数,判断窗口是否可见
func isWindowVisible(hwnd uintptr) bool {
ret, _, _ := isWindowVisibleProc.Call(hwnd) // 调用 IsWindowVisible 函数
return ret != 0                             // 如果返回值不为 0,表示窗口可见
}

// getWindowText 函数,获取窗口标题
func getWindowText(hwnd uintptr) string {
var text [512]uint16    // 创建一个足够大的缓冲区存储标题
getWindowTextProc.Call( // 调用 GetWindowText 函数
hwnd,
uintptr(unsafe.Pointer(&text[0])),
uintptr(len(text)),
)
return syscall.UTF16ToString(text[:]) // 将 UTF-16 编码的字符串转换为 Go 的字符串
}
120

通过网页链接进行分享:

全部评论
暂无评论
访客头像