1

I am attempting to set data into the Windows cipboard using the winapi crate in Rust(I am new both to Rust and the win32 api).

The input for the SetClipboardData call requires a file type, and a C generic pointer, or *mut libc::c_void in Rust. ( docs linked below )

In the final version, I plan to copy image files, but I failed testing with texts strings, and here is the code for it:

extern crate winapi;
extern crate libc;

fn main() {
    let mut test = "test\r\n";
    let test_ptr: *mut libc::c_void = &mut test as *mut _ as *mut libc::c_void;
    loop {
        if (condition)  
        {
            // let mut test = "test\r\n";  // these commented lets are refered to issue 2
            // let test_ptr: *mut libc::c_void = &mut test as *mut _ as *mut libc::c_void; 
            unsafe {winapi::um::winuser::SetClipboardData(winapi::um::winuser::CF_TEXT, test_ptr);}
        }
    }
}

Notes:

Issue 1:

With the code as is, it wont error, but also wont work, and nothing goes into the clipboard.

Issue 2:

When the pointer declaration is inside the loop, my program panics with error: process didn't exit successfully: exit code: 0xc0000374, STATUS_HEAP_CORRUPTION). This got me confused, since I expected my values to be in the stack, not the heap. And if I undesrtood correclty, the winapi expects the data to be in the stack.

Any help would be appreciated.

Auyer
  • 2,693
  • 3
  • 15
  • 32
  • 2
    `input for the SetClipboardData call requires ... a C generic pointer` No, the HANDLE argument must be an HGLOBAL returned by a [GlobalAlloc](https://learn.microsoft.com/en-us/windows/win32/api/winbase/nf-winbase-globalalloc) call with the GMEM_MOVEABLE flag. The steps in C can be seen [here](https://learn.microsoft.com/en-us/windows/win32/dataxchg/using-the-clipboard#copying-information-to-the-clipboard), don't know how that translates to rust. – dxiv May 24 '20 at 05:44
  • 1
    *"With the code as is, it wont error"* - You aren't evaluating the return value of `SetClipboardData` so there's no way for you to know, whether it succeeded or failed. – IInspectable May 24 '20 at 08:29
  • Is this issue solved? – Rita Han May 27 '20 at 09:05

1 Answers1

5

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.");
}
IInspectable
  • 46,945
  • 8
  • 85
  • 181