Skip to content

Commit 9d1115a

Browse files
committed
Provide a macro for HString literals
We could also use a null-terminating variant of wstr! internally in DEFINE_CLSID
1 parent 18b1374 commit 9d1115a

File tree

6 files changed

+59
-18
lines changed

6 files changed

+59
-18
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ exclude = ["Generator/**"]
1414

1515
[dependencies]
1616
winapi = { version = "0.3", features = ["winnt", "combaseapi", "oleauto", "roapi", "roerrorapi", "hstring", "winstring", "winerror", "restrictederrorinfo"] }
17+
wstr = "0.2"
1718

1819
[features]
1920
nightly = []

examples/test.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ fn main() {
1515
}
1616

1717
fn run() {
18-
let base = FastHString::new("https://github.com");
19-
let relative = FastHString::new("contextfree/winrt-rust");
18+
let base = hstr!("https://github.com");
19+
let relative = hstr!("contextfree/winrt-rust");
2020
let uri = Uri::create_with_relative_uri(&base, &relative).unwrap();
2121
let to_string = unsafe { uri.query_interface::<IStringable>().unwrap().to_string().unwrap() };
2222
println!("{} -> {}", uri.get_runtime_class_name(), to_string);
@@ -126,17 +126,17 @@ fn run() {
126126
println!("{:?} = {:?}", array, &returned_array[..]);
127127
assert_eq!(array, &returned_array[..]);
128128

129-
let str1 = FastHString::new("foo");
130-
let str2 = FastHString::new("bar");
131-
let array = &mut [&*str1, &*str2, &*str1, &*str2];
129+
let str1 = hstr!("foo");
130+
let str2 = hstr!("bar");
131+
let array = &mut [&*str1, &*str2, &*str1, &*str2]; // convert to array of `&HStringArg`
132132
let boxed_array = PropertyValue::create_string_array(array);
133133
let boxed_array = boxed_array.unwrap().query_interface::<IPropertyValue>().unwrap();
134134
assert_eq!(unsafe { boxed_array.get_type().unwrap() }, PropertyType::StringArray);
135135
let boxed_array = boxed_array.query_interface::<IReferenceArray<HString>>().unwrap();
136136
let returned_array = unsafe { boxed_array.get_value().unwrap() };
137137
assert_eq!(array.len(), returned_array.len());
138138
for i in 0..array.len() {
139-
assert!(returned_array[i] == (if i % 2 == 0 { &str1 } else { &str2 }));
139+
assert!(returned_array[i] == (if i % 2 == 0 { str1 } else { str2 }));
140140
}
141141
// TODO: test array interface objects (also see if ComArray drops contents correctly)
142142

examples/toast_notify.rs

+5-4
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
// Use the following command to run this example:
44
// > cargo run --example toast_notify --features "windows-data windows-ui"
55

6+
#[macro_use]
67
extern crate winrt;
78

89
use winrt::*;
@@ -20,10 +21,10 @@ fn run() { unsafe {
2021
let toast_xml = ToastNotificationManager::get_template_content(ToastTemplateType::ToastText02).unwrap();
2122

2223
// Fill in the text elements
23-
let toast_text_elements = toast_xml.get_elements_by_tag_name(&FastHString::new("text")).unwrap();
24+
let toast_text_elements = toast_xml.get_elements_by_tag_name(&*hstr!("text")).unwrap();
2425

25-
toast_text_elements.item(0).unwrap().append_child(&*toast_xml.create_text_node(&FastHString::new("Hello from Rust!")).unwrap().query_interface::<IXmlNode>().unwrap()).unwrap();
26-
toast_text_elements.item(1).unwrap().append_child(&*toast_xml.create_text_node(&FastHString::new("This is some more text.")).unwrap().query_interface::<IXmlNode>().unwrap()).unwrap();
26+
toast_text_elements.item(0).unwrap().append_child(&*toast_xml.create_text_node(&*hstr!("Hello from Rust!")).unwrap().query_interface::<IXmlNode>().unwrap()).unwrap();
27+
toast_text_elements.item(1).unwrap().append_child(&*toast_xml.create_text_node(&*hstr!("This is some more text.")).unwrap().query_interface::<IXmlNode>().unwrap()).unwrap();
2728

2829
// could use the following to get the XML code for the notification
2930
//println!("{}", toast_xml.query_interface::<IXmlNodeSerializer>().unwrap().get_xml().unwrap());
@@ -32,5 +33,5 @@ fn run() { unsafe {
3233
let toast = ToastNotification::create_toast_notification(&*toast_xml).unwrap();
3334

3435
// Show the toast. Use PowerShell's App ID to circumvent the need to register one (this is only an example!).
35-
ToastNotificationManager::create_toast_notifier_with_id(&FastHString::new("{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\\WindowsPowerShell\\v1.0\\powershell.exe")).unwrap().show(&*toast).unwrap();
36+
ToastNotificationManager::create_toast_notifier_with_id(&*hstr!("{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\\WindowsPowerShell\\v1.0\\powershell.exe")).unwrap().show(&*toast).unwrap();
3637
}}

src/comptr.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ impl<T> ComPtr<T> {
8383
/// use winrt::windows::foundation::Uri;
8484
///
8585
/// # let rt = winrt::RuntimeContext::init();
86-
/// let uri = FastHString::new("https://www.rust-lang.org");
86+
/// let uri = hstr!("https://www.rust-lang.org");
8787
/// let uri = Uri::create_uri(&uri).unwrap();
8888
/// assert_eq!("Windows.Foundation.Uri", uri.get_runtime_class_name().to_string());
8989
/// ```

src/hstring.rs

+30-7
Original file line numberDiff line numberDiff line change
@@ -363,7 +363,7 @@ pub struct HString(HSTRING);
363363
impl HString {
364364
/// Creates a new `HString` whose memory is managed by the Windows Runtime.
365365
/// This allocates twice (once for the conversion to UTF-16, and again within `WindowsCreateString`),
366-
/// therefore this should not be used. Use `FastHString::new()` instead.
366+
/// therefore this should not be used. Use the `hstr!` macro or `FastHString::new()` instead.
367367
pub fn new<'a>(s: &'a str) -> HString {
368368
// Every UTF-8 byte results in either 1 or 2 UTF-16 bytes and we need one
369369
// more for the null terminator. This size expectation is correct in most cases,
@@ -583,6 +583,22 @@ mod tests {
583583
assert!(s == hstr.to_string());
584584
}
585585

586+
#[test]
587+
fn roundtrip_macro() {
588+
let hstr = hstr!("12345");
589+
assert!(hstr.len() as usize == "12345".len());
590+
assert!("12345" == hstr.to_string());
591+
}
592+
593+
#[test]
594+
fn roundtrip_utf16_internal_nul() {
595+
let s = "This is some\0test string\0with internal nuls";
596+
let utf16: Vec<_> = s.encode_utf16().chain(Some(0)).collect();
597+
let hstr = HStringReference::from_utf16(&utf16);
598+
assert!(hstr.len() as usize == s.len());
599+
assert!(s == hstr.to_string());
600+
}
601+
586602
#[test]
587603
fn make_reference() {
588604
let s1 = HString::new("AAA");
@@ -670,47 +686,54 @@ mod tests {
670686
fn bench_create(b: &mut Bencher) {
671687
let s = "123456789";
672688
b.iter(|| {
673-
let _ = HString::new(s);
689+
HString::new(s)
674690
});;
675691
}
676692

677693
#[bench]
678694
fn bench_create_fast(b: &mut Bencher) {
679695
let s = "123456789";
680696
b.iter(|| {
681-
let _ = FastHString::new(s);
697+
FastHString::new(s)
698+
});
699+
}
700+
701+
#[bench]
702+
fn bench_create_macro_literal(b: &mut Bencher) {
703+
b.iter(|| {
704+
hstr!("123456789")
682705
});
683706
}
684707

685708
#[bench]
686709
fn bench_make_reference(b: &mut Bencher) {
687710
let s = HString::new("123456789");
688711
b.iter(|| {
689-
let _ = s.make_reference();
712+
s.make_reference()
690713
});
691714
}
692715

693716
#[bench]
694717
fn bench_make_reference_fast(b: &mut Bencher) {
695718
let s = FastHString::new("123456789");
696719
b.iter(|| {
697-
let _ = s.make_reference();
720+
s.make_reference()
698721
});
699722
}
700723

701724
#[bench]
702725
fn bench_from_utf16(b: &mut Bencher) {
703726
let utf16: Vec<_> = "This is some test string".encode_utf16().chain(Some(0)).collect();
704727
b.iter(|| {
705-
let _ = HStringReference::from_utf16(&utf16);
728+
HStringReference::from_utf16(&utf16)
706729
});
707730
}
708731

709732
#[bench]
710733
fn bench_to_string(b: &mut Bencher) {
711734
let hstr = FastHString::new("123456789");
712735
b.iter(|| {
713-
let _ = hstr.to_string();
736+
hstr.to_string()
714737
});
715738
}
716739
}

src/lib.rs

+16
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,27 @@
2828

2929
#![allow(dead_code,non_upper_case_globals,non_snake_case)]
3030

31+
#[macro_use]
32+
extern crate wstr;
33+
pub use wstr::*;
34+
3135
extern crate winapi as w;
3236

3337
mod guid;
3438
pub use guid::Guid;
3539

40+
/// Creates an `HStringReference` from a string literal.
41+
/// This is the fastest and easiest way to pass `HString` literals
42+
/// to WinRT functions.
43+
#[macro_export]
44+
macro_rules! hstr {
45+
($str: tt) => {{
46+
let s = wstrz!($str);
47+
#[allow(unused_unsafe)]
48+
unsafe { HStringReference::from_utf16_unchecked(s) }
49+
}}
50+
}
51+
3652
///Represents the trust level of an activatable class (re-export from WinAPI crate)
3753
pub type TrustLevel = ::w::winrt::inspectable::TrustLevel;
3854

0 commit comments

Comments
 (0)