fix(cliprdr): prevent window class registration error on multiple sessions (#1047)
Some checks are pending
CI / Check formatting (push) Waiting to run
CI / FFI (push) Blocked by required conditions
CI / Success (push) Blocked by required conditions
CI / Check typos (push) Waiting to run
CI / Checks [linux] (push) Blocked by required conditions
CI / Checks [macos] (push) Blocked by required conditions
CI / Checks [windows] (push) Blocked by required conditions
CI / Fuzzing (push) Blocked by required conditions
CI / Web Client (push) Blocked by required conditions
Coverage / Coverage Report (push) Waiting to run
Release crates / Open release PR (push) Waiting to run
Release crates / Release crates (push) Waiting to run

When starting a second clipboard session, `RegisterClassA` would fail
with `ERROR_CLASS_ALREADY_EXISTS` because window classes are global to
the process. Now checks if the class is already registered before
attempting registration, allowing multiple WinClipboard instances to
coexist.
This commit is contained in:
Richard Markiewicz 2025-12-04 02:38:05 -05:00 committed by GitHub
parent 924330159a
commit a2af587e60
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -19,7 +19,8 @@ use windows::Win32::System::DataExchange::{AddClipboardFormatListener, RemoveCli
use windows::Win32::System::LibraryLoader::GetModuleHandleA;
use windows::Win32::UI::Shell::{RemoveWindowSubclass, SetWindowSubclass};
use windows::Win32::UI::WindowsAndMessaging::{
CreateWindowExA, DefWindowProcA, RegisterClassA, CW_USEDEFAULT, WINDOW_EX_STYLE, WM_USER, WNDCLASSA, WS_POPUP,
CreateWindowExA, DefWindowProcA, GetClassInfoA, RegisterClassA, CW_USEDEFAULT, WINDOW_EX_STYLE, WM_USER, WNDCLASSA,
WS_POPUP,
};
use self::clipboard_impl::{clipboard_subproc, WinClipboardImpl};
@ -152,17 +153,25 @@ impl WinClipboard {
// SAFETY: low-level WinAPI call
let instance = unsafe { GetModuleHandleA(None)? };
let window_class = s!("IronRDPClipboardMonitor");
let wc = WNDCLASSA {
hInstance: instance.into(),
lpszClassName: window_class,
lpfnWndProc: Some(wndproc),
..Default::default()
};
// SAFETY: low-level WinAPI call
let atom = unsafe { RegisterClassA(&wc) };
if atom == 0 {
return Err(WinCliprdrError::from(Error::from_thread()));
let mut existing_wc = WNDCLASSA::default();
// SAFETY: `instance` is a valid module handle, `window_class` is a valid null-terminated string,
// and `existing_wc` is a valid mutable reference to a WNDCLASSA structure.
let class_exists = unsafe { GetClassInfoA(Some(instance.into()), window_class, &mut existing_wc).is_ok() };
if !class_exists {
let wc = WNDCLASSA {
hInstance: instance.into(),
lpszClassName: window_class,
lpfnWndProc: Some(wndproc),
..Default::default()
};
// SAFETY: low-level WinAPI call
let atom = unsafe { RegisterClassA(&wc) };
if atom == 0 {
return Err(WinCliprdrError::from(Error::from_thread()));
}
}
// SAFETY: low-level WinAPI call