use std::ffi::c_void;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::sync::{Arc, OnceLock};
use std::time::Duration;
use ::windows::core::{w, HSTRING, PWSTR};
use ::windows::Win32::Foundation::{ERROR_BUFFER_OVERFLOW, ERROR_SUCCESS, HINSTANCE, HWND, LPARAM, LRESULT, WPARAM};
use ::windows::Win32::NetworkManagement::IpHelper::{
GetAdaptersAddresses, GAA_FLAG_SKIP_ANYCAST, GAA_FLAG_SKIP_DNS_SERVER, GAA_FLAG_SKIP_MULTICAST, IP_ADAPTER_ADDRESSES_LH,
};
use ::windows::Win32::NetworkManagement::Ndis::IfOperStatusUp;
use ::windows::Win32::Networking::WinSock::{AF_INET, AF_INET6, AF_UNSPEC, SOCKADDR_IN, SOCKADDR_IN6};
use ::windows::Win32::System::LibraryLoader::GetModuleHandleW;
use ::windows::Win32::System::Registry::{RegGetValueW, HKEY_LOCAL_MACHINE, RRF_RT_REG_DWORD, RRF_RT_REG_SZ};
use ::windows::Win32::System::RemoteDesktop::{WTSRegisterSessionNotification, NOTIFY_FOR_THIS_SESSION};
use ::windows::Win32::System::Shutdown::{ShutdownBlockReasonCreate, ShutdownBlockReasonDestroy};
use ::windows::Win32::System::SystemInformation::{GetComputerNameExW, ComputerNameDnsHostname};
use ::windows::Win32::UI::WindowsAndMessaging::{
CreateWindowExW, DefWindowProcW, DispatchMessageW, GetMessageW, RegisterClassExW, TranslateMessage, MSG, PBT_APMRESUMEAUTOMATIC,
PBT_APMRESUMESUSPEND, PBT_APMSUSPEND, WINDOW_EX_STYLE, WM_ENDSESSION, WM_POWERBROADCAST, WM_QUERYENDSESSION, WM_WTSSESSION_CHANGE,
WNDCLASSEXW, WS_OVERLAPPED, WTS_SESSION_LOCK, WTS_SESSION_UNLOCK,
};
use tauri::{AppHandle, Runtime};
use crate::Shared;
const END_SESSION_WAIT: Duration = Duration::from_millis(2500);
enum PowerEvent {
Suspend,
Resume,
Lock(bool),
EndSession,
}
type Handler = Box<dyn Fn(PowerEvent) + Send + Sync>;
static HANDLER: OnceLock<Handler> = OnceLock::new();
pub(super) fn start<R: Runtime>(app: &AppHandle<R>, shared: &Arc<Shared>) {
let (handle, state) = (app.clone(), shared.clone());
let handler: Handler = Box::new(move |event| match event {
PowerEvent::Suspend => super::on_sleep(&state, "WM_POWERBROADCAST"),
PowerEvent::Resume => super::on_resume(&state, None, "WM_POWERBROADCAST"),
PowerEvent::Lock(locked) => super::on_lock(&handle, &state, locked, "WTS"),
PowerEvent::EndSession => super::on_shutdown(&handle, &state, "WM_ENDSESSION"),
});
if HANDLER.set(handler).is_err() {
return;
}
let state = shared.clone();
let spawned = std::thread::Builder::new().name("drim-system-events".into()).spawn(move || {
if let Err(err) = unsafe { run_window() } {
state.logs.plugin("warn", &format!("系统事件窗口创建失败,休眠、锁屏、关机只靠通用办法:{err}"));
}
});
if let Err(err) = spawned {
shared.logs.plugin("warn", &format!("系统事件线程创建失败:{err}"));
}
}
unsafe fn run_window() -> ::windows::core::Result<()> {
unsafe {
let module = GetModuleHandleW(None)?;
let instance: HINSTANCE = module.into();
let class = w!("DeeprespondImSystemEvents");
let wc = WNDCLASSEXW {
cbSize: std::mem::size_of::<WNDCLASSEXW>() as u32,
lpfnWndProc: Some(window_proc),
hInstance: instance,
lpszClassName: class,
..Default::default()
};
if RegisterClassExW(&wc) == 0 {
return Err(::windows::core::Error::from_thread());
}
let hwnd = CreateWindowExW(WINDOW_EX_STYLE::default(), class, w!("deeprespond-im"), WS_OVERLAPPED, 0, 0, 0, 0, None, None, Some(instance), None)?;
if let Err(err) = WTSRegisterSessionNotification(hwnd, NOTIFY_FOR_THIS_SESSION) {
log::warn!("WTSRegisterSessionNotification 失败,收不到锁屏通知:{err}");
}
let mut msg = MSG::default();
while GetMessageW(&mut msg, None, 0, 0).0 > 0 {
let _ = TranslateMessage(&msg);
DispatchMessageW(&msg);
}
}
Ok(())
}
fn emit(event: PowerEvent) {
if let Some(handler) = HANDLER.get() {
handler(event);
}
}
unsafe extern "system" fn window_proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT {
match msg {
WM_POWERBROADCAST => {
match wparam.0 as u32 {
PBT_APMSUSPEND => emit(PowerEvent::Suspend),
PBT_APMRESUMEAUTOMATIC | PBT_APMRESUMESUSPEND => emit(PowerEvent::Resume),
_ => {}
}
LRESULT(1)
}
WM_WTSSESSION_CHANGE => {
match wparam.0 as u32 {
WTS_SESSION_LOCK => emit(PowerEvent::Lock(true)),
WTS_SESSION_UNLOCK => emit(PowerEvent::Lock(false)),
_ => {}
}
LRESULT(0)
}
WM_QUERYENDSESSION => {
let _ = unsafe { ShutdownBlockReasonCreate(hwnd, &HSTRING::from("正在保存数据并断开连接")) };
LRESULT(1)
}
WM_ENDSESSION => {
if wparam.0 != 0 {
emit(PowerEvent::EndSession);
std::thread::sleep(END_SESSION_WAIT);
}
let _ = unsafe { ShutdownBlockReasonDestroy(hwnd) };
LRESULT(0)
}
_ => unsafe { DefWindowProcW(hwnd, msg, wparam, lparam) },
}
}
pub(super) fn interface_addrs() -> Vec<(String, IpAddr)> {
let mut out = Vec::new();
let flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER;
let mut size: u32 = 16 * 1024;
let mut buf: Vec<u64>;
let mut tries = 0;
loop {
buf = vec![0u64; (size as usize).div_ceil(8)];
let ret = unsafe { GetAdaptersAddresses(AF_UNSPEC.0 as u32, flags, None, Some(buf.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH), &mut size) };
if ret == ERROR_BUFFER_OVERFLOW.0 && tries < 3 {
tries += 1;
continue;
}
if ret != ERROR_SUCCESS.0 {
return out;
}
break;
}
unsafe {
let mut adapter = buf.as_ptr() as *const IP_ADAPTER_ADDRESSES_LH;
while !adapter.is_null() {
let a = &*adapter;
adapter = a.Next;
if a.OperStatus != IfOperStatusUp {
continue;
}
let name = a.AdapterName.to_string().unwrap_or_default();
let mut unicast = a.FirstUnicastAddress;
while !unicast.is_null() {
let u = &*unicast;
unicast = u.Next;
let sa = u.Address.lpSockaddr;
if sa.is_null() {
continue;
}
let addr = if (*sa).sa_family == AF_INET {
let sin = &*(sa as *const SOCKADDR_IN);
IpAddr::V4(Ipv4Addr::from(u32::from_be(sin.sin_addr.S_un.S_addr)))
} else if (*sa).sa_family == AF_INET6 {
let sin6 = &*(sa as *const SOCKADDR_IN6);
IpAddr::V6(Ipv6Addr::from(sin6.sin6_addr.u.Byte))
} else {
continue;
};
out.push((name.clone(), addr));
}
}
}
out
}
pub(super) fn default_routes() -> Vec<String> {
Vec::new()
}
const CURRENT_VERSION: &str = r"SOFTWARE\Microsoft\Windows NT\CurrentVersion";
pub(super) fn os_version() -> String {
let major = reg_dword(CURRENT_VERSION, "CurrentMajorVersionNumber");
let minor = reg_dword(CURRENT_VERSION, "CurrentMinorVersionNumber");
let build = reg_string(CURRENT_VERSION, "CurrentBuildNumber");
match (major, minor, build) {
(Some(major), Some(minor), Some(build)) => format!("Windows {major}.{minor}.{build}"),
(_, _, Some(build)) => format!("Windows {build}"),
_ => "Windows".into(),
}
}
fn reg_dword(subkey: &str, name: &str) -> Option<u32> {
let mut value: u32 = 0;
let mut size = std::mem::size_of::<u32>() as u32;
let ret = unsafe {
RegGetValueW(HKEY_LOCAL_MACHINE, &HSTRING::from(subkey), &HSTRING::from(name), RRF_RT_REG_DWORD, None, Some(&mut value as *mut u32 as *mut c_void), Some(&mut size))
};
(ret == ERROR_SUCCESS).then_some(value)
}
fn reg_string(subkey: &str, name: &str) -> Option<String> {
let (key, value) = (HSTRING::from(subkey), HSTRING::from(name));
let mut size: u32 = 0;
unsafe {
if RegGetValueW(HKEY_LOCAL_MACHINE, &key, &value, RRF_RT_REG_SZ, None, None, Some(&mut size)) != ERROR_SUCCESS {
return None;
}
let mut buf = vec![0u16; (size as usize).div_ceil(2) + 1];
let mut size = (buf.len() * 2) as u32;
if RegGetValueW(HKEY_LOCAL_MACHINE, &key, &value, RRF_RT_REG_SZ, None, Some(buf.as_mut_ptr() as *mut c_void), Some(&mut size)) != ERROR_SUCCESS {
return None;
}
let len = buf.iter().position(|c| *c == 0).unwrap_or(buf.len());
Some(String::from_utf16_lossy(&buf[..len]))
}
}
pub(super) fn computer_name() -> String {
let mut size: u32 = 0;
unsafe {
let _ = GetComputerNameExW(ComputerNameDnsHostname, None, &mut size);
if size == 0 {
return String::new();
}
let mut buf = vec![0u16; size as usize + 1];
let mut len = buf.len() as u32;
if GetComputerNameExW(ComputerNameDnsHostname, Some(PWSTR(buf.as_mut_ptr())), &mut len).is_err() {
return String::new();
}
String::from_utf16_lossy(&buf[..len as usize])
}
}