dt_automate/tool/jietu.go
2025-02-19 18:30:19 +08:00

260 lines
6.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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