There are several issues with the code presented. The most prominent one being, that SetClipboardData expects a HANDLE
to memory allocated using GlocalAlloc. It's also a strict requirement to call OpenClipboard prior to performing operations on it.
A more subtle issue is the character encoding requirement for use with CF_TEXT. This clipboard format expects text encoded using ANSI (codepage) encoding. Since Rust uses Unicode throughout internally, it's probably best to use CF_UNICODETEXT
instead, and convert to UTF-16.
The following is a rough implementation:
[dependencies]
winapi = { version = "0.3.8", features = ["winuser", "winbase"] }
use std::ptr;
use winapi::um::winbase::{GlobalAlloc, GlobalFree, GlobalLock, GlobalUnlock, GMEM_MOVEABLE};
use winapi::um::winuser::{CloseClipboard, OpenClipboard, SetClipboardData, CF_UNICODETEXT};
fn copy_to_clipboard(text: &str) {
// Needs to be UTF-16 encoded
let mut text_utf16: Vec<u16> = text.encode_utf16().collect();
// And zero-terminated before passing it into `SetClipboardData`
text_utf16.push(0);
// Allocate memory
let hglob =
unsafe { GlobalAlloc(GMEM_MOVEABLE, text_utf16.len() * std::mem::size_of::<u16>()) };
// Retrieve writeable pointer to memory
let dst = unsafe { GlobalLock(hglob) };
// Copy data
unsafe { ptr::copy_nonoverlapping(text_utf16.as_ptr(), dst as _, text_utf16.len()) };
// Release writeable pointer
unsafe { GlobalUnlock(hglob) };
// Everything is set up now, let's open the clipboard
unsafe { OpenClipboard(ptr::null_mut()) };
// And apply data
unsafe { SetClipboardData(CF_UNICODETEXT, hglob) };
// Clean up
unsafe { GlobalFree(hglob) };
unsafe { CloseClipboard() };
}
fn main() {
copy_to_clipboard("Hello, world!");
}
Error handling has been elided for brevity. This sample is meant to showcase how to access the clipboard using the winapi crate. It's not production-ready code quality.
A safe implementation, including error handling, might look like the following. It uses the scopeguard crate for resource cleanup:
[dependencies]
winapi = { version = "0.3.8", features = ["winuser", "winbase"] }
scopeguard = "1.1.0"
use scopeguard::defer;
use std::io::Error;
use std::ptr;
use winapi::shared::minwindef::FALSE;
use winapi::um::winbase::{GlobalAlloc, GlobalFree, GlobalLock, GlobalUnlock, GMEM_MOVEABLE};
use winapi::um::winuser::{CloseClipboard, OpenClipboard, SetClipboardData, CF_UNICODETEXT};
fn copy_to_clipboard(text: &str) -> Result<(), Error> {
// Needs to be UTF-16 encoded
let mut text_utf16: Vec<u16> = text.encode_utf16().collect();
// And zero-terminated before passing it into `SetClipboardData`
text_utf16.push(0);
// Allocate memory
let hglob =
unsafe { GlobalAlloc(GMEM_MOVEABLE, text_utf16.len() * std::mem::size_of::<u16>()) };
if hglob == ptr::null_mut() {
return Err(Error::last_os_error());
}
// Ensure cleanup on scope exit
defer!(unsafe { GlobalFree(hglob) };);
// Retrieve writeable pointer to memory
let dst = unsafe { GlobalLock(hglob) };
if dst == ptr::null_mut() {
return Err(Error::last_os_error());
}
// Copy data
unsafe { ptr::copy_nonoverlapping(text_utf16.as_ptr(), dst as _, text_utf16.len()) };
// Release writeable pointer
unsafe { GlobalUnlock(hglob) };
// Everything is set up now, let's open the clipboard
let success = unsafe { OpenClipboard(ptr::null_mut()) } != FALSE;
if !success {
return Err(Error::last_os_error());
}
// Ensure cleanup on scope exit
defer!(unsafe { CloseClipboard() };);
// And apply data
let success = unsafe { SetClipboardData(CF_UNICODETEXT, hglob) } != ptr::null_mut();
if !success {
return Err(Error::last_os_error());
}
Ok(())
}
fn main() {
copy_to_clipboard("Hello, world!").expect("Failed to copy text to clipboard.");
}