use std::io::Error;
use std::mem;
use windows::{
core::{HSTRING, PCWSTR},
Win32::{
Foundation::HANDLE,
Security::{
AdjustTokenPrivileges, LookupPrivilegeValueW, SE_PRIVILEGE_ENABLED, SE_SHUTDOWN_NAME,
TOKEN_ADJUST_PRIVILEGES, TOKEN_PRIVILEGES, TOKEN_QUERY,
},
System::{
Power::SetSuspendState,
Shutdown::{
ExitWindowsEx, InitiateSystemShutdownW, EWX_LOGOFF, EWX_REBOOT, EWX_SHUTDOWN,
EXIT_WINDOWS_FLAGS, SHTDN_REASON_FLAG_PLANNED, SHTDN_REASON_MAJOR_OPERATINGSYSTEM,
SHTDN_REASON_MINOR_UPGRADE, EWX_FORCE, EWX_FORCEIFHUNG
},
Threading::{GetCurrentProcess, OpenProcessToken},
}
},
};
use super::ShutdownResult;
#[doc(hidden)]
#[macro_export]
macro_rules! last_os_error {
() => {
Err(Error::last_os_error())
};
}
fn request_privileges() -> ShutdownResult {
unsafe {
let mut token: HANDLE = HANDLE::default();
let mut tkp: TOKEN_PRIVILEGES = mem::zeroed();
if !OpenProcessToken(
GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
&mut token,
)
.is_ok()
{
return last_os_error!();
}
if !LookupPrivilegeValueW(
PCWSTR::null(),
SE_SHUTDOWN_NAME,
&mut tkp.Privileges[0].Luid,
)
.is_ok()
{
return last_os_error!();
}
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if !AdjustTokenPrivileges(token, false, Some(&tkp), 0, None, None).is_ok() {
return last_os_error!();
}
}
Ok(())
}
fn exit_windows(flag: u32) -> ShutdownResult {
unsafe {
request_privileges()?;
if !ExitWindowsEx(
EXIT_WINDOWS_FLAGS(flag | EWX_FORCEIFHUNG.0),
SHTDN_REASON_MAJOR_OPERATINGSYSTEM
| SHTDN_REASON_MINOR_UPGRADE
| SHTDN_REASON_FLAG_PLANNED,
)
.is_ok()
{
return last_os_error!();
}
}
Ok(())
}
fn initiate_system_shutdown(
message: &str,
timeout: u32,
force_close_apps: bool,
restart: bool,
) -> ShutdownResult {
unsafe {
request_privileges()?;
if !InitiateSystemShutdownW(
PCWSTR::null(),
&HSTRING::from(message),
timeout,
force_close_apps,
restart,
)
.is_ok()
{
return last_os_error!();
}
}
Ok(())
}
fn set_suspend_state(hibernate: bool) -> ShutdownResult {
unsafe {
request_privileges()?;
if !SetSuspendState(hibernate, false, false) {
return last_os_error!();
}
}
Ok(())
}
pub fn shutdown_with_message(
message: &str,
timeout: u32,
force_close_apps: bool,
) -> ShutdownResult {
initiate_system_shutdown(message, timeout, force_close_apps, false)
}
pub fn reboot_with_message(message: &str, timeout: u32, force_close_apps: bool) -> ShutdownResult {
initiate_system_shutdown(message, timeout, force_close_apps, true)
}
pub fn shutdown() -> ShutdownResult {
exit_windows(EWX_SHUTDOWN.0)
}
pub fn force_shutdown() -> ShutdownResult {
exit_windows(EWX_SHUTDOWN.0 | EWX_FORCE.0)
}
pub fn reboot() -> ShutdownResult {
exit_windows(EWX_REBOOT.0)
}
pub fn force_reboot() -> ShutdownResult {
exit_windows(EWX_REBOOT.0 | EWX_FORCE.0)
}
pub fn logout() -> ShutdownResult {
exit_windows(EWX_LOGOFF.0)
}
pub fn force_logout() -> ShutdownResult {
exit_windows(EWX_LOGOFF.0 | EWX_FORCE.0)
}
pub fn sleep() -> ShutdownResult {
set_suspend_state(false)
}
pub fn hibernate() -> ShutdownResult {
set_suspend_state(true)
}