修复: 粘贴图失效与SFTP串台回退等一批问题
粘贴图根修(Ctrl+V直读系统剪贴板,不再依赖不可靠的paste事件)/SFTP串台双刀(拨号期禁回退本机+盘符路径自动回根)/PDF导出目录设置与文案带落盘路径/性能批(连接保存下载防重/md渲染防抖+预热/hljs按需加载/审计锁外置/快捷方式解析超时/webview-watch可停)/前端62类型错清零(含keyPath蛇形字段炸弹)/目录图标字段错位修复
This commit is contained in:
+65
-27
@@ -10,6 +10,7 @@ import (
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/sys/windows"
|
||||
"u-desk/internal/common"
|
||||
)
|
||||
|
||||
// Chrome_WidgetWin 系列类名是 WebView2 的宿主窗口层级
|
||||
@@ -28,6 +29,12 @@ var (
|
||||
// 修复去重:同一时刻只允许一个校验在跑(回调写共享切片要求互斥)
|
||||
var webviewFixRunning atomic.Bool
|
||||
|
||||
// WM_SIZE 去重:脏标记 + 单 worker(每条 WM_SIZE 只置位,不再批量 spawn goroutine)
|
||||
var (
|
||||
webviewSizeDirty atomic.Bool
|
||||
webviewSizeWorkerActive atomic.Bool
|
||||
)
|
||||
|
||||
// 当前校验的目标主窗口(枚举回调过滤直属子窗口用)
|
||||
var webviewTargetHwnd uintptr
|
||||
|
||||
@@ -77,32 +84,60 @@ func checkAndFixFill(hwnd uintptr) {
|
||||
}
|
||||
}
|
||||
|
||||
// runGuarded 统一 panic 兜底:windowsgui 下 goroutine panic 会静默杀进程
|
||||
// runGuarded 同步调用点的 panic 兜底:panic 取证落盘后不外抛,
|
||||
// 让调用方(如周期循环)能继续跑下一轮
|
||||
func runGuarded(fn func()) {
|
||||
defer func() { recover() }()
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
common.AppendPanicLog("webview-fix", r)
|
||||
}
|
||||
}()
|
||||
fn()
|
||||
}
|
||||
|
||||
// fixWebviewFill WM_SIZE 路径:等 resize 序列结束、wails 内部去抖落定后校验一次
|
||||
// fixWebviewFill WM_SIZE 路径:等 resize 序列结束、wails 内部去抖落定后校验一次。
|
||||
// WM_SIZE 只置脏标记;活跃期间的后续 WM_SIZE 合并进去(worker 循环消费),
|
||||
// 拖拽缩放的风暴从"每条消息一个 goroutine"降为全程单 worker
|
||||
func fixWebviewFill(hwnd uintptr) {
|
||||
if !webviewFixRunning.CompareAndSwap(false, true) {
|
||||
webviewTargetHwnd = hwnd
|
||||
webviewSizeDirty.Store(true)
|
||||
if !webviewSizeWorkerActive.CompareAndSwap(false, true) {
|
||||
return
|
||||
}
|
||||
go func() {
|
||||
defer webviewFixRunning.Store(false)
|
||||
runGuarded(func() {
|
||||
common.Go("webview-fix", func() {
|
||||
defer webviewSizeWorkerActive.Store(false)
|
||||
for webviewSizeDirty.CompareAndSwap(true, false) {
|
||||
time.Sleep(150 * time.Millisecond)
|
||||
checkAndFixFill(hwnd)
|
||||
})
|
||||
}()
|
||||
if webviewFixRunning.CompareAndSwap(false, true) {
|
||||
runGuarded(func() { checkAndFixFill(webviewTargetHwnd) })
|
||||
webviewFixRunning.Store(false)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// stopWebviewWatch 周期兜底循环的停止信号;StartWebviewFillWatch 重入时先关旧的(重启语义)
|
||||
var stopWebviewWatch chan struct{}
|
||||
|
||||
// StartWebviewFillWatch 周期兜底:覆盖"无新 WM_SIZE 的迟到重放"场景
|
||||
// (主线程忙时排队的过期重绘在窗口静止时才执行,此时不会产生任何 WM_SIZE)
|
||||
func StartWebviewFillWatch() {
|
||||
go runGuarded(func() {
|
||||
if stopWebviewWatch != nil {
|
||||
select {
|
||||
case <-stopWebviewWatch: // 旧循环已自行退出
|
||||
default:
|
||||
close(stopWebviewWatch)
|
||||
}
|
||||
}
|
||||
stop := make(chan struct{})
|
||||
stopWebviewWatch = stop
|
||||
common.Go("webview-watch", func() {
|
||||
for {
|
||||
time.Sleep(3 * time.Second)
|
||||
select {
|
||||
case <-stop:
|
||||
return
|
||||
case <-time.After(3 * time.Second):
|
||||
}
|
||||
if webviewFixRunning.CompareAndSwap(false, true) {
|
||||
runGuarded(func() {
|
||||
checkAndFixFill(findMainWindowHwnd())
|
||||
@@ -114,20 +149,23 @@ func StartWebviewFillWatch() {
|
||||
}
|
||||
|
||||
// findMainWindowHwnd 找本进程的可见主窗口
|
||||
// 回调必须进程级复用(同 enumChildWebviewHosts):runtime 的 Windows callback
|
||||
// 硬上限 2000 个且按闭包指针去重,每 3 秒新建一个约 100 分钟即耗尽,
|
||||
// 触发 runtime throw("too many callback functions") 直接静默退出(无法 recover)
|
||||
var mainWindowHwndResult uintptr
|
||||
|
||||
var enumMainWindowHwnd = syscall.NewCallback(func(h uintptr, _ uintptr) uintptr {
|
||||
var winPid uint32
|
||||
windows.GetWindowThreadProcessId(windows.HWND(h), &winPid)
|
||||
if winPid == windows.GetCurrentProcessId() && windows.IsWindowVisible(windows.HWND(h)) {
|
||||
mainWindowHwndResult = h
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
})
|
||||
|
||||
func findMainWindowHwnd() uintptr {
|
||||
var found uintptr
|
||||
pid := windows.GetCurrentProcessId()
|
||||
cb := syscall.NewCallback(func(h uintptr, _ uintptr) uintptr {
|
||||
var winPid uint32
|
||||
windows.GetWindowThreadProcessId(windows.HWND(h), &winPid)
|
||||
if winPid == pid && windows.IsWindowVisible(windows.HWND(h)) {
|
||||
found = h
|
||||
return 0
|
||||
}
|
||||
return 1
|
||||
})
|
||||
runGuarded(func() {
|
||||
windows.EnumWindows(cb, nil)
|
||||
})
|
||||
return found
|
||||
mainWindowHwndResult = 0
|
||||
windows.EnumWindows(enumMainWindowHwnd, nil)
|
||||
return mainWindowHwndResult
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user