260 lines
6.8 KiB
Go
260 lines
6.8 KiB
Go
![]() |
package tool
|
|||
|
|
|||
|
import (
|
|||
|
"image"
|
|||
|
"image/png"
|
|||
|
"log"
|
|||
|
"os"
|
|||
|
"strings"
|
|||
|
"syscall"
|
|||
|
"unsafe"
|
|||
|
|
|||
|
"github.com/kbinani/screenshot"
|
|||
|
)
|
|||
|
|
|||
|
// Windows API常量
|
|||
|
const (
|
|||
|
SRCCOPY = 0x00CC0020 // 复制操作的代码
|
|||
|
)
|
|||
|
|
|||
|
// Windows API结构体定义
|
|||
|
type RECT struct { // 矩形区域结构
|
|||
|
Left, Top, Right, Bottom int32
|
|||
|
}
|
|||
|
|
|||
|
type BITMAPINFOHEADER struct { // 位图信息头
|
|||
|
Size uint32
|
|||
|
Width int32
|
|||
|
Height int32
|
|||
|
Planes uint16
|
|||
|
BitCount uint16
|
|||
|
Compression uint32
|
|||
|
SizeImage uint32
|
|||
|
XPelsPerMeter int32
|
|||
|
YPelsPerMeter int32
|
|||
|
ClrUsed uint32
|
|||
|
ClrImportant uint32
|
|||
|
}
|
|||
|
|
|||
|
var (
|
|||
|
user32 = syscall.NewLazyDLL("user32.dll")
|
|||
|
procEnumWindows = user32.NewProc("EnumWindows")
|
|||
|
procGetWindowText = user32.NewProc("GetWindowTextW")
|
|||
|
titless string
|
|||
|
)
|
|||
|
|
|||
|
// 定义回调函数的类型
|
|||
|
type EnumWindowsProc func(hwnd uintptr, lParam uintptr) uintptr
|
|||
|
|
|||
|
// 枚举窗口的回调函数
|
|||
|
func enumWindowsProc(hwnd uintptr, lParam uintptr) uintptr {
|
|||
|
var buffer [256]uint16
|
|||
|
length, _, _ := procGetWindowText.Call(hwnd, uintptr(unsafe.Pointer(&buffer[0])), uintptr(len(buffer)))
|
|||
|
if length > 0 {
|
|||
|
title := syscall.UTF16ToString(buffer[:length])
|
|||
|
// fmt.Printf("Window Handle: %x, Title: %s\n", hwnd, title)
|
|||
|
if strings.Contains(title, "Chromium") && title != "GFoxChromiumCloseForm" {
|
|||
|
titless = title
|
|||
|
log.Println(titless)
|
|||
|
}
|
|||
|
}
|
|||
|
return 1 // 返回非零值以继续枚举
|
|||
|
}
|
|||
|
|
|||
|
func Jietu(path_img string) {
|
|||
|
// 将 Go 回调函数转换为 Windows 可调用的回调函数
|
|||
|
cb := syscall.NewCallback(enumWindowsProc)
|
|||
|
|
|||
|
// 调用 EnumWindows 函数
|
|||
|
procEnumWindows.Call(cb, 0)
|
|||
|
// 需要截图的窗口标题(根据实际情况修改)
|
|||
|
windowTitle := titless
|
|||
|
|
|||
|
// 加载Windows DLL
|
|||
|
user32 := syscall.NewLazyDLL("user32.dll")
|
|||
|
// gdi32 := syscall.NewLazyDLL("gdi32.dll")
|
|||
|
|
|||
|
// 获取API函数指针
|
|||
|
findWindow := user32.NewProc("FindWindowW")
|
|||
|
getWindowDC := user32.NewProc("GetWindowDC")
|
|||
|
getWindowRect := user32.NewProc("GetWindowRect")
|
|||
|
releaseDC := user32.NewProc("ReleaseDC")
|
|||
|
getDpiForWindow := user32.NewProc("GetDpiForWindow") // 获取窗口DPI
|
|||
|
|
|||
|
// createCompatibleDC := gdi32.NewProc("CreateCompatibleDC")
|
|||
|
// createCompatibleBitmap := gdi32.NewProc("CreateCompatibleBitmap")
|
|||
|
// selectObject := gdi32.NewProc("SelectObject")
|
|||
|
// bitBlt := gdi32.NewProc("BitBlt")
|
|||
|
// deleteDC := gdi32.NewProc("DeleteDC")
|
|||
|
// deleteObject := gdi32.NewProc("DeleteObject")
|
|||
|
// getDIBits := gdi32.NewProc("GetDIBits")
|
|||
|
|
|||
|
// 1. 查找目标窗口句柄
|
|||
|
windowName := syscall.StringToUTF16Ptr(windowTitle)
|
|||
|
hwnd, _, _ := findWindow.Call(0, uintptr(unsafe.Pointer(windowName)))
|
|||
|
if hwnd == 0 {
|
|||
|
panic("找不到指定窗口")
|
|||
|
}
|
|||
|
|
|||
|
// 2. 获取窗口设备上下文(DC)
|
|||
|
hdc, _, _ := getWindowDC.Call(hwnd)
|
|||
|
if hdc == 0 {
|
|||
|
panic("获取窗口DC失败")
|
|||
|
}
|
|||
|
defer releaseDC.Call(hwnd, hdc) // 确保释放DC
|
|||
|
|
|||
|
// 3. 获取窗口尺寸
|
|||
|
var rect RECT
|
|||
|
ret, _, _ := getWindowRect.Call(hwnd, uintptr(unsafe.Pointer(&rect)))
|
|||
|
if ret == 0 {
|
|||
|
panic("获取窗口尺寸失败")
|
|||
|
}
|
|||
|
|
|||
|
// 4. 获取窗口DPI
|
|||
|
dpi, _, _ := getDpiForWindow.Call(hwnd)
|
|||
|
if dpi == 0 {
|
|||
|
dpi = 96 // 默认DPI
|
|||
|
}
|
|||
|
|
|||
|
// 5. 计算实际窗口尺寸(考虑DPI缩放)
|
|||
|
scaleFactor := float64(dpi) / 216
|
|||
|
|
|||
|
width := int(float64(rect.Right-rect.Left) / scaleFactor)
|
|||
|
height := int(float64(rect.Bottom-rect.Top) / scaleFactor)
|
|||
|
log.Println(width, height)
|
|||
|
// // 6. 创建内存兼容DC
|
|||
|
// memHdc, _, _ := createCompatibleDC.Call(hdc)
|
|||
|
// if memHdc == 0 {
|
|||
|
// panic("创建兼容DC失败")
|
|||
|
// }
|
|||
|
// defer deleteDC.Call(memHdc)
|
|||
|
|
|||
|
// // 7. 创建兼容位图
|
|||
|
// hBitmap, _, _ := createCompatibleBitmap.Call(hdc, uintptr(width), uintptr(height))
|
|||
|
// if hBitmap == 0 {
|
|||
|
// panic("创建兼容位图失败")
|
|||
|
// }
|
|||
|
// defer deleteObject.Call(hBitmap)
|
|||
|
|
|||
|
// // 8. 将位图选入内存DC
|
|||
|
// oldObj, _, _ := selectObject.Call(memHdc, hBitmap)
|
|||
|
// if oldObj == 0 {
|
|||
|
// panic("选定位图失败")
|
|||
|
// }
|
|||
|
// defer selectObject.Call(memHdc, oldObj) // 恢复原始对象
|
|||
|
|
|||
|
// // 9. 执行位块传输(截图操作)
|
|||
|
// ret, _, _ = bitBlt.Call(
|
|||
|
// memHdc, // 目标DC
|
|||
|
// 0, 0, // 目标坐标
|
|||
|
// uintptr(width), // 宽度
|
|||
|
// uintptr(height), // 高度
|
|||
|
// hdc, // 源DC
|
|||
|
// 0, 0, // 源坐标
|
|||
|
// SRCCOPY, // 操作类型
|
|||
|
// )
|
|||
|
// if ret == 0 {
|
|||
|
// panic("截图操作失败")
|
|||
|
// }
|
|||
|
|
|||
|
// // 10. 准备位图信息头(设置为top-down格式)
|
|||
|
// bmi := BITMAPINFOHEADER{
|
|||
|
// Size: uint32(unsafe.Sizeof(BITMAPINFOHEADER{})),
|
|||
|
// Width: int32(width),
|
|||
|
// Height: -int32(height), // 负数表示top-down格式
|
|||
|
// Planes: 1,
|
|||
|
// BitCount: 32, // 32位色深(BGRA)
|
|||
|
// Compression: 0, // 无压缩
|
|||
|
// }
|
|||
|
|
|||
|
// // 11. 创建像素缓冲区
|
|||
|
// pixels := make([]byte, width*height*4)
|
|||
|
|
|||
|
// // 12. 获取位图数据
|
|||
|
// ret, _, _ = getDIBits.Call(
|
|||
|
// memHdc,
|
|||
|
// hBitmap,
|
|||
|
// 0,
|
|||
|
// uintptr(height),
|
|||
|
// uintptr(unsafe.Pointer(&pixels[0])),
|
|||
|
// uintptr(unsafe.Pointer(&bmi)),
|
|||
|
// 0,
|
|||
|
// )
|
|||
|
// if ret == 0 {
|
|||
|
// panic("获取像素数据失败")
|
|||
|
// }
|
|||
|
|
|||
|
// // 13. 创建图像对象并转换颜色格式
|
|||
|
// img := image.NewRGBA(image.Rect(0, 0, width, height))
|
|||
|
// for y := 0; y < height; y++ {
|
|||
|
// for x := 0; x < width; x++ {
|
|||
|
// idx := (y*width + x) * 4
|
|||
|
// // Windows返回BGRA格式,转换为RGBA
|
|||
|
// img.Set(x, y, color.RGBA{
|
|||
|
// R: pixels[idx+2],
|
|||
|
// G: pixels[idx+1],
|
|||
|
// B: pixels[idx],
|
|||
|
// A: pixels[idx+3],
|
|||
|
// })
|
|||
|
// }
|
|||
|
// }
|
|||
|
|
|||
|
// // 14. 保存为PNG文件
|
|||
|
// file, err := os.Create("window_screenshot.png")
|
|||
|
// if err != nil {
|
|||
|
// panic("创建文件失败: " + err.Error())
|
|||
|
// }
|
|||
|
// defer file.Close()
|
|||
|
|
|||
|
// if err := png.Encode(file, img); err != nil {
|
|||
|
// panic("保存PNG失败: " + err.Error())
|
|||
|
// }
|
|||
|
|
|||
|
//自定义截图
|
|||
|
// img, err := screenshot.Capture(0, 0, width, height)
|
|||
|
// if err != nil {
|
|||
|
// panic(err)
|
|||
|
// }
|
|||
|
// save(img, "自定义.png")
|
|||
|
//获取所有活动屏幕
|
|||
|
// n := screenshot.NumActiveDisplays()
|
|||
|
// if n <= 0 {
|
|||
|
// panic("没有发现活动的显示器")
|
|||
|
// }
|
|||
|
// //全屏截取所有活动屏幕
|
|||
|
// if n > 0 {
|
|||
|
// for i := 0; i < n; i++ {
|
|||
|
// img, err := screenshot.CaptureDisplay(i)
|
|||
|
// if err != nil {
|
|||
|
// panic(err)
|
|||
|
// }
|
|||
|
// fileName := fmt.Sprintf("第%d屏幕截图.png", i)
|
|||
|
// save(img, fileName)
|
|||
|
// }
|
|||
|
// }
|
|||
|
|
|||
|
// 获取第一个屏幕显示范围
|
|||
|
var new image.Rectangle = image.Rect(22, 15, width+5, height+3)
|
|||
|
img, err := screenshot.CaptureRect(new)
|
|||
|
if err != nil {
|
|||
|
panic(err)
|
|||
|
}
|
|||
|
save(img, path_img)
|
|||
|
// //使用Union获取指定屏幕 矩形最小点和最大点
|
|||
|
// var all image.Rectangle = image.Rect(0, 0, 0, 0)
|
|||
|
// bounds1 := screenshot.GetDisplayBounds(0)
|
|||
|
// all = bounds1.Union(all)
|
|||
|
// fmt.Println(all.Min.X, all.Min.Y, all.Dx(), all.Dy())
|
|||
|
|
|||
|
}
|
|||
|
|
|||
|
// save *image.RGBA to filePath with PNG format.
|
|||
|
func save(img *image.RGBA, filePath string) {
|
|||
|
file, err := os.Create(filePath)
|
|||
|
if err != nil {
|
|||
|
panic(err)
|
|||
|
}
|
|||
|
defer file.Close()
|
|||
|
png.Encode(file, img)
|
|||
|
}
|