| Crates.io | ergonomic-windows |
| lib.rs | ergonomic-windows |
| version | 0.1.0 |
| created_at | 2025-12-29 16:34:43.023022+00 |
| updated_at | 2025-12-29 16:34:43.023022+00 |
| description | Ergonomic, safe Rust wrappers for Windows APIs - handles, processes, registry, file system, UI controls, Direct2D graphics, and more |
| homepage | https://pegasusheavy.github.io/ergonomic-windows/ |
| repository | https://github.com/pegasusheavy/ergonomic-windows |
| max_upload_size | |
| id | 2010878 |
| size | 397,632 |
Ergonomic, safe Rust wrappers for Windows APIs โ handles, processes, registry, file system, and UTF-16 strings with zero-cost abstractions.
๐ Documentation | ๐ API Reference
| Module | Description |
|---|---|
string |
UTF-8 โ UTF-16 conversion with small string optimization and object pooling |
handle |
RAII wrappers for Windows HANDLE with automatic cleanup |
process |
Process creation, management, and querying |
registry |
Windows Registry read/write with type-safe values |
fs |
Windows-specific file system operations |
window |
Window creation and message handling |
error |
Rich error types with Windows error code support |
Add to your Cargo.toml:
[dependencies]
ergonomic-windows = "0.1"
Or use cargo:
cargo add ergonomic-windows
use ergonomic_windows::prelude::*;
fn main() -> Result<()> {
// Spawn a process
let exit_code = Command::new("cmd.exe")
.args(["/c", "echo", "Hello from Rust!"])
.no_window()
.run()?;
println!("Exit code: {}", exit_code);
// Read from the registry
let key = Key::open(
RootKey::CURRENT_USER,
r"Software\Microsoft\Windows\CurrentVersion\Explorer",
Access::READ,
)?;
if let Ok(value) = key.get_value("ShellState") {
println!("ShellState: {:?}", value);
}
Ok(())
}
Convert between Rust UTF-8 strings and Windows UTF-16 strings:
use ergonomic_windows::string::{to_wide, from_wide, WideString};
// Basic conversion
let wide = to_wide("Hello, Windows! ๐");
let back = from_wide(&wide)?;
assert_eq!(back, "Hello, Windows! ๐");
// WideString for Windows API calls
let ws = WideString::new("C:\\Windows\\System32");
// ws.as_pcwstr() returns PCWSTR for Windows APIs
// Small strings are stored inline (no heap allocation!)
let small = WideString::new("Hello");
assert!(small.is_inline()); // true for strings โค22 UTF-16 chars
For scenarios converting many strings, use the object pool to reuse buffers:
use ergonomic_windows::string::WideStringPool;
let mut pool = WideStringPool::new();
for filename in &["file1.txt", "file2.txt", "file3.txt"] {
let wide = pool.get(filename);
// Use wide.as_pcwstr() with Windows APIs
pool.put(wide); // Return buffer for reuse
}
Windows handles are automatically closed when dropped:
use ergonomic_windows::handle::OwnedHandle;
use ergonomic_windows::fs::OpenOptions;
// Open a file - handle is automatically managed
let handle = OpenOptions::new()
.read(true)
.open("file.txt")?;
// Clone handles safely
let cloned = handle.try_clone()?;
// Handles close automatically when dropped
Create and manage Windows processes:
use ergonomic_windows::process::{Command, Process, ProcessAccess};
// Spawn a process
let process = Command::new("notepad.exe")
.arg("document.txt")
.current_dir("C:\\Users\\Public")
.env("MY_VAR", "value")
.spawn()?;
println!("Started process with PID: {}", process.pid());
// Wait with timeout
use std::time::Duration;
match process.wait_timeout(Some(Duration::from_secs(5))) {
Ok(exit_code) => println!("Exited with: {}", exit_code),
Err(_) => {
process.terminate(1)?;
println!("Process terminated");
}
}
// Open existing process
let current = Process::open(process.pid(), ProcessAccess::QUERY)?;
println!("Is running: {}", current.is_running()?);
Read and write Windows Registry values:
use ergonomic_windows::registry::{Key, RootKey, Access, Value};
// Read system information
let key = Key::open(
RootKey::LOCAL_MACHINE,
r"SOFTWARE\Microsoft\Windows NT\CurrentVersion",
Access::READ,
)?;
if let Ok(Value::String(name)) = key.get_value("ProductName") {
println!("Windows Version: {}", name);
}
// Write application settings
let app_key = Key::create(
RootKey::CURRENT_USER,
r"Software\MyApp\Settings",
Access::ALL,
)?;
app_key.set_value("Volume", &Value::dword(75))?;
app_key.set_value("Username", &Value::string("Alice"))?;
app_key.set_value("RecentFiles", &Value::MultiString(vec![
"doc1.txt".into(),
"doc2.txt".into(),
]))?;
// Enumerate keys and values
for subkey in app_key.subkeys()? {
println!("Subkey: {}", subkey);
}
Windows-specific file operations:
use ergonomic_windows::fs::{
get_attributes, set_attributes, FileAttributes,
exists, is_dir, is_file, delete_file,
move_file, move_file_with_options, MoveOptions,
get_system_directory, get_temp_directory,
};
// Check file attributes
let attrs = get_attributes("C:\\Windows")?;
assert!(attrs.is_directory());
// Get system paths
let system_dir = get_system_directory()?;
let temp_dir = get_temp_directory()?;
println!("System: {:?}", system_dir);
println!("Temp: {:?}", temp_dir);
// Move with options
move_file_with_options(
"old_path.txt",
"new_path.txt",
MoveOptions::new().replace().allow_copy(),
)?;
Create windows and handle messages:
use ergonomic_windows::window::{WindowBuilder, MessageHandler, Message};
use ergonomic_windows::prelude::*;
struct MyHandler;
impl MessageHandler for MyHandler {
fn on_create(&mut self, hwnd: windows::Win32::Foundation::HWND) -> Result<()> {
println!("Window created!");
Ok(())
}
fn on_destroy(&mut self, _hwnd: windows::Win32::Foundation::HWND) -> Result<()> {
std::process::exit(0);
}
}
let window = WindowBuilder::new()
.title("My Window")
.size(800, 600)
.style(Style::OVERLAPPEDWINDOW)
.build(MyHandler)?;
window.show(ShowCommand::Show);
Message::run_loop();
This crate uses unsafe code to interface with Windows APIs. All unsafe blocks are:
The public API is entirely safe Rust. Handles are managed via RAII, preventing:
The crate is optimized for performance:
| Optimization | Impact |
|---|---|
| Small String Optimization | Zero allocation for strings โค22 chars |
| Object Pooling | Reusable buffers for high-throughput scenarios |
| Pre-allocated Buffers | Reduced allocations in string builders |
| Inline Hints | Hot paths marked for inlining |
Benchmark results (typical):
WideString::new("Hello"): 0 allocations, ~12nsto_wide / from_wide: 1 allocation eachThis crate requires Rust 1.70 or later.
Contributions are welcome! Please see our Contributing Guidelines.
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)# Run tests
cargo test
# Run benchmarks
cargo bench
# Run clippy
cargo clippy --all-targets
# Build docs
cargo doc --open
Licensed under either of:
at your option.
Built with the excellent windows-rs crate from Microsoft.
Made with โค๏ธ by Pegasus Heavy Industries