virtual-file-transfer 0.1.0

Transfer in-memory, generated, or archived data as files via Windows drag-and-drop and clipboard without staging temporary files
Documentation
use virtual_file_transfer::{FactorySource, SourceReader, VirtualFile, begin_drag};

use std::io::{self, Read};
use windows::Win32::Foundation::{HINSTANCE, HWND, LPARAM, LRESULT, WPARAM};
use windows::Win32::System::LibraryLoader::GetModuleHandleW;
use windows::Win32::UI::WindowsAndMessaging::{
    CS_HREDRAW, CS_VREDRAW, CW_USEDEFAULT, CreateWindowExW, DefWindowProcW, DispatchMessageW,
    GetMessageW, IDC_ARROW, LoadCursorW, MSG, PostQuitMessage, RegisterClassW, TranslateMessage,
    WINDOW_EX_STYLE, WM_DESTROY, WM_LBUTTONDOWN, WNDCLASSW, WS_OVERLAPPEDWINDOW, WS_VISIBLE,
};
use windows::core::{Error, Result, w};

const TEST_SIZE: u64 = (1u64 << 32) + 1;
const BLOCK_SIZE: u64 = 1024 * 1024;

struct GeneratedReader {
    position: u64,
}

impl Read for GeneratedReader {
    fn read(&mut self, buffer: &mut [u8]) -> io::Result<usize> {
        let remaining = TEST_SIZE - self.position;
        let length = (buffer.len() as u64).min(remaining) as usize;

        let mut written = 0;

        while written < length {
            let block_index = self.position / BLOCK_SIZE;
            let block_offset = self.position % BLOCK_SIZE;

            let chunk = (length - written).min((BLOCK_SIZE - block_offset) as usize);

            buffer[written..written + chunk].fill((block_index % 251) as u8);

            self.position += chunk as u64;
            written += chunk;
        }

        Ok(length)
    }
}

fn begin_drag_example() -> virtual_file_transfer::Result<()> {
    let source =
        FactorySource::new(|| Ok(SourceReader::streaming(GeneratedReader { position: 0 })));

    let file = VirtualFile::new("large-test.bin", Box::new(source))?.with_size(TEST_SIZE);

    begin_drag(vec![file])?;

    Ok(())
}

unsafe extern "system" fn window_proc(
    hwnd: HWND,
    message: u32,
    wparam: WPARAM,
    lparam: LPARAM,
) -> LRESULT {
    match message {
        WM_LBUTTONDOWN => {
            eprintln!("WM_LBUTTONDOWN");

            if let Err(error) = begin_drag_example() {
                eprintln!("begin_drag failed: {error}");
            }

            eprintln!("start_drag returned");

            LRESULT(0)
        }

        WM_DESTROY => {
            unsafe {
                PostQuitMessage(0);
            }
            LRESULT(0)
        }
        _ => unsafe { DefWindowProcW(hwnd, message, wparam, lparam) },
    }
}

fn main() -> Result<()> {
    let module = unsafe { GetModuleHandleW(None)? };

    let instance = HINSTANCE(module.0);

    let class_name = w!("VirtualFileLargeDrag");

    let window_class = WNDCLASSW {
        style: CS_HREDRAW | CS_VREDRAW,
        lpfnWndProc: Some(window_proc),
        hInstance: instance,
        hCursor: unsafe { LoadCursorW(None, IDC_ARROW)? },
        lpszClassName: class_name,
        ..Default::default()
    };

    let atom = unsafe { RegisterClassW(&window_class) };

    if atom == 0 {
        return Err(Error::from_thread());
    }

    let _window = unsafe {
        CreateWindowExW(
            WINDOW_EX_STYLE::default(),
            class_name,
            w!("Large File Drag - click and drag to Explorer"),
            WS_OVERLAPPEDWINDOW | WS_VISIBLE,
            CW_USEDEFAULT,
            CW_USEDEFAULT,
            600,
            300,
            None,
            None,
            Some(instance),
            None,
        )?
    };

    let mut message = MSG::default();

    loop {
        let result = unsafe { GetMessageW(&mut message, None, 0, 0) };

        if result.0 == -1 {
            return Err(Error::from_thread());
        }

        if result.0 == 0 {
            break;
        }

        unsafe {
            _ = TranslateMessage(&message);
            _ = DispatchMessageW(&message);
        }
    }

    Ok(())
}