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")
         }