149 lines
4.3 KiB
Go
149 lines
4.3 KiB
Go
package main
|
||
|
||
import (
|
||
"log"
|
||
"unsafe"
|
||
|
||
"github.com/jchv/go-webview2"
|
||
"golang.org/x/sys/windows"
|
||
)
|
||
|
||
var (
|
||
user32 = windows.NewLazySystemDLL("user32.dll")
|
||
procFindWindowW = user32.NewProc("FindWindowW")
|
||
procFindWindowExW = user32.NewProc("FindWindowExW")
|
||
procSendMessageTimeoutW = user32.NewProc("SendMessageTimeoutW")
|
||
procSetParent = user32.NewProc("SetParent")
|
||
procGetForegroundWindow = user32.NewProc("GetForegroundWindow")
|
||
procGetWindowRect = user32.NewProc("GetWindowRect")
|
||
procGetSystemMetrics = user32.NewProc("GetSystemMetrics")
|
||
procMoveWindow = user32.NewProc("MoveWindow")
|
||
procShowWindow = user32.NewProc("ShowWindow")
|
||
procSetProcessDPIAware = user32.NewProc("SetProcessDPIAware")
|
||
procSetWindowLongPtrW = user32.NewProc("SetWindowLongPtrW")
|
||
procGetWindowLongPtrW = user32.NewProc("GetWindowLongPtrW")
|
||
procGetDpiForWindow = user32.NewProc("GetDpiForWindow")
|
||
procGetClientRect = user32.NewProc("GetClientRect")
|
||
procMessageBoxW = user32.NewProc("MessageBoxW")
|
||
procGetMessageW = user32.NewProc("GetMessageW")
|
||
procPostMessageW = user32.NewProc("PostMessageW")
|
||
procTranslateMessage = user32.NewProc("TranslateMessage")
|
||
procDispatchMessageW = user32.NewProc("DispatchMessageW")
|
||
)
|
||
|
||
var wv webview2.WebView
|
||
var wvHwnd uintptr
|
||
var jsQueue = make(chan string, 64)
|
||
var paused int32
|
||
|
||
const (
|
||
wmEvalJS = 0x0401
|
||
wmSetHtml = 0x0402
|
||
)
|
||
|
||
const (
|
||
gwlStyle = uintptr(0xFFFFFFF0) // GWL_STYLE = -16
|
||
wsPopup = uintptr(0x80000000)
|
||
wsVisible = uintptr(0x10000000)
|
||
wsChild = uintptr(0x02000000)
|
||
wsSizebox = uintptr(0x00040000)
|
||
wsMaxbox = uintptr(0x00010000)
|
||
)
|
||
|
||
var htmlQueue = make(chan string, 1)
|
||
|
||
var (
|
||
classProgman = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr("Progman")))
|
||
classShellDefView = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr("SHELLDLL_DefView")))
|
||
classWorkerW = uintptr(unsafe.Pointer(windows.StringToUTF16Ptr("WorkerW")))
|
||
)
|
||
|
||
func evalJS(js string) {
|
||
select {
|
||
case jsQueue <- js:
|
||
log.Println("evalJS queued:", js[:min(len(js), 60)])
|
||
default:
|
||
log.Println("jsQueue full, dropping eval")
|
||
}
|
||
if wvHwnd != 0 {
|
||
procPostMessageW.Call(wvHwnd, wmEvalJS, 0, 0)
|
||
}
|
||
}
|
||
|
||
func findWorkerW() uintptr {
|
||
progman, _, _ := procFindWindowW.Call(
|
||
classProgman, 0)
|
||
if progman == 0 {
|
||
log.Println("findWorkerW: Progman not found")
|
||
return 0
|
||
}
|
||
// 发送 0x052C 触发 Progman 创建 WorkerW
|
||
var result uintptr
|
||
procSendMessageTimeoutW.Call(progman, 0x052C, 0, 0, 0x0000, 1000, uintptr(unsafe.Pointer(&result)))
|
||
|
||
// 方法1: Progman 下找 SHELLDLL_DefView,再找其后的 WorkerW
|
||
shellDefView, _, _ := procFindWindowExW.Call(progman, 0, classShellDefView, 0)
|
||
if shellDefView != 0 {
|
||
ww, _, _ := procFindWindowExW.Call(progman, shellDefView, classWorkerW, 0)
|
||
if ww != 0 {
|
||
log.Printf("findWorkerW: 方法1成功, WorkerW=0x%x", ww)
|
||
return ww
|
||
}
|
||
}
|
||
|
||
// 方法2: 遍历顶层 WorkerW,找到含 SHELLDLL_DefView 的那个,取它后面的 WorkerW
|
||
var prev uintptr
|
||
for {
|
||
ww, _, _ := procFindWindowExW.Call(0, prev, classWorkerW, 0)
|
||
if ww == 0 {
|
||
break
|
||
}
|
||
child, _, _ := procFindWindowExW.Call(ww, 0, classShellDefView, 0)
|
||
if child != 0 {
|
||
// 这个 WorkerW 包含 SHELLDLL_DefView,找它后面的 WorkerW
|
||
next, _, _ := procFindWindowExW.Call(0, ww, classWorkerW, 0)
|
||
if next != 0 {
|
||
log.Printf("findWorkerW: 方法2成功, WorkerW=0x%x (after 0x%x)", next, ww)
|
||
return next
|
||
}
|
||
}
|
||
prev = ww
|
||
}
|
||
|
||
// 方法3: 遍历顶层 WorkerW,找任意不含 SHELLDLL_DefView 的可见 WorkerW
|
||
prev = 0
|
||
for {
|
||
ww, _, _ := procFindWindowExW.Call(0, prev, classWorkerW, 0)
|
||
if ww == 0 {
|
||
break
|
||
}
|
||
child, _, _ := procFindWindowExW.Call(ww, 0, classShellDefView, 0)
|
||
if child == 0 {
|
||
log.Printf("findWorkerW: 方法3成功, WorkerW=0x%x", ww)
|
||
return ww
|
||
}
|
||
prev = ww
|
||
}
|
||
|
||
// 方法4: 兜底,Progman 下的第一个 WorkerW
|
||
ww, _, _ := procFindWindowExW.Call(progman, 0, classWorkerW, 0)
|
||
if ww != 0 {
|
||
log.Printf("findWorkerW: 方法4(兜底), WorkerW=0x%x", ww)
|
||
}
|
||
return ww
|
||
}
|
||
|
||
func getDPI(hwnd uintptr) int {
|
||
dpi, _, _ := procGetDpiForWindow.Call(hwnd)
|
||
if dpi == 0 {
|
||
return 96
|
||
}
|
||
return int(dpi)
|
||
}
|
||
|
||
func getScreenSize() (int32, int32) {
|
||
w, _, _ := procGetSystemMetrics.Call(0)
|
||
h, _, _ := procGetSystemMetrics.Call(1)
|
||
return int32(w), int32(h)
|
||
}
|