吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 134|回复: 1
上一主题 下一主题
收起左侧

[学习记录] rust 版实现 - Windows右键【复制多行文件完整路径】

[复制链接]
跳转到指定楼层
楼主
pyjiujiu 发表于 2026-4-12 22:23 回帖奖励

原帖是 www.52pojie.cn/thread-2101392-1-1.html
原作者已经介绍了 详细的 windows 注册表的设置,这不再涉及这部分
简单套用,替换 rust 版即可  (CopyMultiPath.exe  -换成-> copy_paths_client.exe)(#copy_paths_server.exe 也要放在同目录)

# 介绍

编译后,是两个 copy_paths_client.execopy_paths_server.exe
(码有不对的地方,欢迎指出 交流)

用的是 TCP 的协议
winodws 选中多个文件 --> 每个文件启动一个客户端进程(-client.exe + 文件路径参数)

每个客户端 --> 尝试连接 服务端 --> 如果失败,会启动同目录的 -server.exe , ,继续发送

服务端 对于每个收到的连接,都会转到 一个子线程 去处理数据 --> 这个子线程 又会通知 超时线程

超时线程 会计算超时,时间到 就存入 剪贴板

(注:服务器首次启动 会耗费点时间,因为是低频操作,不然可以 直接开机自启)
(注:一次选中很多文件,需要 修改 MultiSelectModel 为 Player)(个人已经测试 50+文件同时选中,但超过100个 成功不了,具体原因还未知)
(注:端口是 5038 ,若有冲突 可以自行更改)





下面是代码


Cargo.toml (区分部分)

[[bin]]
name = "copy_paths_client"
path = "src/client.rs"

[[bin]]
name = "copy_paths_server"
path = "src/server.rs"

[dependencies]
clipboard = "0.5.0"

这是客户端(client.rs)

use std::io::Write;
use std::net::TcpStream;
use std::process::Command;
use std::thread;
use std::time::Duration;
use std::env;

const PORT: u16 = 5038;
const SERVER_EXE: &str = "copy_paths_server.exe";

fn start_server() {
    let current_exe = env::current_exe().expect("获取当前 exe 路径失败");
    let server_path = current_exe.with_file_name(SERVER_EXE);
    let _ = Command::new(server_path).spawn();
}

fn send_path(path: &str) -> Result<(), String> {
    let mut retries = 0;
    loop {
        match TcpStream::connect(("127.0.0.1", PORT)) {
            Ok(mut stream) => {
                // 发送路径,换行作为结束符
                let line = format!("{}\n", path);
                stream.write_all(line.as_bytes()).map_err(|e| e.to_string())?;
                stream.flush().map_err(|e| e.to_string())?;
                return Ok(());
            }
            Err(e) if e.kind() == std::io::ErrorKind::ConnectionRefused => {
                // 服务端未运行,启动它
                start_server();
                thread::sleep(Duration::from_millis(50));
                retries += 1;
                if retries > 20 {
                    return Err("无法连接服务端".to_string());
                }
                continue;
            }
            Err(e) => return Err(format!("连接失败: {}", e)),
        }
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    if args.len() < 2 {
        return;
    }
    let path = &args[1];
    if let Err(e) = send_path(path) {
        eprintln!("发送失败: {}", e);
    }
}

这是服务端(server.rs)

#![windows_subsystem = "windows"]

use clipboard::{ClipboardContext, ClipboardProvider};
use std::io::{BufRead, BufReader};
use std::net::{TcpListener, TcpStream};
use std::sync::{Arc, Condvar, Mutex};
use std::thread;
use std::time::{Duration, Instant};

const PORT: u16 = 5038;
const TIMEOUT_MS: u64 = 400; // 无新消息等待 多文件选择 能稳定执行

// 两者放在同一目录

// 共享状态
struct SharedState {
    paths: Vec<String>,
    deadline: Instant,
    idle: bool,  // 可用于调试,但不用于唤醒判断
}

// 类型别名便于传递
type StateArc = Arc<(Mutex<SharedState>, Condvar)>;

fn copy_to_clipboard(paths: &[String]) {
    if paths.is_empty() {
        return;
    }
    let content = paths.join("\r\n");
    if let Ok(mut ctx) = ClipboardContext::new() {
        let _ = ctx.set_contents(content);
        println!("已复制 {} 个路径", paths.len());
    }
}

fn handle_client(stream:TcpStream, state: StateArc){
    let reader = BufReader::new(stream);
    let (lock, cvar) = &*state;

    for line in reader.lines() {
        match line {
            Ok(path) if !path.is_empty() => {
                let mut guard =lock.lock().unwrap();
                guard.paths.push(path);
                guard.deadline = Instant::now() + Duration::from_millis(TIMEOUT_MS);

                guard.idle = false;
                cvar.notify_one(); // 无条件调用,,
            }
         _ => break,
       }
    }

}

fn main() {
    let listener = match TcpListener::bind(("127.0.0.1", PORT)) {
        Ok(l) => l,
        Err(e) => {
            // 端口已被占用,说明已有服务端在运行,本进程直接退出
            eprintln!("端口 {} 已被占用,服务端已存在: {}", PORT, e);
            return;
        }
    };
    println!("服务端启动,监听端口 {}", PORT);

    let state = Arc::new((Mutex::new(SharedState {
            paths: Vec::new(),
            deadline: Instant::now() + Duration::from_millis(TIMEOUT_MS),
            idle:true,
    }), Condvar::new()));

    // 超时检查线程
    thread::spawn({
        let state = state.clone();
        move || {
        let (lock, cvar) = &*state;
        loop {
            let mut guard = lock.lock().unwrap();

            //计算需要等待的时间
            let now: Instant  = Instant::now();

            // 安全计算等待时间(避免 deadline < now 时 panic)
            if let Some(wait_time) = guard.deadline.checked_duration_since(now) {
                let result = cvar.wait_timeout(guard, wait_time).unwrap();
                guard = result.0;

                //若因超时 返回 (未收到通知)
                if result.1.timed_out() {
                    if !guard.paths.is_empty(){
                        copy_to_clipboard(&guard.paths);
                        guard.paths.clear();
                    }
                    guard.idle = true;
                    guard.deadline = Instant::now() + Duration::from_secs(1_000_000);
                }

            } else {
                // dealine 已过 (可能刚被唤醒 且时间已到)
                if !guard.paths.is_empty(){
                        copy_to_clipboard(&guard.paths);
                        guard.paths.clear();
                }
                guard.idle = true;
                guard.deadline = Instant::now() + Duration::from_secs(1_000_000);
            }
        }
    }});

    // 主循环接受客户端连接
    for stream in listener.incoming() {
        match stream {
            Ok(stream) => {
                let state = state.clone();
                thread::spawn(move || handle_client(stream, state));
            }
            Err(e) => eprintln!("接受连接失败: {}", e),
        }
    }
}



发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

沙发
gm01 发表于 2026-4-13 00:55

感谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - 52pojie.cn ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2026-4-13 03:45

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表