Skip to content

Commit a662573

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 881ec77 commit a662573

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
@@ -17,6 +17,7 @@ winapi = "0.2.8"
1717
runtimeobject-sys = "0.2.0"
1818
ole32-sys = "0.2.0"
1919
oleaut32-sys = "0.2.0"
20+
wstr = "0.2"
2021

2122
[features]
2223
nightly = []

examples/test.rs

+6-6
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ fn main() {
1717
}
1818

1919
fn run() {
20-
let base = FastHString::new("https://github.com");
21-
let relative = FastHString::new("contextfree/winrt-rust");
20+
let base = hstr!("https://github.com");
21+
let relative = hstr!("contextfree/winrt-rust");
2222
let uri = Uri::create_with_relative_uri(&base, &relative).unwrap();
2323
let to_string = unsafe { uri.query_interface::<IStringable>().unwrap().to_string().unwrap() };
2424
println!("{} -> {}", uri.get_runtime_class_name(), to_string);
@@ -128,17 +128,17 @@ fn run() {
128128
println!("{:?} = {:?}", array, &returned_array[..]);
129129
assert_eq!(array, &returned_array[..]);
130130

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

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
@@ -77,7 +77,7 @@ impl<T> ComPtr<T> {
7777
/// use winrt::windows::foundation::Uri;
7878
///
7979
/// # let rt = winrt::RuntimeContext::init();
80-
/// let uri = FastHString::new("https://www.rust-lang.org");
80+
/// let uri = hstr!("https://www.rust-lang.org");
8181
/// let uri = Uri::create_uri(&uri).unwrap();
8282
/// assert_eq!("Windows.Foundation.Uri", uri.get_runtime_class_name().to_string());
8383
/// ```

src/hstring.rs

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

592+
#[test]
593+
fn roundtrip_macro() {
594+
let hstr = hstr!("12345");
595+
assert!(hstr.len() as usize == "12345".len());
596+
assert!("12345" == hstr.to_string());
597+
}
598+
599+
#[test]
600+
fn roundtrip_utf16_internal_nul() {
601+
let s = "This is some\0test string\0with internal nuls";
602+
let utf16: Vec<_> = s.encode_utf16().chain(Some(0)).collect();
603+
let hstr = HStringReference::from_utf16(&utf16);
604+
assert!(hstr.len() as usize == s.len());
605+
assert!(s == hstr.to_string());
606+
}
607+
592608
#[test]
593609
fn make_reference() {
594610
let s1 = HString::new("AAA");
@@ -676,47 +692,54 @@ mod tests {
676692
fn bench_create(b: &mut Bencher) {
677693
let s = "123456789";
678694
b.iter(|| {
679-
let _ = HString::new(s);
695+
HString::new(s)
680696
});;
681697
}
682698

683699
#[bench]
684700
fn bench_create_fast(b: &mut Bencher) {
685701
let s = "123456789";
686702
b.iter(|| {
687-
let _ = FastHString::new(s);
703+
FastHString::new(s)
704+
});
705+
}
706+
707+
#[bench]
708+
fn bench_create_macro_literal(b: &mut Bencher) {
709+
b.iter(|| {
710+
hstr!("123456789")
688711
});
689712
}
690713

691714
#[bench]
692715
fn bench_make_reference(b: &mut Bencher) {
693716
let s = HString::new("123456789");
694717
b.iter(|| {
695-
let _ = s.make_reference();
718+
s.make_reference()
696719
});
697720
}
698721

699722
#[bench]
700723
fn bench_make_reference_fast(b: &mut Bencher) {
701724
let s = FastHString::new("123456789");
702725
b.iter(|| {
703-
let _ = s.make_reference();
726+
s.make_reference()
704727
});
705728
}
706729

707730
#[bench]
708731
fn bench_from_utf16(b: &mut Bencher) {
709732
let utf16: Vec<_> = "This is some test string".encode_utf16().chain(Some(0)).collect();
710733
b.iter(|| {
711-
let _ = HStringReference::from_utf16(&utf16);
734+
HStringReference::from_utf16(&utf16)
712735
});
713736
}
714737

715738
#[bench]
716739
fn bench_to_string(b: &mut Bencher) {
717740
let hstr = FastHString::new("123456789");
718741
b.iter(|| {
719-
let _ = hstr.to_string();
742+
hstr.to_string()
720743
});
721744
}
722745
}

src/lib.rs

+16
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@
2929

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

32+
#[macro_use]
33+
extern crate wstr;
34+
pub use wstr::*;
35+
3236
extern crate winapi as w;
3337
extern crate runtimeobject;
3438
extern crate ole32;
@@ -37,6 +41,18 @@ extern crate oleaut32;
3741
mod guid;
3842
pub use guid::Guid;
3943

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

0 commit comments

Comments
 (0)