feat(windows): implement file extension and ProgId parsing structs
- Add Ext struct for handling file extensions with validation - Implement ProgId struct following Microsoft's suggested format - Create Clsid struct for handling CLSID with UUID parsing - Add ExpandString struct for environment variable expansion - Include comprehensive tests for new structs and parsing logic - Remove duplicate ExpandString implementation from windows.rs - Organize code regions for better readability and maintenance
This commit is contained in:
@ -236,11 +236,3 @@ impl Display for Clsid {
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region: Icon Resource
|
||||
|
||||
// endregion
|
||||
|
||||
// region: String Resource
|
||||
|
||||
// endregion
|
||||
|
||||
@ -9,9 +9,383 @@ use std::path::Path;
|
||||
use std::str::FromStr;
|
||||
use std::sync::LazyLock;
|
||||
use thiserror::Error as TeError;
|
||||
use uuid::Uuid;
|
||||
use widestring::{WideCString, WideChar, WideStr};
|
||||
use windows_sys::Win32::UI::WindowsAndMessaging::HICON;
|
||||
|
||||
// region: File Extension
|
||||
|
||||
/// The error occurs when constructing Ext with bad body.
|
||||
#[derive(Debug, TeError)]
|
||||
#[error("given file extension body \"{inner}\" is invalid")]
|
||||
pub struct BadExtBodyError {
|
||||
/// The clone of string which is not a valid file extension body.
|
||||
inner: String,
|
||||
}
|
||||
|
||||
impl BadExtBodyError {
|
||||
/// Create new error instance.
|
||||
fn new(inner: &str) -> Self {
|
||||
Self {
|
||||
inner: inner.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The struct representing an file extension which must start with dot (`.`)
|
||||
/// and followed by at least one arbitrary characters.
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Ext {
|
||||
/// The body of file extension (excluding dot).
|
||||
body: String,
|
||||
}
|
||||
|
||||
impl Ext {
|
||||
/// Create an new file extension.
|
||||
///
|
||||
/// `body` is the body of file extension (excluding dot).
|
||||
/// If you want to create this struct with ordinary extension string like `.jpg`,
|
||||
/// please use `from_str()` instead.
|
||||
pub fn new(body: &str) -> Result<Self, BadExtBodyError> {
|
||||
// Check whether given body has dot or empty
|
||||
if body.is_empty() || body.contains('.') {
|
||||
Err(BadExtBodyError::new(body))
|
||||
} else {
|
||||
Ok(Self {
|
||||
body: body.to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the body part of file extension (excluding dot)
|
||||
pub fn inner(&self) -> &str {
|
||||
&self.body
|
||||
}
|
||||
}
|
||||
|
||||
/// The error occurs when try parsing string into FileExt.
|
||||
#[derive(Debug, TeError)]
|
||||
#[error("given file extension name \"{inner}\" is invalid")]
|
||||
pub struct ParseExtError {
|
||||
/// The clone of string which is not a valid file extension.
|
||||
inner: String,
|
||||
}
|
||||
|
||||
impl ParseExtError {
|
||||
/// Create new error instance.
|
||||
fn new(inner: &str) -> Self {
|
||||
Self {
|
||||
inner: inner.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Ext {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, ".{}", self.body)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Ext {
|
||||
type Err = ParseExtError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
static RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"^\.([^\.]+)$").unwrap());
|
||||
match RE.captures(s) {
|
||||
Some(v) => Ok(Self::new(&v[1]).expect("unexpected dot in Ext body")),
|
||||
None => Err(ParseExtError::new(s)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region: Programmatic Identifiers (ProgId)
|
||||
|
||||
/// The error occurs when constructing ProgId.
|
||||
#[derive(Debug, TeError)]
|
||||
#[error("given ProgId part \"{inner}\" is invalid")]
|
||||
pub struct BadProgIdPartError {
|
||||
/// The clone of string which is not a valid ProgId part.
|
||||
inner: String,
|
||||
}
|
||||
|
||||
impl BadProgIdPartError {
|
||||
/// Create new error instance.
|
||||
fn new(s: &str) -> Self {
|
||||
Self {
|
||||
inner: s.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// The ProgId exactly follows Microsoft suggested
|
||||
/// `[Vendor or Application].[Component].[Version]` format.
|
||||
///
|
||||
/// However, most of applications do no follow this standard,
|
||||
/// this scenario is not convered by this struct in there.
|
||||
/// It should be done in other place.
|
||||
/// Additionally, `[Version]` part is optional.
|
||||
///
|
||||
/// Reference:
|
||||
/// - https://learn.microsoft.com/en-us/windows/win32/shell/fa-progids
|
||||
/// - https://learn.microsoft.com/en-us/windows/win32/com/-progid--key
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ProgId {
|
||||
/// The vendor part of ProgId.
|
||||
vendor: String,
|
||||
/// The component part of ProgId.
|
||||
component: String,
|
||||
/// The optional version part of ProgId.
|
||||
version: Option<u32>,
|
||||
}
|
||||
|
||||
impl ProgId {
|
||||
/// Create a new ProgId with given parts.
|
||||
pub fn new(
|
||||
vendor: &str,
|
||||
component: &str,
|
||||
version: Option<u32>,
|
||||
) -> Result<Self, BadProgIdPartError> {
|
||||
// Check whether vendor or component part is empty or has dot
|
||||
if vendor.is_empty() || vendor.contains('.') {
|
||||
Err(BadProgIdPartError::new(vendor))
|
||||
} else if component.is_empty() || component.contains('.') {
|
||||
Err(BadProgIdPartError::new(component))
|
||||
} else {
|
||||
Ok(Self {
|
||||
vendor: vendor.to_string(),
|
||||
component: component.to_string(),
|
||||
version,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the vendor part of standard ProgId.
|
||||
pub fn get_vendor(&self) -> &str {
|
||||
&self.vendor
|
||||
}
|
||||
|
||||
/// Get the component part of standard ProgId.
|
||||
pub fn get_component(&self) -> &str {
|
||||
&self.component
|
||||
}
|
||||
|
||||
/// Get the version part of standard ProgId.
|
||||
pub fn get_version(&self) -> Option<u32> {
|
||||
self.version
|
||||
}
|
||||
}
|
||||
|
||||
/// The error occurs when parsing ProgId.
|
||||
#[derive(Debug, TeError)]
|
||||
#[error("given ProgId \"{inner}\" is invalid")]
|
||||
pub struct ParseProgIdError {
|
||||
/// The clone of string which is not a valid ProgId.
|
||||
inner: String,
|
||||
}
|
||||
|
||||
impl ParseProgIdError {
|
||||
/// Create new error instance.
|
||||
fn new(s: &str) -> Self {
|
||||
Self {
|
||||
inner: s.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ProgId {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match &self.version {
|
||||
Some(version) => write!(f, "{}.{}.{}", self.vendor, self.component, version),
|
||||
None => write!(f, "{}.{}", self.vendor, self.component),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ProgId {
|
||||
type Err = ParseProgIdError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
static RE: LazyLock<Regex> =
|
||||
LazyLock::new(|| Regex::new(r"^([^\.]+)\.([^\.]+)(\.([0-9]+))?$").unwrap());
|
||||
let caps = RE.captures(s);
|
||||
if let Some(caps) = caps {
|
||||
let vendor = &caps[1];
|
||||
let component = &caps[2];
|
||||
let version = match caps.get(4) {
|
||||
Some(sv) => Some(
|
||||
sv.as_str()
|
||||
.parse::<u32>()
|
||||
.map_err(|_| ParseProgIdError::new(s))?,
|
||||
),
|
||||
None => None,
|
||||
};
|
||||
Ok(Self::new(vendor, component, version).expect("unexpected bad part of ProgId"))
|
||||
} else {
|
||||
Err(ParseProgIdError::new(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region: CLSID
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Clsid {
|
||||
inner: Uuid,
|
||||
}
|
||||
|
||||
impl Clsid {
|
||||
pub fn new(uuid: &str) -> Result<Self, ParseClsidError> {
|
||||
Self::from_str(uuid)
|
||||
}
|
||||
|
||||
// TODO: May add CLSID generator in there.
|
||||
}
|
||||
|
||||
/// The error occurs when parsing CLSID
|
||||
#[derive(Debug, TeError)]
|
||||
#[error("given string \"{inner}\" is invalid for uuid")]
|
||||
pub struct ParseClsidError {
|
||||
inner: String,
|
||||
}
|
||||
|
||||
impl ParseClsidError {
|
||||
fn new(s: &str) -> Self {
|
||||
Self {
|
||||
inner: s.to_string(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Clsid {
|
||||
type Err = ParseClsidError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(Self {
|
||||
inner: Uuid::parse_str(s).map_err(|_| ParseClsidError::new(s))?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Clsid {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.inner.braced().to_string())
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region: Expand String
|
||||
|
||||
/// Error occurs when creating Expand String.
|
||||
#[derive(Debug, TeError)]
|
||||
#[error("given string is not an expand string")]
|
||||
pub struct ParseExpandStrError {}
|
||||
|
||||
impl ParseExpandStrError {
|
||||
fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurs when expand Expand String
|
||||
#[derive(Debug, TeError)]
|
||||
#[error("error occurs when expanding expand string")]
|
||||
pub enum ExpandEnvVarError {
|
||||
/// Given string has embedded NUL.
|
||||
EmbeddedNul(#[from] widestring::error::ContainsNul<WideChar>),
|
||||
/// The encoding of string is invalid.
|
||||
BadEncoding(#[from] widestring::error::Utf16Error),
|
||||
/// Error occurs when int type casting.
|
||||
BadIntCast(#[from] std::num::TryFromIntError),
|
||||
/// Integeral arithmatic downflow.
|
||||
Underflow,
|
||||
/// Error occurs when executing Win32 expand function.
|
||||
ExpandFunction,
|
||||
/// Some environment vairable are not expanded.
|
||||
NoEnvVar,
|
||||
}
|
||||
|
||||
/// The struct representing an Expand String,
|
||||
/// which contain environment variable in string,
|
||||
/// like `%LOCALAPPDATA%\SomeApp.exe`.
|
||||
pub struct ExpandString {
|
||||
inner: String,
|
||||
}
|
||||
|
||||
impl ExpandString {
|
||||
const VAR_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"%[a-zA-Z0-9_]+%").unwrap());
|
||||
}
|
||||
|
||||
impl ExpandString {
|
||||
/// Create a new expand string
|
||||
pub fn new(s: &str) -> Result<Self, ParseExpandStrError> {
|
||||
Self::from_str(s)
|
||||
}
|
||||
|
||||
/// Expand the variables located in this string
|
||||
/// and produce the final usable string.
|
||||
pub fn expand_string(&self) -> Result<String, ExpandEnvVarError> {
|
||||
use windows_sys::Win32::System::Environment::ExpandEnvironmentStringsW;
|
||||
|
||||
// Fetch the size of expand result
|
||||
let source = WideCString::from_str(self.inner.as_str())?;
|
||||
let size = unsafe { ExpandEnvironmentStringsW(source.as_ptr(), std::ptr::null_mut(), 0) };
|
||||
if size == 0 {
|
||||
return Err(ExpandEnvVarError::ExpandFunction);
|
||||
}
|
||||
let size_no_nul = size.checked_sub(1).ok_or(ExpandEnvVarError::Underflow)?;
|
||||
|
||||
// Allocate buffer for it.
|
||||
let len: usize = size.try_into()?;
|
||||
let len_no_nul = len.checked_sub(1).ok_or(ExpandEnvVarError::Underflow)?;
|
||||
let mut buffer = vec![0; len];
|
||||
// Receive result
|
||||
let size =
|
||||
unsafe { ExpandEnvironmentStringsW(source.as_ptr(), buffer.as_mut_ptr(), size_no_nul) };
|
||||
if size == 0 {
|
||||
return Err(ExpandEnvVarError::ExpandFunction);
|
||||
}
|
||||
|
||||
// Cast result as Rust string
|
||||
let wstr = unsafe { WideStr::from_ptr(buffer.as_ptr(), len_no_nul) };
|
||||
let rv = wstr.to_string()?;
|
||||
|
||||
// If the final string still has environment variable,
|
||||
// we think we fail to expand it.
|
||||
if Self::VAR_RE.is_match(rv.as_str()) {
|
||||
Err(ExpandEnvVarError::NoEnvVar)
|
||||
} else {
|
||||
Ok(rv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ExpandString {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ExpandString {
|
||||
type Err = ParseExpandStrError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if Self::VAR_RE.is_match(s) {
|
||||
Ok(Self {
|
||||
inner: s.to_string(),
|
||||
})
|
||||
} else {
|
||||
Err(ParseExpandStrError::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region: Windows Resource Reference String
|
||||
|
||||
// region: Icon Reference String
|
||||
@ -359,114 +733,6 @@ impl StrRc {
|
||||
|
||||
// endregion
|
||||
|
||||
// region: Expand String
|
||||
|
||||
/// Error occurs when creating Expand String.
|
||||
#[derive(Debug, TeError)]
|
||||
#[error("given string is not an expand string")]
|
||||
pub struct ParseExpandStrError {}
|
||||
|
||||
impl ParseExpandStrError {
|
||||
fn new() -> Self {
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Error occurs when expand Expand String
|
||||
#[derive(Debug, TeError)]
|
||||
#[error("error occurs when expanding expand string")]
|
||||
pub enum ExpandEnvVarError {
|
||||
/// Given string has embedded NUL.
|
||||
EmbeddedNul(#[from] widestring::error::ContainsNul<WideChar>),
|
||||
/// The encoding of string is invalid.
|
||||
BadEncoding(#[from] widestring::error::Utf16Error),
|
||||
/// Error occurs when int type casting.
|
||||
BadIntCast(#[from] std::num::TryFromIntError),
|
||||
/// Integeral arithmatic downflow.
|
||||
Underflow,
|
||||
/// Error occurs when executing Win32 expand function.
|
||||
ExpandFunction,
|
||||
/// Some environment vairable are not expanded.
|
||||
NoEnvVar,
|
||||
}
|
||||
|
||||
/// The struct representing an Expand String,
|
||||
/// which contain environment variable in string,
|
||||
/// like `%LOCALAPPDATA%\SomeApp.exe`.
|
||||
pub struct ExpandString {
|
||||
inner: String,
|
||||
}
|
||||
|
||||
impl ExpandString {
|
||||
const VAR_RE: LazyLock<Regex> = LazyLock::new(|| Regex::new(r"%[a-zA-Z0-9_]+%").unwrap());
|
||||
}
|
||||
|
||||
impl ExpandString {
|
||||
/// Create a new expand string
|
||||
pub fn new(s: &str) -> Result<Self, ParseExpandStrError> {
|
||||
Self::from_str(s)
|
||||
}
|
||||
|
||||
/// Expand the variables located in this string
|
||||
/// and produce the final usable string.
|
||||
pub fn expand_string(&self) -> Result<String, ExpandEnvVarError> {
|
||||
use windows_sys::Win32::System::Environment::ExpandEnvironmentStringsW;
|
||||
|
||||
// Fetch the size of expand result
|
||||
let source = WideCString::from_str(self.inner.as_str())?;
|
||||
let size = unsafe { ExpandEnvironmentStringsW(source.as_ptr(), std::ptr::null_mut(), 0) };
|
||||
if size == 0 {
|
||||
return Err(ExpandEnvVarError::ExpandFunction);
|
||||
}
|
||||
let size_no_nul = size.checked_sub(1).ok_or(ExpandEnvVarError::Underflow)?;
|
||||
|
||||
// Allocate buffer for it.
|
||||
let len: usize = size.try_into()?;
|
||||
let len_no_nul = len.checked_sub(1).ok_or(ExpandEnvVarError::Underflow)?;
|
||||
let mut buffer = vec![0; len];
|
||||
// Receive result
|
||||
let size =
|
||||
unsafe { ExpandEnvironmentStringsW(source.as_ptr(), buffer.as_mut_ptr(), size_no_nul) };
|
||||
if size == 0 {
|
||||
return Err(ExpandEnvVarError::ExpandFunction);
|
||||
}
|
||||
|
||||
// Cast result as Rust string
|
||||
let wstr = unsafe { WideStr::from_ptr(buffer.as_ptr(), len_no_nul) };
|
||||
let rv = wstr.to_string()?;
|
||||
|
||||
// If the final string still has environment variable,
|
||||
// we think we fail to expand it.
|
||||
if Self::VAR_RE.is_match(rv.as_str()) {
|
||||
Err(ExpandEnvVarError::NoEnvVar)
|
||||
} else {
|
||||
Ok(rv)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for ExpandString {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for ExpandString {
|
||||
type Err = ParseExpandStrError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
if Self::VAR_RE.is_match(s) {
|
||||
Ok(Self {
|
||||
inner: s.to_string(),
|
||||
})
|
||||
} else {
|
||||
Err(ParseExpandStrError::new())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// endregion
|
||||
|
||||
// region: Windows Commandline
|
||||
|
||||
// region: Cmd Lexer
|
||||
|
||||
@ -1,6 +1,90 @@
|
||||
use std::path::Path;
|
||||
use std::{path::Path, str::FromStr};
|
||||
use wfassoc::extra::windows::*;
|
||||
|
||||
#[test]
|
||||
fn test_ex_new() {
|
||||
fn ok_tester(s: &str, probe: &str) {
|
||||
let rv = Ext::new(s);
|
||||
assert!(rv.is_ok());
|
||||
let rv = rv.unwrap();
|
||||
assert_eq!(rv.to_string(), probe);
|
||||
}
|
||||
fn err_tester(s: &str) {
|
||||
let rv = Ext::new(s);
|
||||
assert!(rv.is_err());
|
||||
}
|
||||
|
||||
ok_tester("jpg", ".jpg");
|
||||
err_tester(".jpg");
|
||||
err_tester("");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ext_parse() {
|
||||
fn ok_tester(s: &str, probe: &str) {
|
||||
let rv = Ext::from_str(s);
|
||||
assert!(rv.is_ok());
|
||||
let rv = rv.unwrap();
|
||||
assert_eq!(rv.inner(), probe);
|
||||
}
|
||||
fn err_tester(s: &str) {
|
||||
let rv = Ext::from_str(s);
|
||||
assert!(rv.is_err());
|
||||
}
|
||||
|
||||
ok_tester(".jpg", "jpg");
|
||||
err_tester(".jar.disabled");
|
||||
err_tester("jar");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prog_id_new() {
|
||||
fn ok_tester(vendor: &str, component: &str, version: Option<u32>, probe: &str) {
|
||||
let rv = ProgId::new(vendor, component, version);
|
||||
assert!(rv.is_ok());
|
||||
let rv = rv.unwrap();
|
||||
assert_eq!(rv.to_string(), probe);
|
||||
}
|
||||
fn err_tester(vendor: &str, component: &str, version: Option<u32>) {
|
||||
let rv = ProgId::new(vendor, component, version);
|
||||
assert!(rv.is_err());
|
||||
}
|
||||
|
||||
ok_tester("PowerPoint", "Template", Some(12), "PowerPoint.Template.12");
|
||||
err_tester("", "MyApp", None);
|
||||
err_tester("Me", "", None);
|
||||
err_tester("M.e", "MyApp", None);
|
||||
err_tester("Me", "My.App", None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_prog_id_parse() {
|
||||
fn ok_tester(s: &str, probe_vendor: &str, probe_component: &str, probe_version: Option<u32>) {
|
||||
let rv = ProgId::from_str(s);
|
||||
assert!(rv.is_ok());
|
||||
let rv =rv.unwrap();
|
||||
assert_eq!(rv.get_vendor(), probe_vendor);
|
||||
assert_eq!(rv.get_component(), probe_component);
|
||||
assert_eq!(rv.get_version(), probe_version);
|
||||
}
|
||||
fn err_tester(s: &str) {
|
||||
let rv = ProgId::from_str(s);
|
||||
assert!(rv.is_err());
|
||||
}
|
||||
|
||||
ok_tester("VSCode.c++", "VSCode", "c++", None);
|
||||
ok_tester("PowerPoint.Template.12", "PowerPoint", "Template", Some(12));
|
||||
err_tester("Me.MyApp.");
|
||||
err_tester("WMP11.AssocFile.3G2");
|
||||
err_tester("What the f*ck?");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_clsid() {
|
||||
fn ok_tester(s: &str) {}
|
||||
fn err_tester(s: &str) {}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_icon_ref_str() {
|
||||
fn ok_tester(s: &str, probe: (&str, u32)) {
|
||||
@ -15,7 +99,10 @@ fn test_icon_ref_str() {
|
||||
assert!(rv.is_err());
|
||||
}
|
||||
|
||||
ok_tester(r#"%SystemRoot%\System32\imageres.dll,-72"#, (r#"%SystemRoot%\System32\imageres.dll"#, 72));
|
||||
ok_tester(
|
||||
r#"%SystemRoot%\System32\imageres.dll,-72"#,
|
||||
(r#"%SystemRoot%\System32\imageres.dll"#, 72),
|
||||
);
|
||||
err_tester(r#"C:\Windows\Cursors\aero_arrow.cur"#);
|
||||
err_tester(r#"@%SystemRoot%\System32\shell32.dll,-30596"#);
|
||||
}
|
||||
@ -34,7 +121,10 @@ fn test_str_ref_str() {
|
||||
assert!(rv.is_err());
|
||||
}
|
||||
|
||||
ok_tester(r#"@%SystemRoot%\System32\shell32.dll,-30596"#, (r#"%SystemRoot%\System32\shell32.dll"#, 30596));
|
||||
ok_tester(
|
||||
r#"@%SystemRoot%\System32\shell32.dll,-30596"#,
|
||||
(r#"%SystemRoot%\System32\shell32.dll"#, 30596),
|
||||
);
|
||||
err_tester(r#"This is my application, OK?"#);
|
||||
err_tester(r#"%SystemRoot%\System32\imageres.dll,-72"#);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user