use anyhow::{Result, anyhow};
use std::ffi::c_void;
use windows_sys::Win32::Foundation::{CloseHandle, GetLastError, HANDLE, HLOCAL, LocalFree};
use windows_sys::Win32::Security::{
GetTokenInformation, SID_AND_ATTRIBUTES, TOKEN_QUERY,
};
use windows_sys::Win32::Security::Authorization::ConvertSidToStringSidW;
use windows_sys::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
pub fn to_wide(s: impl AsRef<std::ffi::OsStr>) -> Vec<u16> {
use std::os::windows::ffi::OsStrExt;
s.as_ref()
.encode_wide()
.chain(std::iter::once(0))
.collect()
}
#[repr(C)]
struct TokenUserStruct {
user: SID_AND_ATTRIBUTES,
}
const TOKEN_USER_CLASS: i32 = 1;
pub fn get_current_user_sid() -> Result<String> {
unsafe {
let mut token_handle: HANDLE = 0;
if OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &mut token_handle) == 0 {
return Err(anyhow!("OpenProcessToken 失败: {}", GetLastError()));
}
let mut buf_size: u32 = 0;
GetTokenInformation(
token_handle,
TOKEN_USER_CLASS,
std::ptr::null_mut(),
0,
&mut buf_size,
);
if buf_size == 0 {
CloseHandle(token_handle);
return Err(anyhow!("GetTokenInformation 查询大小失败: {}", GetLastError()));
}
let mut buf = vec![0u8; buf_size as usize];
if GetTokenInformation(
token_handle,
TOKEN_USER_CLASS,
buf.as_mut_ptr() as *mut c_void,
buf_size,
&mut buf_size,
) == 0
{
CloseHandle(token_handle);
return Err(anyhow!("GetTokenInformation 失败: {}", GetLastError()));
}
let token_user = &*(buf.as_ptr() as *const TokenUserStruct);
let user_sid = token_user.user.Sid;
let mut sid_str_ptr: *mut u16 = std::ptr::null_mut();
if ConvertSidToStringSidW(user_sid, &mut sid_str_ptr) == 0 {
CloseHandle(token_handle);
return Err(anyhow!("ConvertSidToStringSidW 失败: {}", GetLastError()));
}
let len = (0..).take_while(|&i| *sid_str_ptr.add(i) != 0).count();
let slice = std::slice::from_raw_parts(sid_str_ptr, len);
let result = String::from_utf16_lossy(slice);
LocalFree(sid_str_ptr as HLOCAL);
CloseHandle(token_handle);
Ok(result)
}
}
#[cfg(windows)]
pub fn decode_oem_string(bytes: &[u8]) -> Option<String> {
use windows_sys::Win32::Globalization::{GetOEMCP, MultiByteToWideChar};
unsafe {
let codepage = GetOEMCP();
if codepage == 0 {
return None;
}
if codepage == 65001 {
return None;
}
let needed = MultiByteToWideChar(
codepage,
0,
bytes.as_ptr(),
bytes.len() as i32,
std::ptr::null_mut(),
0,
);
if needed == 0 {
return None;
}
let mut buf = vec![0u16; needed as usize];
let written = MultiByteToWideChar(
codepage,
0,
bytes.as_ptr(),
bytes.len() as i32,
buf.as_mut_ptr(),
needed,
);
if written == 0 {
return None;
}
Some(String::from_utf16_lossy(&buf))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_to_wide_ascii() {
let wide = to_wide("hello");
assert_eq!(wide, &[104, 101, 108, 108, 111, 0]);
}
#[test]
fn test_to_wide_ends_with_null() {
let wide = to_wide("test");
assert!(wide.ends_with(&[0]));
assert_eq!(wide.len(), 5); }
#[test]
fn test_to_wide_empty_string() {
let wide = to_wide("");
assert_eq!(wide, &[0]);
}
#[test]
fn test_to_wide_unicode() {
let wide = to_wide("中文");
assert_eq!(wide, &[0x4E2D, 0x6587, 0]);
}
#[test]
fn test_to_wide_with_emoji() {
let wide = to_wide("a🌍b");
assert_eq!(wide[0], 0x0061); assert_eq!(wide[1], 0xD83C); assert_eq!(wide[2], 0xDF0D); assert_eq!(wide[3], 0x0062); assert_eq!(wide[4], 0); }
#[test]
fn test_to_wide_path_with_spaces() {
let wide = to_wide("C:\\Program Files\\test");
assert!(wide.contains(&0x0020)); assert!(wide.ends_with(&[0]));
}
#[test]
fn test_to_wide_preserves_case() {
let lower = to_wide("abc");
let upper = to_wide("ABC");
assert_ne!(lower, upper, "大小写应被保留");
}
#[test]
fn test_get_current_user_sid_works() -> Result<()> {
let sid = get_current_user_sid()?;
assert!(sid.starts_with("S-1-5-21-") || sid.starts_with("S-1-5-"), "用户 SID 格式无效: {sid}");
assert!(!sid.is_empty(), "SID 不应为空");
Ok(())
}
}