Skip to content

Instantly share code, notes, and snippets.

@kennykerr
Created October 16, 2024 17:25
Show Gist options
  • Save kennykerr/e1389a2b03028209fd576d5df1ded4fa to your computer and use it in GitHub Desktop.
Save kennykerr/e1389a2b03028209fd576d5df1ded4fa to your computer and use it in GitHub Desktop.
use windows::{
core::{HSTRING, PWSTR},
Win32::Foundation::UNICODE_STRING,
};
fn main() {
let us = UnicodeString::new("hello world");
dbg!(&us);
unsafe {
print_value(&*us);
}
}
// Example of some Windows API that expects a `UNICODE_STRING` in the typical fashion.
//
// This is safe assuming the `UNICODE_STRING` is constructed correctly.
unsafe fn print_value(value: *const UNICODE_STRING) {
if !value.is_null() {
let value = *value;
println!(
"some_api: {}",
String::from_utf16_lossy(std::slice::from_raw_parts(
value.Buffer.0,
(value.Length / 2).into()
))
);
}
}
// A safe Rust wrapper for `UNICODE_STRING`.
#[derive(Debug)]
struct UnicodeString {
header: UNICODE_STRING,
_buffer: HSTRING,
}
// Allows the `UnicodeString` to be treated as a `UNICODE_STRING` but note that the latter is immutable
// and a reference bound to the lifetime of `UnicodeString` so this is quite safe.
impl std::ops::Deref for UnicodeString {
type Target = UNICODE_STRING;
fn deref(&self) -> &UNICODE_STRING {
&self.header
}
}
impl UnicodeString {
// Safely construct a `UNICODE_STRING` from a Rust string slice.
pub fn new(value: &str) -> Self {
// Convert the string to UTF-16.
let buffer = HSTRING::from(value);
// Get the length in bytes as a `u16` value.
let bytes_len = u16::try_from(buffer.len() * 2).expect("too long");
// Construct the `UNICODE_STRING` pointing to the internal buffer.
let header = UNICODE_STRING {
Length: bytes_len,
MaximumLength: bytes_len,
Buffer: PWSTR(buffer.as_ptr() as _),
};
// The resulting `UnicodeString` owns both the `UNICODE_STRING` and the backing buffer.
Self {
header,
_buffer: buffer,
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment