From 5bf303fd26e793d83f6b8faf9d03d84d559fa379 Mon Sep 17 00:00:00 2001 From: Michael Streif <streifmi@gmail.com> Date: Sat, 29 Jun 2019 00:07:36 +0200 Subject: [PATCH] Improve handling of file paths in the windows DnD handler (#980) * Make FileDropHandler::iterate_filenames more robust by replacing the call to mem::uninitialized with mem::zeroed and change file name retrieval to use buffers of exact length as reported by DragQueryFileW instead of relying on MAX_PATH. * Change remaining calls of uninitialized to zeroed * Run rustfmt * Add CHANGELOG entry and comment --- CHANGELOG.md | 2 ++ src/platform_impl/windows/drop_handler.rs | 24 ++++++++++++----------- src/platform_impl/windows/event_loop.rs | 6 +++--- src/platform_impl/windows/monitor.rs | 2 +- src/platform_impl/windows/raw_input.rs | 4 ++-- src/platform_impl/windows/util.rs | 4 ++-- src/platform_impl/windows/window.rs | 2 +- 7 files changed, 24 insertions(+), 20 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05c9d644..cde86e26 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,7 @@ # Unreleased +- On Windows, support paths longer than MAX_PATH (260 characters) in `WindowEvent::DroppedFile` +and `WindowEvent::HoveredFile`. - On Mac, implement `DeviceEvent::Button`. - Change `Event::Suspended(true / false)` to `Event::Suspended` and `Event::Resumed`. - On X11, fix sanity check which checks that a monitor's reported width and height (in millimeters) are non-zero when calculating the DPI factor. diff --git a/src/platform_impl/windows/drop_handler.rs b/src/platform_impl/windows/drop_handler.rs index 2ad5a97a..feec2639 100644 --- a/src/platform_impl/windows/drop_handler.rs +++ b/src/platform_impl/windows/drop_handler.rs @@ -1,6 +1,5 @@ use std::{ ffi::OsString, - mem, os::windows::ffi::OsStringExt, path::PathBuf, ptr, @@ -11,7 +10,7 @@ use winapi::{ ctypes::c_void, shared::{ guiddef::REFIID, - minwindef::{DWORD, MAX_PATH, UINT, ULONG}, + minwindef::{DWORD, UINT, ULONG}, windef::{HWND, POINTL}, winerror::S_OK, }, @@ -171,7 +170,6 @@ impl FileDropHandler { F: Fn(PathBuf), { use winapi::{ - ctypes::wchar_t, shared::{ winerror::{DV_E_FORMATETC, SUCCEEDED}, wtypes::{CLIPFORMAT, DVASPECT_CONTENT}, @@ -191,7 +189,7 @@ impl FileDropHandler { tymed: TYMED_HGLOBAL, }; - let mut medium = mem::uninitialized(); + let mut medium = std::mem::zeroed(); let get_data_result = (*data_obj).GetData(&mut drop_format, &mut medium); if SUCCEEDED(get_data_result) { let hglobal = (*medium.u).hGlobal(); @@ -200,15 +198,19 @@ impl FileDropHandler { // The second parameter (0xFFFFFFFF) instructs the function to return the item count let item_count = DragQueryFileW(hdrop, 0xFFFFFFFF, ptr::null_mut(), 0); - let mut pathbuf: [wchar_t; MAX_PATH] = mem::uninitialized(); - for i in 0..item_count { - let character_count = - DragQueryFileW(hdrop, i, pathbuf.as_mut_ptr(), MAX_PATH as UINT) as usize; + // Get the length of the path string NOT including the terminating null character. + // Previously, this was using a fixed size array of MAX_PATH length, but the + // Windows API allows longer paths under certain circumstances. + let character_count = DragQueryFileW(hdrop, i, ptr::null_mut(), 0) as usize; + let str_len = character_count + 1; - if character_count > 0 { - callback(OsString::from_wide(&pathbuf[0..character_count]).into()); - } + // Fill path_buf with the null-terminated file name + let mut path_buf = Vec::with_capacity(str_len); + DragQueryFileW(hdrop, i, path_buf.as_mut_ptr(), str_len as UINT); + path_buf.set_len(str_len); + + callback(OsString::from_wide(&path_buf[0..character_count]).into()); } return Some(hdrop); diff --git a/src/platform_impl/windows/event_loop.rs b/src/platform_impl/windows/event_loop.rs index 4520e34b..3eac2910 100644 --- a/src/platform_impl/windows/event_loop.rs +++ b/src/platform_impl/windows/event_loop.rs @@ -182,7 +182,7 @@ impl<T: 'static> EventLoop<T> { } unsafe { - let mut msg = mem::uninitialized(); + let mut msg = mem::zeroed(); let mut msg_unprocessed = false; 'main: loop { @@ -507,7 +507,7 @@ impl<T> EventLoopRunner<T> { // Returns true if the wait time was reached, and false if a message must be processed. unsafe fn wait_until_time_or_msg(wait_until: Instant) -> bool { - let mut msg = mem::uninitialized(); + let mut msg = mem::zeroed(); let now = Instant::now(); if now <= wait_until { // MsgWaitForMultipleObjects tends to overshoot just a little bit. We subtract 1 millisecond @@ -1645,7 +1645,7 @@ unsafe extern "system" fn thread_event_target_callback<T>( } }; if in_modal_loop { - let mut msg = mem::uninitialized(); + let mut msg = mem::zeroed(); loop { if 0 == winuser::PeekMessageW(&mut msg, ptr::null_mut(), 0, 0, 0) { break; diff --git a/src/platform_impl/windows/monitor.rs b/src/platform_impl/windows/monitor.rs index e4ee3f92..addb0175 100644 --- a/src/platform_impl/windows/monitor.rs +++ b/src/platform_impl/windows/monitor.rs @@ -109,7 +109,7 @@ impl Window { } pub(crate) fn get_monitor_info(hmonitor: HMONITOR) -> Result<winuser::MONITORINFOEXW, io::Error> { - let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::uninitialized() }; + let mut monitor_info: winuser::MONITORINFOEXW = unsafe { mem::zeroed() }; monitor_info.cbSize = mem::size_of::<winuser::MONITORINFOEXW>() as DWORD; let status = unsafe { winuser::GetMonitorInfoW( diff --git a/src/platform_impl/windows/raw_input.rs b/src/platform_impl/windows/raw_input.rs index 5e065606..73b136a8 100644 --- a/src/platform_impl/windows/raw_input.rs +++ b/src/platform_impl/windows/raw_input.rs @@ -74,7 +74,7 @@ impl From<RID_DEVICE_INFO> for RawDeviceInfo { #[allow(dead_code)] pub fn get_raw_input_device_info(handle: HANDLE) -> Option<RawDeviceInfo> { - let mut info: RID_DEVICE_INFO = unsafe { mem::uninitialized() }; + let mut info: RID_DEVICE_INFO = unsafe { mem::zeroed() }; let info_size = size_of::<RID_DEVICE_INFO>() as UINT; info.cbSize = info_size; @@ -164,7 +164,7 @@ pub fn register_all_mice_and_keyboards_for_raw_input(window_handle: HWND) -> boo } pub fn get_raw_input_data(handle: HRAWINPUT) -> Option<RAWINPUT> { - let mut data: RAWINPUT = unsafe { mem::uninitialized() }; + let mut data: RAWINPUT = unsafe { mem::zeroed() }; let mut data_size = size_of::<RAWINPUT>() as UINT; let header_size = size_of::<RAWINPUTHEADER>() as UINT; diff --git a/src/platform_impl/windows/util.rs b/src/platform_impl/windows/util.rs index edb5769e..28513d30 100644 --- a/src/platform_impl/windows/util.rs +++ b/src/platform_impl/windows/util.rs @@ -33,7 +33,7 @@ pub fn wchar_ptr_to_string(wchar: *const wchar_t) -> String { } pub unsafe fn status_map<T, F: FnMut(&mut T) -> BOOL>(mut fun: F) -> Option<T> { - let mut data: T = mem::uninitialized(); + let mut data: T = mem::zeroed(); if fun(&mut data) != 0 { Some(data) } else { @@ -59,7 +59,7 @@ pub fn get_window_rect(hwnd: HWND) -> Option<RECT> { pub fn get_client_rect(hwnd: HWND) -> Result<RECT, io::Error> { unsafe { - let mut rect = mem::uninitialized(); + let mut rect = mem::zeroed(); let mut top_left = mem::zeroed(); win_to_err(|| winuser::ClientToScreen(hwnd, &mut top_left))?; diff --git a/src/platform_impl/windows/window.rs b/src/platform_impl/windows/window.rs index 54089219..4e67d5c4 100644 --- a/src/platform_impl/windows/window.rs +++ b/src/platform_impl/windows/window.rs @@ -211,7 +211,7 @@ impl Window { } pub(crate) fn inner_size_physical(&self) -> (u32, u32) { - let mut rect: RECT = unsafe { mem::uninitialized() }; + let mut rect: RECT = unsafe { mem::zeroed() }; if unsafe { winuser::GetClientRect(self.window.0, &mut rect) } == 0 { panic!("Unexpected GetClientRect failure: please report this error to https://github.com/rust-windowing/winit") }