英文:
Getting window geometry in Go for Windows
问题
我想用Go语言创建一个工具,可以调整屏幕上多个窗口的大小。举个例子,假设我想找到我的Firefox窗口和Atom(文本编辑器)窗口,并将它们放置在屏幕的一半位置(Firefox在左侧,Atom在右侧)。
到目前为止,我已经意识到我需要使用Windows API来实现这个功能。我创建了一个方法,可以获取所有窗口的句柄和标题,但是我在处理几何信息时遇到了困难。我了解到API调用GetWindowRect
可以帮助我,但是我如何从指向矩形的指针中获取信息呢?
后续问题1:我还能获取窗口的哪些其他信息?
后续问题2:如何调整窗口的大小,使其正好占据屏幕的一半?我猜我需要另一个调用来获取监视器的尺寸。
我目前的代码如下。主程序找到所有句柄,并显示标题中包含“Atom”的窗口。Windows包含访问Windows API的代码。
我的当前结果是我得到了两个Atom的句柄(为什么不只有一个?)。我猜我还需要学习更多关于Windows API的知识。有没有好的摘要来理解基础知识?
main.go:
package main
import (
"resizer/windows"
"fmt"
"log"
"strings"
)
func main() {
const title = "Atom"
m := windows.GetAllWindows()
fmt.Printf("窗口映射:\n")
for handle := range m {
if strings.Contains(m[handle].Title(), title) {
fmt.Printf("'%v'\n", m[handle])
}
}
}
windows.go:
package windows
import (
"fmt"
"log"
"syscall"
"unsafe"
)
var (
user32 = syscall.MustLoadDLL("user32.dll")
procEnumWindows = user32.MustFindProc("EnumWindows")
procGetWindowTextW = user32.MustFindProc("GetWindowTextW")
)
// Window表示Windows操作系统中打开的任何窗口
type Window struct {
handle syscall.Handle
title string
}
// Title返回窗口的标题
func (w Window) Title() string {
return w.title
}
// GetAllWindows查找所有当前打开的窗口
func GetAllWindows() map[syscall.Handle]Window {
m := make(map[syscall.Handle]Window)
cb := syscall.NewCallback(func(h syscall.Handle, p uintptr) uintptr {
bytes := make([]uint16, 200)
_, err := GetWindowText(h, &bytes[0], int32(len(bytes)))
title := "|||找不到标题|||"
if err == nil {
title = syscall.UTF16ToString(bytes)
}
m[h] = Window{h, title}
return 1 // 继续枚举
})
EnumWindows(cb, 0)
return m
}
// EnumWindows循环遍历所有窗口,并在每个窗口上调用回调函数
func EnumWindows(enumFunc uintptr, lparam uintptr) (err error) {
r1, _, e1 := syscall.Syscall(procEnumWindows.Addr(), 2, uintptr(enumFunc), uintptr(lparam), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
// GetWindowText根据给定的句柄获取窗口的标题
func GetWindowText(hwnd syscall.Handle, str *uint16, maxCount int32) (len int32, err error) {
r0, _, e1 := syscall.Syscall(procGetWindowTextW.Addr(), 3, uintptr(hwnd), uintptr(unsafe.Pointer(str)), uintptr(maxCount))
len = int32(r0)
if len == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
英文:
I want to create a tool with Go that lets me resize multiple windows on my screen. As an example lets assume that I want to find my Firefox window and my Atom (text editor) window and place them, so that they take up exactly half of my screen (FF left, Atom right).
So far I realized, that I need to use the Windows API for that. I created a method that gives me all handles and the titles of all windows, but I'm struggling with geometry information. I understand that the api call GetWindowRect
will help, but how can I get the information out of a pointer to a rect?
Follow up question 1: what other information can I get about the windows?
Follow up question 2: How do I resize the window so that it takes exactly half my screen size? I guess, I need another call to get the monitor dimensions.
What I have so far is the code below. The main program finds all handles and displays those containing 'Atom' in the title. The windows package contains the code accessing the windows API.
My current result is that I get 2 handles for atom (why not just 1?). I guess, I have to learn more about the Windows API, too. Are there good summaries to understand the basics?
main.go:
package main
import (
"resizer/windows"
"fmt"
"log"
"strings"
)
func main() {
const title = "Atom"
m := windows.GetAllWindows()
fmt.Printf("Map of windows: \n")
for handle := range m {
if strings.Contains(m[handle].Title(), title) {
fmt.Printf("'%v'\n", m[handle])
}
}
}
windows.go:
package windows
import (
"fmt"
"log"
"syscall"
"unsafe"
)
var (
user32 = syscall.MustLoadDLL("user32.dll")
procEnumWindows = user32.MustFindProc("EnumWindows")
procGetWindowTextW = user32.MustFindProc("GetWindowTextW")
)
// Window represents any Window that is opened in the Windows OS
type Window struct {
handle syscall.Handle
title string
}
// Title returns the title of the window
func (w Window) Title() string {
return w.title
}
// GetAllWindows finds all currently opened windows
func GetAllWindows() map[syscall.Handle]Window {
m := make(map[syscall.Handle]Window)
cb := syscall.NewCallback(func(h syscall.Handle, p uintptr) uintptr {
bytes := make([]uint16, 200)
_, err := GetWindowText(h, &bytes[0], int32(len(bytes)))
title := "||| no title found |||"
if err == nil {
title = syscall.UTF16ToString(bytes)
}
m[h] = Window{h, title}
return 1 // continue enumeration
})
EnumWindows(cb, 0)
return m
}
// EnumWindows loops through all windows and calls a callback function on each
func EnumWindows(enumFunc uintptr, lparam uintptr) (err error) {
r1, _, e1 := syscall.Syscall(procEnumWindows.Addr(), 2, uintptr(enumFunc), uintptr(lparam), 0)
if r1 == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
// GetWindowText gets the title of a Window given by a certain handle
func GetWindowText(hwnd syscall.Handle, str *uint16, maxCount int32) (len int32, err error) {
r0, _, e1 := syscall.Syscall(procGetWindowTextW.Addr(), 3, uintptr(hwnd), uintptr(unsafe.Pointer(str)), uintptr(maxCount))
len = int32(r0)
if len == 0 {
if e1 != 0 {
err = error(e1)
} else {
err = syscall.EINVAL
}
}
return
}
答案1
得分: 1
GetWindowRect()
函数将窗口的几何信息写入你传递指针的RECT
结构中。它的操作方式与你已经使用的GetWindowText()
函数相同,不同之处在于你需要自己提供RECT
结构。
你应该可以直接复制该结构。如果要替换数据类型,请参考这个页面。RECT
的定义中指定了所有字段都是LONG
类型,该页面说明LONG
是“32位有符号整数”。因此,以下定义应该足够:
type RECT struct {
left int32 // 如果要导出该类型,则可以使用 Left、Top 等命名
top int32
right int32
bottom int32
}
(可能与本问题无关,但值得指出的是,RECT
的操作方式与image.Rectangle
相同,其中left
和top
对应于Min
,right
和bottom
对应于Max
。它们并不完全相同,因为image.Rectangle
使用的是int
类型,所以如果你想使用image
的几何函数来操作矩形而不是GDI的函数,你可能需要考虑提供转换函数。)
英文:
GetWindowRect()
writes the geometry to the RECT
structure you pass the pointer to in. It operates exactly like the GetWindowText()
call you already have; the difference is you have to provide the RECT
structure yourself.
You should be able to just get away with copying the structure verbatim. To substitute data types, use this page. The definition for RECT
says all the fields are LONG
, which that page says is "[a] 32-bit signed integer". So this should suffice:
type RECT struct {
left int32 // or Left, Top, etc. if this type is to be exported
top int32
right int32
bottom int32
}
(Most likely irrelevant, but it's worth pointing out that RECT
operates identically to image.Rectangle
, with left
and top
being Min
and right
and bottom
being Max
. They are not identical because image.Rectangle
uses int
, so you may want to consider providing conversion functions if you want to use image
's geometry functions to manipulate rectangles instead of GDI's.)
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论