Skip to content

Provide hstr! macro #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ exclude = ["Generator/**"]

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

[features]
nightly = []
Expand Down
6 changes: 0 additions & 6 deletions Generator/NameHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,12 +46,6 @@ public static string CamelToSnakeCase(string name)
return newName.ToString();
}

// Returns the string as an UTF16 encoded, null-terminated sequence of u16 values
public static string StringToUTF16WithZero(string str)
{
return String.Join(",", str.Select(c => ((ushort)c).ToString()).Concat(new string[] { "0" }));
}

public static Tuple<string, int> GetSortKeyIgnoringInterfacePrefix(string str)
{
if (str[0] == 'I' && char.IsUpper(str[1]))
Expand Down
2 changes: 1 addition & 1 deletion Generator/Types/ClassDef.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,7 @@ public override void Emit()
if (needClassID)
{
Module.Append($@"
DEFINE_CLSID!({ classType }(&[{ NameHelpers.StringToUTF16WithZero(Type.FullName) }]) [CLSID_{ classType }]);");
DEFINE_CLSID!({ classType }: ""{ Type.FullName }"");");
}
}
}
Expand Down
12 changes: 6 additions & 6 deletions examples/test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ fn main() {
}

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

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

Expand Down
9 changes: 5 additions & 4 deletions examples/toast_notify.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Use the following command to run this example:
// > cargo run --example toast_notify --features "windows-data windows-ui"

#[macro_use]
extern crate winrt;

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

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

toast_text_elements.item(0).unwrap().append_child(&*toast_xml.create_text_node(&FastHString::new("Hello from Rust!")).unwrap().query_interface::<IXmlNode>().unwrap()).unwrap();
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();
toast_text_elements.item(0).unwrap().append_child(&*toast_xml.create_text_node(&*hstr!("Hello from Rust!")).unwrap().query_interface::<IXmlNode>().unwrap()).unwrap();
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();

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

// Show the toast. Use PowerShell's App ID to circumvent the need to register one (this is only an example!).
ToastNotificationManager::create_toast_notifier_with_id(&FastHString::new("{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\\WindowsPowerShell\\v1.0\\powershell.exe")).unwrap().show(&*toast).unwrap();
ToastNotificationManager::create_toast_notifier_with_id(&*hstr!("{1AC14E77-02E7-4E5D-B744-2EB1AE5198B7}\\WindowsPowerShell\\v1.0\\powershell.exe")).unwrap().show(&*toast).unwrap();
}}
2 changes: 1 addition & 1 deletion src/comptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ impl<T> ComPtr<T> {
/// use winrt::windows::foundation::Uri;
///
/// # let rt = winrt::RuntimeContext::init();
/// let uri = FastHString::new("https://www.rust-lang.org");
/// let uri = hstr!("https://www.rust-lang.org");
/// let uri = Uri::create_uri(&uri).unwrap();
/// assert_eq!("Windows.Foundation.Uri", uri.get_runtime_class_name().to_string());
/// ```
Expand Down
37 changes: 30 additions & 7 deletions src/hstring.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,7 +363,7 @@ pub struct HString(HSTRING);
impl HString {
/// Creates a new `HString` whose memory is managed by the Windows Runtime.
/// This allocates twice (once for the conversion to UTF-16, and again within `WindowsCreateString`),
/// therefore this should not be used. Use `FastHString::new()` instead.
/// therefore this should not be used. Use the `hstr!` macro or `FastHString::new()` instead.
pub fn new<'a>(s: &'a str) -> HString {
// Every UTF-8 byte results in either 1 or 2 UTF-16 bytes and we need one
// more for the null terminator. This size expectation is correct in most cases,
Expand Down Expand Up @@ -583,6 +583,22 @@ mod tests {
assert!(s == hstr.to_string());
}

#[test]
fn roundtrip_macro() {
let hstr = hstr!("12345");
assert!(hstr.len() as usize == "12345".len());
assert!("12345" == hstr.to_string());
}

#[test]
fn roundtrip_utf16_internal_nul() {
let s = "This is some\0test string\0with internal nuls";
let utf16: Vec<_> = s.encode_utf16().chain(Some(0)).collect();
let hstr = HStringReference::from_utf16(&utf16);
assert!(hstr.len() as usize == s.len());
assert!(s == hstr.to_string());
}

#[test]
fn make_reference() {
let s1 = HString::new("AAA");
Expand Down Expand Up @@ -670,47 +686,54 @@ mod tests {
fn bench_create(b: &mut Bencher) {
let s = "123456789";
b.iter(|| {
let _ = HString::new(s);
HString::new(s)
});;
}

#[bench]
fn bench_create_fast(b: &mut Bencher) {
let s = "123456789";
b.iter(|| {
let _ = FastHString::new(s);
FastHString::new(s)
});
}

#[bench]
fn bench_create_macro_literal(b: &mut Bencher) {
b.iter(|| {
hstr!("123456789")
});
}

#[bench]
fn bench_make_reference(b: &mut Bencher) {
let s = HString::new("123456789");
b.iter(|| {
let _ = s.make_reference();
s.make_reference()
});
}

#[bench]
fn bench_make_reference_fast(b: &mut Bencher) {
let s = FastHString::new("123456789");
b.iter(|| {
let _ = s.make_reference();
s.make_reference()
});
}

#[bench]
fn bench_from_utf16(b: &mut Bencher) {
let utf16: Vec<_> = "This is some test string".encode_utf16().chain(Some(0)).collect();
b.iter(|| {
let _ = HStringReference::from_utf16(&utf16);
HStringReference::from_utf16(&utf16)
});
}

#[bench]
fn bench_to_string(b: &mut Bencher) {
let hstr = FastHString::new("123456789");
b.iter(|| {
let _ = hstr.to_string();
hstr.to_string()
});
}
}
16 changes: 16 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,27 @@

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

#[macro_use]
extern crate wstr;
pub use wstr::*;

extern crate winapi as w;

mod guid;
pub use guid::Guid;

/// Creates an `HStringReference` from a string literal.
/// This is the fastest and easiest way to pass `HString` literals
/// to WinRT functions.
#[macro_export]
macro_rules! hstr {
($str: tt) => {{
let s = wstrz!($str);
#[allow(unused_unsafe)]
unsafe { HStringReference::from_utf16_unchecked(s) }
}}
}

///Represents the trust level of an activatable class (re-export from WinAPI crate)
pub type TrustLevel = ::w::winrt::inspectable::TrustLevel;

Expand Down
Loading