Files
u-desk/app_clipboard.go
T
lxy b9c0786c02 新增: 资源管理器文件粘贴到文件列表
- 后端 ReadClipboardFiles 读系统剪贴板 CF_HDROP 拿路径列表
- Ctrl+V 优先粘贴系统剪贴板文件,复用拖入导入链路(SFTP 上传/本机复制/目录递归)
- 补齐设计文档记录的历史缺口(Wails 限制未做)
2026-09-29 18:47:09 +08:00

72 lines
2.0 KiB
Go

//go:build windows
package main
import (
"runtime"
"syscall"
"time"
"unsafe"
"golang.org/x/sys/windows"
)
var (
procIsClipboardFormatAvailable = windows.NewLazySystemDLL("user32.dll").NewProc("IsClipboardFormatAvailable")
procOpenClipboard = windows.NewLazySystemDLL("user32.dll").NewProc("OpenClipboard")
procCloseClipboard = windows.NewLazySystemDLL("user32.dll").NewProc("CloseClipboard")
procGetClipboardData = windows.NewLazySystemDLL("user32.dll").NewProc("GetClipboardData")
procDragQueryFileW = windows.NewLazySystemDLL("shell32.dll").NewProc("DragQueryFileW")
)
// cfHDrop 剪贴板文件列表格式(资源管理器复制文件/文件夹),Win32 稳定 ABI 值
const cfHDrop = 15
// ReadClipboardFiles 读取系统剪贴板中的文件/文件夹路径列表。
// 剪贴板为文本/截图等非文件内容时返回空列表,前端据此回落图片粘贴等原有链路。
func (a *App) ReadClipboardFiles() ([]string, error) {
// OpenClipboard 与 CloseClipboard 必须同线程,否则剪贴板死锁
runtime.LockOSThread()
defer runtime.UnlockOSThread()
if r, _, _ := procIsClipboardFormatAvailable.Call(cfHDrop); r == 0 {
return nil, nil
}
// 剪贴板是全局互斥资源,短重试避开其他进程的瞬时占用
opened := false
for i := 0; i < 20; i++ {
if r, _, _ := procOpenClipboard.Call(0); r != 0 {
opened = true
break
}
time.Sleep(10 * time.Millisecond)
}
if !opened {
return nil, syscall.EBUSY
}
defer procCloseClipboard.Call()
h, _, _ := procGetClipboardData.Call(cfHDrop)
if h == 0 {
return nil, nil
}
count, _, _ := procDragQueryFileW.Call(h, 0xFFFFFFFF, 0, 0)
if count == 0 {
return nil, nil
}
paths := make([]string, 0, count)
for i := uintptr(0); i < count; i++ {
n, _, _ := procDragQueryFileW.Call(h, i, 0, 0)
if n == 0 {
continue
}
buf := make([]uint16, n+1)
procDragQueryFileW.Call(h, i, uintptr(unsafe.Pointer(&buf[0])), n+1)
paths = append(paths, syscall.UTF16ToString(buf))
}
return paths, nil
}