|
|
@@ -0,0 +1,333 @@
|
|
|
+package main
|
|
|
+
|
|
|
+import (
|
|
|
+ "encoding/json"
|
|
|
+ "fmt"
|
|
|
+ "log"
|
|
|
+ "net/http"
|
|
|
+ "os/exec"
|
|
|
+ "syscall"
|
|
|
+ "time"
|
|
|
+ "unsafe"
|
|
|
+)
|
|
|
+
|
|
|
+type RequestData struct {
|
|
|
+ Type string `json:"type"`
|
|
|
+ Path string `json:"path"`
|
|
|
+ FileName string `json:"fileName"`
|
|
|
+}
|
|
|
+
|
|
|
+// 常量
|
|
|
+const (
|
|
|
+ VK_CONTROL = 0x11
|
|
|
+ VK_V = 0x56
|
|
|
+)
|
|
|
+
|
|
|
+var (
|
|
|
+ user32 = syscall.NewLazyDLL("user32.dll")
|
|
|
+
|
|
|
+ findWindowW = user32.NewProc("FindWindowW")
|
|
|
+ moveWindow = user32.NewProc("MoveWindow")
|
|
|
+ setForegroundWindow = user32.NewProc("SetForegroundWindow")
|
|
|
+ getWindowRect = user32.NewProc("GetWindowRect")
|
|
|
+ setCursorPos = user32.NewProc("SetCursorPos")
|
|
|
+ mouse_event = user32.NewProc("mouse_event")
|
|
|
+ keybd_event = user32.NewProc("keybd_event")
|
|
|
+)
|
|
|
+
|
|
|
+type RECT struct {
|
|
|
+ Left int32
|
|
|
+ Top int32
|
|
|
+ Right int32
|
|
|
+ Bottom int32
|
|
|
+}
|
|
|
+
|
|
|
+// 回车键的虚拟键码
|
|
|
+const VK_RETURN = 0x0D
|
|
|
+const KEYEVENTF_KEYUP = 0x0002
|
|
|
+
|
|
|
+// 简化函数
|
|
|
+func FindWindow(title string) (uintptr, error) {
|
|
|
+ titlePtr, err := syscall.UTF16PtrFromString(title)
|
|
|
+ if err != nil {
|
|
|
+ return 0, err
|
|
|
+ }
|
|
|
+
|
|
|
+ hwnd, _, err := findWindowW.Call(0, uintptr(unsafe.Pointer(titlePtr)))
|
|
|
+ if hwnd == 0 {
|
|
|
+ return 0, fmt.Errorf("窗口未找到: %v", err)
|
|
|
+ }
|
|
|
+ return hwnd, nil
|
|
|
+}
|
|
|
+
|
|
|
+func MoveWindow(hwnd uintptr, x, y, w, h int32) error {
|
|
|
+ ret, _, err := moveWindow.Call(hwnd, uintptr(x), uintptr(y), uintptr(w), uintptr(h), 1)
|
|
|
+ if ret == 0 {
|
|
|
+ return fmt.Errorf("移动窗口失败: %v", err)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func SetForegroundWindow(hwnd uintptr) error {
|
|
|
+ ret, _, err := setForegroundWindow.Call(hwnd)
|
|
|
+ if ret == 0 {
|
|
|
+ return fmt.Errorf("设置前台窗口失败: %v", err)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func GetWindowRect(hwnd uintptr) (RECT, error) {
|
|
|
+ var rect RECT
|
|
|
+ ret, _, err := getWindowRect.Call(hwnd, uintptr(unsafe.Pointer(&rect)))
|
|
|
+ if ret == 0 {
|
|
|
+ return rect, fmt.Errorf("获取窗口矩形失败: %v", err)
|
|
|
+ }
|
|
|
+ return rect, nil
|
|
|
+}
|
|
|
+
|
|
|
+func SetCursorPos(x, y int32) error {
|
|
|
+ ret, _, err := setCursorPos.Call(uintptr(x), uintptr(y))
|
|
|
+ if ret == 0 {
|
|
|
+ return fmt.Errorf("设置光标位置失败: %v", err)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func MouseClick(x, y int32) error {
|
|
|
+ // 移动鼠标
|
|
|
+ if err := SetCursorPos(x, y); err != nil {
|
|
|
+ return err
|
|
|
+ }
|
|
|
+
|
|
|
+ time.Sleep(100 * time.Millisecond)
|
|
|
+
|
|
|
+ // 左键按下 (0x0002 = MOUSEEVENTF_LEFTDOWN)
|
|
|
+ mouse_event.Call(2, 0, 0, 0, 0)
|
|
|
+ time.Sleep(50 * time.Millisecond)
|
|
|
+
|
|
|
+ // 左键释放 (0x0004 = MOUSEEVENTF_LEFTUP)
|
|
|
+ mouse_event.Call(4, 0, 0, 0, 0)
|
|
|
+
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func Paste() {
|
|
|
+ // Ctrl 按下
|
|
|
+ keybd_event.Call(uintptr(VK_CONTROL), 0, 0, 0)
|
|
|
+ time.Sleep(50 * time.Millisecond)
|
|
|
+
|
|
|
+ // V 按下
|
|
|
+ keybd_event.Call(uintptr(VK_V), 0, 0, 0)
|
|
|
+ time.Sleep(50 * time.Millisecond)
|
|
|
+
|
|
|
+ // V 释放 (0x0002 = KEYEVENTF_KEYUP)
|
|
|
+ keybd_event.Call(uintptr(VK_V), 0, 2, 0)
|
|
|
+ time.Sleep(50 * time.Millisecond)
|
|
|
+
|
|
|
+ // Ctrl 释放
|
|
|
+ keybd_event.Call(uintptr(VK_CONTROL), 0, 2, 0)
|
|
|
+}
|
|
|
+
|
|
|
+func PressEnter() {
|
|
|
+ // 按下回车键
|
|
|
+ keybd_event.Call(
|
|
|
+ uintptr(VK_RETURN), // bVk
|
|
|
+ 0, // bScan
|
|
|
+ 0, // dwFlags (按下)
|
|
|
+ 0, // dwExtraInfo
|
|
|
+ )
|
|
|
+
|
|
|
+ time.Sleep(5 * time.Millisecond)
|
|
|
+
|
|
|
+ // 释放回车键
|
|
|
+ keybd_event.Call(
|
|
|
+ uintptr(VK_RETURN), // bVk
|
|
|
+ 0, // bScan
|
|
|
+ uintptr(KEYEVENTF_KEYUP), // dwFlags (释放)
|
|
|
+ 0, // dwExtraInfo
|
|
|
+ )
|
|
|
+}
|
|
|
+
|
|
|
+// 使用系统命令设置剪贴板(更可靠)
|
|
|
+func SetClipboard(text string) error {
|
|
|
+ // 方法1:使用 clip 命令
|
|
|
+ cmd := exec.Command("cmd", "/c", fmt.Sprintf("echo %s | clip", text))
|
|
|
+ if err := cmd.Run(); err == nil {
|
|
|
+ return nil
|
|
|
+ }
|
|
|
+
|
|
|
+ // 方法2:使用 PowerShell
|
|
|
+ cmd2 := exec.Command("powershell", "-Command", fmt.Sprintf("Set-Clipboard -Value '%s'", text))
|
|
|
+ return cmd2.Run()
|
|
|
+}
|
|
|
+
|
|
|
+func findAndOperateWindow(title string, filePath string, fileName string) {
|
|
|
+ fmt.Printf("开始查找窗口: %s\n", title)
|
|
|
+
|
|
|
+ var hwnd uintptr
|
|
|
+ var err error
|
|
|
+ found := false
|
|
|
+
|
|
|
+ // 10秒内每秒尝试查找窗口
|
|
|
+ for i := 0; i < 10; i++ {
|
|
|
+ fmt.Printf("尝试第 %d 次查找...\n", i+1)
|
|
|
+
|
|
|
+ hwnd, err = FindWindow(title)
|
|
|
+ if err == nil && hwnd != 0 {
|
|
|
+ found = true
|
|
|
+ fmt.Printf("找到窗口: %s (句柄: 0x%X)\n", title, hwnd)
|
|
|
+ break
|
|
|
+ }
|
|
|
+
|
|
|
+ time.Sleep(1 * time.Second)
|
|
|
+ }
|
|
|
+
|
|
|
+ if !found {
|
|
|
+ log.Printf("在10秒内未找到窗口: %s", title)
|
|
|
+ log.Println("请确保:")
|
|
|
+ log.Println("1. 窗口已经打开")
|
|
|
+ log.Println("2. 窗口标题完全匹配: " + title)
|
|
|
+ log.Println("3. 可能需要以管理员身份运行此程序")
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 激活窗口
|
|
|
+ if err := SetForegroundWindow(hwnd); err != nil {
|
|
|
+ log.Printf("激活窗口失败: %v", err)
|
|
|
+ }
|
|
|
+ time.Sleep(500 * time.Millisecond)
|
|
|
+
|
|
|
+ // 调整窗口大小到800x600
|
|
|
+ fmt.Println("正在修改窗口大小为800x600...")
|
|
|
+ if err := MoveWindow(hwnd, 100, 100, 800, 600); err != nil {
|
|
|
+ log.Printf("调整窗口大小失败: %v", err)
|
|
|
+ } else {
|
|
|
+ fmt.Println("窗口大小修改成功")
|
|
|
+ }
|
|
|
+
|
|
|
+ // 获取窗口位置
|
|
|
+ rect, err := GetWindowRect(hwnd)
|
|
|
+ if err != nil {
|
|
|
+ log.Printf("获取窗口位置失败: %v", err)
|
|
|
+ } else {
|
|
|
+ fmt.Printf("窗口位置: X=%d, Y=%d\n", rect.Left, rect.Top)
|
|
|
+ }
|
|
|
+
|
|
|
+ // 等待窗口调整完成
|
|
|
+ time.Sleep(500 * time.Millisecond)
|
|
|
+
|
|
|
+ MouseClick(rect.Left+450, rect.Top+60)
|
|
|
+
|
|
|
+ SetClipboard(filePath)
|
|
|
+
|
|
|
+ time.Sleep(500 * time.Millisecond)
|
|
|
+
|
|
|
+ // 粘贴剪贴板内容
|
|
|
+ fmt.Println("正在粘贴剪贴板内容...")
|
|
|
+ Paste()
|
|
|
+ fmt.Println("粘贴完成")
|
|
|
+ time.Sleep(500 * time.Millisecond)
|
|
|
+ fmt.Println("按回车键")
|
|
|
+ PressEnter()
|
|
|
+ fmt.Println("操作完成")
|
|
|
+ MouseClick(rect.Left+450, rect.Top+530)
|
|
|
+ time.Sleep(2 * time.Second)
|
|
|
+ SetClipboard(fileName)
|
|
|
+ time.Sleep(500 * time.Millisecond)
|
|
|
+ Paste()
|
|
|
+ time.Sleep(500 * time.Millisecond)
|
|
|
+ PressEnter()
|
|
|
+}
|
|
|
+
|
|
|
+func handlePostRequest(w http.ResponseWriter, r *http.Request) {
|
|
|
+ // 只处理POST请求
|
|
|
+ if r.Method != http.MethodPost {
|
|
|
+ http.Error(w, "只支持POST请求", http.StatusMethodNotAllowed)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 解析JSON数据
|
|
|
+ var data RequestData
|
|
|
+ decoder := json.NewDecoder(r.Body)
|
|
|
+ if err := decoder.Decode(&data); err != nil {
|
|
|
+ http.Error(w, "JSON解析错误: "+err.Error(), http.StatusBadRequest)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ defer r.Body.Close()
|
|
|
+
|
|
|
+ // 输出接收到的数据
|
|
|
+ fmt.Println("=== 接收到POST请求数据 ===")
|
|
|
+ fmt.Printf("类型 (type): %s\n", data.Type)
|
|
|
+ fmt.Printf("路径 (path): %s\n", data.Path)
|
|
|
+ fmt.Printf("文件名 (fileName): %s\n", data.FileName)
|
|
|
+ fmt.Println("==========================")
|
|
|
+
|
|
|
+ // 根据type执行不同操作
|
|
|
+ if data.Type == "openFile" {
|
|
|
+ // 在新goroutine中执行窗口操作,避免阻塞HTTP响应
|
|
|
+ go func() {
|
|
|
+ findAndOperateWindow("打开", data.Path, data.FileName)
|
|
|
+ }()
|
|
|
+
|
|
|
+ // 返回响应
|
|
|
+ w.Header().Set("Content-Type", "application/json")
|
|
|
+ response := map[string]interface{}{
|
|
|
+ "status": "processing",
|
|
|
+ "message": "正在执行窗口操作",
|
|
|
+ "data": data,
|
|
|
+ }
|
|
|
+ json.NewEncoder(w).Encode(response)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ // 返回成功响应
|
|
|
+ w.Header().Set("Content-Type", "application/json")
|
|
|
+ response := map[string]interface{}{
|
|
|
+ "status": "success",
|
|
|
+ "message": "数据接收成功",
|
|
|
+ "data": data,
|
|
|
+ }
|
|
|
+ json.NewEncoder(w).Encode(response)
|
|
|
+}
|
|
|
+
|
|
|
+func main() {
|
|
|
+ fmt.Println("窗口操作服务启动...")
|
|
|
+ fmt.Println("注意:某些窗口操作可能需要管理员权限")
|
|
|
+ fmt.Println("如果需要,请以管理员身份运行此程序")
|
|
|
+ fmt.Println()
|
|
|
+
|
|
|
+ // 设置处理POST请求的路由
|
|
|
+ http.HandleFunc("/", handlePostRequest)
|
|
|
+
|
|
|
+ // 设置服务器监听8866端口
|
|
|
+ port := ":8866"
|
|
|
+ fmt.Printf("服务器正在监听端口 %s ...\n", port)
|
|
|
+ fmt.Println("等待POST请求...")
|
|
|
+ fmt.Println()
|
|
|
+
|
|
|
+ // 启动HTTP服务器
|
|
|
+ if err := http.ListenAndServe(port, nil); err != nil {
|
|
|
+ log.Fatal("服务器启动失败:", err)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// 前端JS请求示例
|
|
|
+// const myHeaders = new Headers();
|
|
|
+// myHeaders.append("Content-Type", "application/json");
|
|
|
+
|
|
|
+// const raw = JSON.stringify({
|
|
|
+// "type": "openFile",
|
|
|
+// "path": "C:\\Users\\mail\\Downloads",
|
|
|
+// "fileName": "128.png"
|
|
|
+// });
|
|
|
+
|
|
|
+// const requestOptions = {
|
|
|
+// method: "POST",
|
|
|
+// headers: myHeaders,
|
|
|
+// body: raw,
|
|
|
+// };
|
|
|
+
|
|
|
+// fetch("http://127.0.0.1:8866/", requestOptions)
|
|
|
+// .then((response) => response.text())
|
|
|
+// .then((result) => console.log(result))
|
|
|
+// .catch((error) => console.error(error));
|