You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用Rust实现Windows剪贴板监视器?现有代码无输出无报错

Rust Windows剪贴板管理器无输出问题解决

尝试使用winapi-rs crate开发Windows剪贴板管理器,参考C++和Python实现编写代码后,运行程序既无输出也无报错,使用Rust v1.66.0-stable版本。


原代码

src/clipboard.rs

use std::mem::zeroed;
use std::ptr;

use winapi::shared::windef::HWND;
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::winuser::{
    AddClipboardFormatListener,
    RemoveClipboardFormatListener,
    CreateWindowExW,
    RegisterClassW,
    WNDCLASSW,
    WM_CREATE,
    WM_DESTROY,
    WM_CLIPBOARDUPDATE,
    HWND_MESSAGE,
    DefWindowProcW
};
use winapi::shared::minwindef::{LRESULT, UINT, WPARAM, LPARAM, BOOL};

static mut ADDED_LISTENER: BOOL = 0;

unsafe extern "system" fn callback_proc(h_wnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
    println!("called");
    (match msg {
        WM_CREATE => {
            ADDED_LISTENER = AddClipboardFormatListener(h_wnd);
            if ADDED_LISTENER == 1 { 0 } else { -1 }
        }
        WM_DESTROY => {
            if ADDED_LISTENER == 1 {
                RemoveClipboardFormatListener(h_wnd);
                ADDED_LISTENER = 0;
            }
            0
        }
        WM_CLIPBOARDUPDATE => {
            println!("clipboard updated.");
            0
        },
        _ => DefWindowProcW(h_wnd, msg, wparam, lparam)
    }) as LRESULT
}

pub fn run() {
    unsafe {
        let hinst = GetModuleHandleW(ptr::null_mut());
        let wnd_class = WNDCLASSW {
            hInstance: hinst,
            lpfnWndProc: Some(callback_proc),
            lpszClassName: &[67 as u16, 108 as u16, 105 as u16, 112 as u16, 98 as u16, 111 as u16, 97 as u16, 114 as u16, 100 as u16, 77 as u16, 101 as u16, 115 as u16, 115 as u16, 97 as u16, 103 as u16, 101 as u16, 87 as u16, 105 as u16, 110 as u16, 100 as u16, 111 as u16, 119 as u16] as *const u16,
            ..zeroed::<WNDCLASSW>()
        };

        let class_atom = RegisterClassW(&wnd_class as *const WNDCLASSW);

        CreateWindowExW(class_atom.into(), wnd_class.lpszClassName, ptr::null(), 0, 0, 0, 0, 0, HWND_MESSAGE, ptr::null_mut(), hinst, ptr::null_mut());
    }

    loop { } // Added this as the code was exiting immediately
}

src/main.rs

mod clipboard;

fn main() {
    clipboard::run();
}

问题根源与修复方案

核心问题

  1. 缺少Windows消息循环:Windows窗口依赖消息循环分发消息,空loop {}无法处理任何消息,导致窗口回调永远不会被调用。
  2. 无效的窗口类名:原代码中窗口类名是栈上临时切片,注册后指针失效;且未添加宽字符串终止符\0,导致RegisterClassW可能失败。
  3. 未检查API返回值:窗口注册、创建等操作可能静默失败,无法定位问题。
  4. 不安全的静态可变变量:ADDED_LISTENER是全局可变静态变量,存在线程安全隐患。

修改后的代码

src/clipboard.rs

use std::sync::atomic::{AtomicBool, Ordering};
use std::ptr;

use winapi::shared::windef::HWND;
use winapi::um::libloaderapi::GetModuleHandleW;
use winapi::um::winuser::{
    AddClipboardFormatListener,
    RemoveClipboardFormatListener,
    CreateWindowExW,
    RegisterClassW,
    WNDCLASSW,
    WM_CREATE,
    WM_DESTROY,
    WM_CLIPBOARDUPDATE,
    HWND_MESSAGE,
    DefWindowProcW,
    GetMessageW,
    TranslateMessage,
    DispatchMessageW,
    PostQuitMessage,
};
use winapi::shared::minwindef::{LRESULT, UINT, WPARAM, LPARAM, BOOL, MSG};

