David's Blog

Interacting with the Windows Clipboard using WinAPI in Rust

By David Li on 2023-04-24T18:13:58.063Z

Interacting with the Windows Clipboard using WinAPI in Rust

In this article, we will learn how to interact with the Windows clipboard using the Windows API (WinAPI) in Rust. The clipboard is a temporary storage area for data that the user wants to copy from one place to another. It allows users to perform copy-paste operations across different applications. We will cover the following topics:

  1. Setting up the Rust environment
  2. Accessing WinAPI functions in Rust
  3. Implementing clipboard functions
  4. Example application

1. Setting up the Rust environment

First, you need to have Rust installed on your system. You can follow the official installation guide here.

Next, create a new Rust project:

$ cargo new winapi_clipboard
$ cd winapi_clipboard

2. Accessing WinAPI functions in Rust

To access WinAPI functions in Rust, we will use the winapi crate. Add the following dependencies to your Cargo.toml file:

[dependencies]
winapi = { version = "0.3", features = ["winuser"] }

The winuser feature enables the necessary clipboard functions.

3. Implementing clipboard functions

Let’s create a new module named clipboard in src/lib.rs:

mod clipboard {
    use winapi::um::winuser::{
        CloseClipboard, EmptyClipboard, GetClipboardData, OpenClipboard, SetClipboardData,
        CF_TEXT,
    };
    use winapi::ctypes::c_void;
    use winapi::shared::minwindef::{HGLOBAL, UINT};
    use winapi::shared::ntdef::NULL;
    use std::ptr::{null_mut, null};
    use std::ffi::CString;

    // WinAPI clipboard functions
}

Now, let’s implement the clipboard functions:

Opening and closing the clipboard

To interact with the clipboard, we need to open it first. We can do this using the OpenClipboard function:

fn open_clipboard() -> Result<(), &'static str> {
    let result = unsafe { OpenClipboard(null_mut()) };
    if result == 0 {
        Err("Failed to open the clipboard")
    } else {
        Ok(())
    }
}

After we are done interacting with the clipboard, we need to close it using the CloseClipboard function:

fn close_clipboard() {
    unsafe {
        CloseClipboard();
    }
}

Reading from the clipboard

To read text from the clipboard, we’ll use the GetClipboardData function:

fn get_clipboard_text() -> Result<String, &'static str> {
    let clipboard_data = unsafe { GetClipboardData(CF_TEXT) as *mut u8 };
    if clipboard_data.is_null() {
        return Err("Failed to get clipboard data");
    }

    let mut text = String::new();
    let mut index = 0;
    loop {
        let ch = unsafe { *clipboard_data.offset(index) } as char;
        if ch == '\0' {
            break;
        }
        text.push(ch);
        index += 1;
    }

    Ok(text)
}

Writing to the clipboard

To write text to the clipboard, we’ll use the EmptyClipboard and SetClipboardData functions:

fn set_clipboard_text(text: &str) -> Result<(), &'static str> {
    let text_len = text.len() + 1;
    let text_cstring = CString::new(text).unwrap();
    let h_mem = unsafe { GlobalAlloc(GMEM_MOVEABLE, text_len) } as *mut c_void;

    if h_mem.is_null() {
        return Err("Failed to allocate memory for clipboard data");
    }

    let h_mem_text = unsafe { GlobalLock(h_mem) as *mut u8 };
    for (i, ch) in text_cstring.as_bytes_with_nul().iter().enumerate() {
        unsafe {
            *h_mem_text.offset(i as isize) = *ch;
        }
    }

    unsafe {
        GlobalUnlock(h_mem);
        EmptyClipboard();
        if SetClipboardData(CF_TEXT, h_mem as HGLOBAL) == NULL {
            GlobalFree(h_mem);
            return Err("Failed to set clipboard data");
        }
    }

    Ok(())
}

4. Example application

Now, let’s create an example application that reads and writes text to the clipboard:

fn main() {
    let example_text = "Hello, clipboard!";

    println!("Writing to clipboard: {}", example_text);
    clipboard::open_clipboard().unwrap();
    clipboard::set_clipboard_text(example_text).unwrap();
    clipboard::close_clipboard();

    println!("Reading from clipboard:");
    clipboard::open_clipboard().unwrap();
       let clipboard_text = clipboard::get_clipboard_text().unwrap();
    clipboard::close_clipboard();

    println!("Clipboard content: {}", clipboard_text);
}

Compile and run the example:

$ cargo build --release
$ target/release/winapi_clipboard

You should see the following output:

Writing to clipboard: Hello, clipboard!
Reading from clipboard:
Clipboard content: Hello, clipboard!

Conclusion

In this article, we’ve learned how to interact with the Windows clipboard using WinAPI in Rust. We covered setting up the Rust environment, accessing WinAPI functions, and implementing clipboard functions for reading and writing text data. This example can serve as a starting point for more advanced clipboard manipulation in your Rust applications.

© Copyright 2024 by FriendlyUsers Tech Blog. Built with ♥ by FriendlyUser. Last updated on 2024-02-29.