I am trying to make a clipboard manager for windows in rust and am using winapi-rs
crate to use winapi.
My current implementation is as follows:
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();
}
I got help from a c++ impl from this post from stackoverflow and this implementation in python.
But here I am not getting any output nor any error messages.
Note: using Rust v1.66.0-stable
There are multiple things I would like to annotate about your code.
&[u16]
array. That's just absolutely impossible to read. Use a to_wstring()
helper function and an actual string.CreateWindowExW
, otherwise you would realize that this function fails.class_atom.into()
into CreateWindowExW
as the first argument? I couldn't find any reference what that would accomplish; the first argument is dwExStyle
, and class_atom
is the class handle. Those have nothing in common; this is most likely the reason why CreateWindowExW
fails.
If at all, it should be the second argument, but as you already provide lpszClassName
, you simply don't need the class atom.static mut
in Rust. It's not thread-safe and requires unsafe
to be accessed. Use AtomicBool
instead.That said, here is a version that works for me:
use winapi::shared::minwindef::*;
use winapi::shared::windef::*;
use winapi::um::libloaderapi::*;
use winapi::um::winuser::*;
use std::ffi::OsStr;
use std::mem::zeroed;
use std::os::windows::ffi::OsStrExt;
use std::sync::atomic::AtomicBool;
use std::sync::atomic::Ordering;
fn to_wstring(s: &str) -> Vec<u16> {
OsStr::new(s)
.encode_wide()
.chain(std::iter::once(0))
.collect()
}
static ADDED_LISTENER: AtomicBool = AtomicBool::new(false);
pub unsafe extern "system" fn window_proc(
hwnd: HWND,
msg: UINT,
wparam: WPARAM,
lparam: LPARAM,
) -> LRESULT {
println!("called {}", msg);
match msg {
WM_CREATE => {
let add_result = AddClipboardFormatListener(hwnd);
if add_result == 1 {
ADDED_LISTENER.store(true, Ordering::Relaxed);
0
} else {
-1
}
}
WM_DESTROY => {
if ADDED_LISTENER.swap(false, Ordering::Relaxed) {
RemoveClipboardFormatListener(hwnd);
}
PostQuitMessage(0);
0
}
WM_CLIPBOARDUPDATE => {
println!("clipboard updated.");
0
}
_ => DefWindowProcW(hwnd, msg, wparam, lparam),
}
}
fn main() {
unsafe {
let hinst = GetModuleHandleW(std::ptr::null_mut());
let wnd_class = WNDCLASSW {
hInstance: hinst,
lpfnWndProc: Some(window_proc),
lpszClassName: to_wstring("ClipboardMessageWindow").as_ptr(),
..zeroed::<WNDCLASSW>()
};
if RegisterClassW(&wnd_class) == 0 {
panic!("RegisterClassEx failed");
}
let hwnd = CreateWindowExW(
0,
wnd_class.lpszClassName,
std::ptr::null(),
0,
0,
0,
0,
0,
HWND_MESSAGE,
std::ptr::null_mut(),
hinst,
std::ptr::null_mut(),
);
if hwnd == std::ptr::null_mut() {
panic!("CreateWindowEx failed");
}
let mut msg = std::mem::zeroed();
loop {
let res = GetMessageW(&mut msg, std::ptr::null_mut(), 0, 0);
if res == 0 || res == -1 {
break;
}
DispatchMessageW(&msg);
}
}
}