// 安全的原子变量替代不安全静态可变变量
static ADDED_LISTENER: AtomicBool = AtomicBool::new(false);

// 带终止符的静态窗口类名(宽字符串)
static WND_CLASS_NAME: &[u16] = &[
    b'C' as u16, b'l' as u16, b'i' as u16, b'p' as u16, b'b' as u16, b'o' as u16, b'a' as u16, b'r' as u16, b'd' as u16,
    b'M' as u16, b'e' as u16, b's' as u16, b's' as u16, b'a' as u16, b'g' as u16, b'e' as u16, b'W' as u16, b'i' as u16,
    b'n' as u16, b'd' as u16, b'o' as u16, b'w' as u16, 0 // 必须添加宽字符串终止符
];

unsafe extern "system" fn callback_proc(h_wnd: HWND, msg: UINT, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
    println!("callback called, msg: {}", msg);
    match msg {
        WM_CREATE => {
            let success = AddClipboardFormatListener(h_wnd) != 0;
            ADDED_LISTENER.store(success, Ordering::SeqCst);
            if success { 0 } else { -1 }
        }
        WM_DESTROY => {
            if ADDED_LISTENER.load(Ordering::SeqCst) {
                RemoveClipboardFormatListener(h_wnd);
                ADDED_LISTENER.store(false, Ordering::SeqCst);
            }
            PostQuitMessage(0);
            0
        }
        WM_CLIPBOARDUPDATE => {
            println!("clipboard updated.");
            0
        },
        _ => DefWindowProcW(h_wnd, msg, wparam, lparam)
    }
}

pub fn run() {
    unsafe {
        let hinst = GetModuleHandleW(ptr::null_mut());
        if hinst.is_null() {
            panic!("Failed to get module handle");
        }

        let wnd_class = WNDCLASSW {
            hInstance: hinst,
            lpfnWndProc: Some(callback_proc),
            lpszClassName: WND_CLASS_NAME.as_ptr(),
            cbClsExtra: 0,
            cbWndExtra: 0,
            hCursor: ptr::null_mut(),
            hIcon: ptr::null_mut(),
            hbrBackground: ptr::null_mut(),
            lpszMenuName: ptr::null_mut(),
            style: 0,
        };

        let class_atom = RegisterClassW(&wnd_class);
        if class_atom == 0 {
            panic!("Failed to register window class");
        }

        let hwnd = CreateWindowExW(
            0,
            WND_CLASS_NAME.as_ptr(),
            ptr::null(),
            0,
            0, 0, 0, 0,
            HWND_MESSAGE,
            ptr::null_mut(),
            hinst,
            ptr::null_mut()
        );
        if hwnd.is_null() {
            panic!("Failed to create window");
        }

        // 启动标准Windows消息循环
        let mut msg = std::mem::zeroed::<MSG>();
        while GetMessageW(&mut msg, ptr::null_mut(), 0, 0) > 0 {
            TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
    }
}

src/main.rs(无需修改)

mod clipboard;

fn main() {
    clipboard::run();
}

关键修改说明

  1. 添加消息循环:使用GetMessageW/TranslateMessage/DispatchMessageW处理Windows消息,确保窗口回调能被触发。
  2. 修复窗口类名:使用带\0终止符的静态宽字符串,避免指针失效和注册失败。
  3. 添加错误检查:对GetModuleHandleW、RegisterClassW、CreateWindowExW的返回值进行检查,快速定位失败点。
  4. 原子变量替代不安全静态变量:用AtomicBool保证线程安全,符合Rust的安全规范。
  5. 添加PostQuitMessage:在窗口销毁时发送退出消息,让消息循环正常终止。

内容的提问来源于stack exchange,提问作者Timsib Adnap

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 07:25:14