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
CodePudding user response:
There are multiple things I would like to annotate about your code.
- Don't hard-code a string through a manually specified
&[u16]
array. That's just absolutely impossible to read. Use ato_wstring()
helper function and an actual string. - You have to actually receive and process the messages. An empty loop does exactly what it should do: Nothing :) (and that with 100% CPU power)
- There is a return value check missing after
CreateWindowExW
, otherwise you would realize that this function fails. - Why do you feed
class_atom.into()
intoCreateWindowExW
as the first argument? I couldn't find any reference what that would accomplish; the first argument isdwExStyle
, andclass_atom
is the class handle. Those have nothing in common; this is most likely the reason whyCreateWindowExW
fails. If at all, it should be the second argument, but as you already providelpszClassName
, you simply don't need the class atom. - Avoid
static mut
in Rust. It's not thread-safe and requiresunsafe
to be accessed. UseAtomicBool
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);
}
}
}