webview2_com/
pwstr.rs

1use std::{fmt::Display, marker::PhantomData, mem, ptr};
2
3use windows::{
4    core::{PCWSTR, PWSTR},
5    Win32::{Globalization::lstrlenW, System::Com},
6};
7
8/// RAII holder for a [`PWSTR`] which is allocated with [`Com::CoTaskMemAlloc`] and freed
9/// with [`Com::CoTaskMemFree`] when dropped.
10pub struct CoTaskMemPWSTR<'a>(PWSTR, PhantomData<&'a PWSTR>);
11
12/// Constant guard object tied to the lifetime of the [`CoTaskMemPWSTR`] so that it
13/// is safe to dereference the [`PCWSTR`] as long as both are still in scope.
14pub struct CoTaskMemRef<'a>(PCWSTR, PhantomData<&'a PCWSTR>);
15
16impl CoTaskMemRef<'_> {
17    pub fn as_pcwstr(&self) -> &PCWSTR {
18        &self.0
19    }
20}
21
22impl<'a> From<&'a CoTaskMemPWSTR<'a>> for CoTaskMemRef<'a> {
23    fn from(value: &'a CoTaskMemPWSTR<'a>) -> Self {
24        Self(PCWSTR::from_raw(value.0.as_ptr()), PhantomData)
25    }
26}
27
28/// Mutable guard object tied to the lifetime of the [`CoTaskMemPWSTR`] so that it
29/// is safe to dereference the [`PWSTR`] as long as both are still in scope.
30pub struct CoTaskMemMut<'a>(&'a PWSTR);
31
32impl<'a> CoTaskMemMut<'a> {
33    pub fn as_pwstr(&mut self) -> &'a PWSTR {
34        self.0
35    }
36}
37
38impl<'a> From<&'a mut CoTaskMemPWSTR<'a>> for CoTaskMemMut<'a> {
39    fn from(value: &'a mut CoTaskMemPWSTR<'a>) -> Self {
40        Self(&value.0)
41    }
42}
43
44impl<'a> CoTaskMemPWSTR<'a> {
45    /// Get a mutable [`PWSTR`] guard which borrows the pointer.
46    pub fn as_mut(&'a mut self) -> CoTaskMemMut<'a> {
47        From::from(self)
48    }
49
50    /// Get a constant [`PCWSTR`] guard which borrows the pointer.
51    pub fn as_ref(&'a self) -> CoTaskMemRef<'a> {
52        From::from(self)
53    }
54
55    /// Take the [`PWSTR`] pointer and hand off ownership so that it is not freed when the `CoTaskMemPWSTR` is dropped.
56    pub fn take(&mut self) -> PWSTR {
57        let result = self.0;
58        self.0 = PWSTR::null();
59        result
60    }
61}
62
63impl Drop for CoTaskMemPWSTR<'_> {
64    fn drop(&mut self) {
65        if !self.0.is_null() {
66            unsafe {
67                Com::CoTaskMemFree(Some(self.0.as_ptr() as *mut _ as *const _));
68            }
69        }
70    }
71}
72
73impl Default for CoTaskMemPWSTR<'_> {
74    fn default() -> Self {
75        Self(PWSTR::null(), PhantomData)
76    }
77}
78
79impl From<PWSTR> for CoTaskMemPWSTR<'_> {
80    fn from(value: PWSTR) -> Self {
81        Self(value, PhantomData)
82    }
83}
84
85impl From<&str> for CoTaskMemPWSTR<'_> {
86    fn from(value: &str) -> Self {
87        match value {
88            "" => Default::default(),
89            value => {
90                let encoded: Vec<_> = value.encode_utf16().chain(std::iter::once(0)).collect();
91
92                unsafe {
93                    let mut buffer =
94                        Com::CoTaskMemAlloc(encoded.len() * mem::size_of::<u16>()) as *mut u16;
95                    let result = PWSTR::from_raw(buffer);
96
97                    for char in encoded {
98                        *buffer = char;
99                        buffer = buffer.add(1);
100                    }
101
102                    Self(result, PhantomData)
103                }
104            }
105        }
106    }
107}
108
109impl Display for CoTaskMemPWSTR<'_> {
110    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
111        let value = string_from_pcwstr(self.as_ref().as_pcwstr());
112        f.write_str(value.as_str())
113    }
114}
115
116/// Copy a [`PCWSTR`] from an input param to a [`String`].
117pub fn string_from_pcwstr(source: &PCWSTR) -> String {
118    if source.0.is_null() {
119        String::new()
120    } else {
121        let len = unsafe { lstrlenW(*source) };
122
123        if len > 0 {
124            unsafe {
125                let buffer = ptr::slice_from_raw_parts(source.0, len as usize);
126                String::from_utf16_lossy(&*buffer)
127            }
128        } else {
129            String::new()
130        }
131    }
132}
133
134/// Copy a [`PWSTR`] allocated with [`Com::CoTaskMemAlloc`] from an input param to a [`String`]
135/// and free the original buffer with [`Com::CoTaskMemFree`].
136pub fn take_pwstr(source: PWSTR) -> String {
137    CoTaskMemPWSTR::from(source).to_string()
138}
139
140/// Allocate a [`PWSTR`] with [`Com::CoTaskMemAlloc`] and copy a [`&str`] into it.
141pub fn pwstr_from_str(source: &str) -> PWSTR {
142    CoTaskMemPWSTR::from(source).take()
143}