From a2af587e60e869f0235703e21772d1fc6a7dadcd Mon Sep 17 00:00:00 2001 From: Richard Markiewicz Date: Thu, 4 Dec 2025 02:38:05 -0500 Subject: [PATCH] fix(cliprdr): prevent window class registration error on multiple sessions (#1047) 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. --- .../ironrdp-cliprdr-native/src/windows/mod.rs | 31 ++++++++++++------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/crates/ironrdp-cliprdr-native/src/windows/mod.rs b/crates/ironrdp-cliprdr-native/src/windows/mod.rs index 48bf2dbc..87574ca5 100644 --- a/crates/ironrdp-cliprdr-native/src/windows/mod.rs +++ b/crates/ironrdp-cliprdr-native/src/windows/mod.rs @@ -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