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