winapi_easy/
clipboard.rs

1//! Clipboard access.
2
3use std::ffi::OsString;
4use std::io;
5use std::os::windows::ffi::OsStringExt;
6use std::path::PathBuf;
7
8use windows::Win32::Foundation::HGLOBAL;
9use windows::Win32::System::DataExchange::{
10    CloseClipboard,
11    GetClipboardData,
12    OpenClipboard,
13};
14use windows::Win32::System::Ole::CF_HDROP;
15use windows::Win32::UI::Shell::{
16    DragQueryFileW,
17    HDROP,
18};
19
20use crate::internal::{
21    GlobalLockedData,
22    ReturnValue,
23};
24
25/// Returns a list of file paths that have been copied to the clipboard.
26///
27/// Will return `Err` if the clipboard cannot be accessed or does not contain files.
28pub fn get_file_list() -> io::Result<Vec<PathBuf>> {
29    let f = || {
30        let mut clipboard_data = {
31            let clipboard_data = unsafe { GetClipboardData(CF_HDROP.0.into()) }?;
32            GlobalLockedData::lock(HGLOBAL(clipboard_data.0))?
33        };
34
35        let num_files = unsafe { DragQueryFileW(HDROP(clipboard_data.ptr()), u32::MAX, None) };
36        let file_names: io::Result<Vec<PathBuf>> = (0..num_files)
37            .map(|file_index| {
38                let required_size =
39                    unsafe { 1 + DragQueryFileW(HDROP(clipboard_data.ptr()), file_index, None) }
40                        .if_null_to_error(|| io::ErrorKind::Other.into())?;
41                let file_str_buf = {
42                    let mut buffer = vec![0; required_size as usize];
43                    unsafe {
44                        DragQueryFileW(
45                            HDROP(clipboard_data.ptr()),
46                            file_index,
47                            Some(buffer.as_mut_slice()),
48                        )
49                    }
50                    .if_null_to_error(|| io::ErrorKind::Other.into())?;
51                    // Set length, remove terminating zero
52                    buffer.truncate(buffer.len() - 1);
53                    buffer
54                };
55                let os_string = OsString::from_wide(&file_str_buf);
56                Ok(PathBuf::from(os_string))
57            })
58            .collect();
59        file_names
60    };
61    with_open_clipboard_do(f)
62}
63
64fn with_open_clipboard_do<F, R>(f: F) -> io::Result<R>
65where
66    F: FnOnce() -> io::Result<R>,
67{
68    unsafe {
69        OpenClipboard(None)?;
70    }
71    let result = f();
72    unsafe {
73        CloseClipboard()?;
74    }
75    result
76}
77
78#[cfg(test)]
79mod tests {
80    use std::time::Duration;
81
82    use super::*;
83
84    #[test]
85    fn open_clipboard() -> io::Result<()> {
86        with_open_clipboard_do(|| {
87            std::thread::sleep(Duration::from_millis(0));
88            Ok(())
89        })
90    }
91}