Files
u-desktop/win32.go
绝尘 f5a473c4b8 新增: 自动升级体系 + WorkerW重建检测 + 知识卡片优化
自动升级:
- 版本变量注入 (-ldflags -X main.version)
- 远程 version.json 检查 + 弹窗提示(非强制右下角/强制居中)
- updater.exe 嵌入主程序, 运行时释放到临时目录
- 下载+SHA256校验+备份+替换+回滚 全流程
- 托盘菜单"检查更新"

稳定性:
- WorkerW 重建自动检测(每10s)并重新嵌入
- 左键托盘点击防抖
- 设置窗口已打开时正确前置

优化:
- 知识卡片prompt改为面向开发者的硬核内容
- 配置目录统一到 ~/.u-desktop/
- 设置窗口WebView2数据目录改为configDir下
2026-05-28 19:06:26 +08:00

157 lines
4.5 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 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")
procGetParent = user32.NewProc("GetParent")
)
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
gwlExStyle = uintptr(0xFFFFFFFEC) // GWL_EXSTYLE = -20
wsPopup = uintptr(0x80000000)
wsVisible = uintptr(0x10000000)
wsChild = uintptr(0x02000000)
wsSizebox = uintptr(0x00040000)
wsMaxbox = uintptr(0x00010000)
)
const (
wsExTransparent = uintptr(0x00000020)
wsExLayered = uintptr(0x00080000)
wsExToolwindow = uintptr(0x00000080)
)
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)
}