diff --git a/src/common.rs b/src/common.rs index 630885af1..d4fb53e57 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1627,7 +1627,7 @@ impl UpdateKind { /// /// When all refresh are ruled out, a [`Process`] will still retrieve the following information: /// * Process ID ([`Pid`]) -/// * Parent process ID +/// * Parent process ID (on Windows it never changes though) /// * Process name /// * Start time /// diff --git a/src/windows/disk.rs b/src/windows/disk.rs index 440743b3d..16c7eafdb 100644 --- a/src/windows/disk.rs +++ b/src/windows/disk.rs @@ -1,5 +1,6 @@ // Take a look at the license at the top of the repository in the LICENSE file. +use crate::sys::utils::HandleWrapper; use crate::{Disk, DiskKind}; use std::ffi::{c_void, OsStr, OsString}; @@ -8,11 +9,10 @@ use std::os::windows::ffi::OsStringExt; use std::path::Path; use windows::core::{Error, HRESULT, PCWSTR}; -use windows::Win32::Foundation::{CloseHandle, HANDLE, MAX_PATH}; +use windows::Win32::Foundation::MAX_PATH; use windows::Win32::Storage::FileSystem::{ - CreateFileW, FindFirstVolumeW, FindNextVolumeW, FindVolumeClose, GetDiskFreeSpaceExW, - GetDriveTypeW, GetVolumeInformationW, GetVolumePathNamesForVolumeNameW, FILE_ACCESS_RIGHTS, - FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING, + FindFirstVolumeW, FindNextVolumeW, FindVolumeClose, GetDiskFreeSpaceExW, GetDriveTypeW, + GetVolumeInformationW, GetVolumePathNamesForVolumeNameW, }; use windows::Win32::System::Ioctl::{ PropertyStandardQuery, StorageDeviceSeekPenaltyProperty, DEVICE_SEEK_PENALTY_DESCRIPTOR, @@ -205,31 +205,6 @@ impl DisksInner { } } -struct HandleWrapper(HANDLE); - -impl HandleWrapper { - unsafe fn new(drive_name: &[u16], open_rights: FILE_ACCESS_RIGHTS) -> Option { - let lpfilename = PCWSTR::from_raw(drive_name.as_ptr()); - let handle = CreateFileW( - lpfilename, - open_rights.0, - FILE_SHARE_READ | FILE_SHARE_WRITE, - None, - OPEN_EXISTING, - Default::default(), - HANDLE::default(), - ) - .ok()?; - Some(Self(handle)) - } -} - -impl Drop for HandleWrapper { - fn drop(&mut self) { - let _err = unsafe { CloseHandle(self.0) }; - } -} - unsafe fn get_drive_size(mount_point: &[u16]) -> Option<(u64, u64)> { let mut total_size = 0; let mut available_space = 0; @@ -292,7 +267,8 @@ pub(crate) unsafe fn get_list() -> Vec { .copied() .chain([0]) .collect::>(); - let Some(handle) = HandleWrapper::new(&device_path[..], Default::default()) else { + let Some(handle) = HandleWrapper::new_from_file(&device_path[..], Default::default()) + else { return Vec::new(); }; let Some((total_space, available_space)) = get_drive_size(&mount_paths[0][..]) else { diff --git a/src/windows/process.rs b/src/windows/process.rs index 2896012c1..880af4c41 100644 --- a/src/windows/process.rs +++ b/src/windows/process.rs @@ -1,6 +1,7 @@ // Take a look at the license at the top of the repository in the LICENSE file. use crate::sys::system::is_proc_running; +use crate::sys::utils::HandleWrapper; use crate::windows::Sid; use crate::{DiskUsage, Gid, Pid, ProcessRefreshKind, ProcessStatus, Signal, Uid}; @@ -9,7 +10,6 @@ use std::fmt; #[cfg(feature = "debug")] use std::io; use std::mem::{size_of, zeroed, MaybeUninit}; -use std::ops::Deref; use std::os::windows::ffi::OsStringExt; use std::os::windows::process::CommandExt; use std::path::{Path, PathBuf}; @@ -30,9 +30,8 @@ use windows::Wdk::System::Threading::{ ProcessWow64Information, PROCESSINFOCLASS, }; use windows::Win32::Foundation::{ - CloseHandle, LocalFree, ERROR_INSUFFICIENT_BUFFER, FILETIME, HANDLE, HINSTANCE, HLOCAL, - MAX_PATH, STATUS_BUFFER_OVERFLOW, STATUS_BUFFER_TOO_SMALL, STATUS_INFO_LENGTH_MISMATCH, - UNICODE_STRING, + LocalFree, ERROR_INSUFFICIENT_BUFFER, FILETIME, HANDLE, HINSTANCE, HLOCAL, MAX_PATH, + STATUS_BUFFER_OVERFLOW, STATUS_BUFFER_TOO_SMALL, STATUS_INFO_LENGTH_MISMATCH, UNICODE_STRING, }; use windows::Win32::Security::{GetTokenInformation, TokenUser, TOKEN_QUERY, TOKEN_USER}; use windows::Win32::System::Diagnostics::Debug::ReadProcessMemory; @@ -167,32 +166,6 @@ unsafe fn get_process_user_id(process: &mut ProcessInner, refresh_kind: ProcessR } } -struct HandleWrapper(HANDLE); - -impl HandleWrapper { - fn new(handle: HANDLE) -> Option { - if handle.is_invalid() { - None - } else { - Some(Self(handle)) - } - } -} - -impl Deref for HandleWrapper { - type Target = HANDLE; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl Drop for HandleWrapper { - fn drop(&mut self) { - let _err = unsafe { CloseHandle(self.0) }; - } -} - #[allow(clippy::non_send_fields_in_send_ty)] unsafe impl Send for HandleWrapper {} unsafe impl Sync for HandleWrapper {} @@ -354,48 +327,20 @@ unsafe fn get_exe(process_handler: &HandleWrapper) -> Option { } impl ProcessInner { - pub(crate) fn new_from_pid( - pid: Pid, - now: u64, - refresh_kind: ProcessRefreshKind, - ) -> Option { + pub(crate) fn new_from_pid(pid: Pid, now: u64) -> Option { unsafe { let process_handler = get_process_handler(pid)?; - let mut info: MaybeUninit = MaybeUninit::uninit(); - if NtQueryInformationProcess( - process_handler.0, - ProcessBasicInformation, - info.as_mut_ptr().cast(), - size_of::() as _, - null_mut(), - ) - .is_err() - { - return None; - } - let info = info.assume_init(); - let name = get_process_name(pid).unwrap_or_default(); - let exe = if refresh_kind.exe().needs_update(|| true) { - get_exe(&process_handler) - } else { - None - }; let (start_time, run_time) = get_start_and_run_time(*process_handler, now); - let parent = if info.InheritedFromUniqueProcessId != 0 { - Some(Pid(info.InheritedFromUniqueProcessId as _)) - } else { - None - }; - let mut p = Self { + Some(Self { handle: Some(Arc::new(process_handler)), name, pid, - parent, + parent: None, user_id: None, cmd: Vec::new(), environ: Vec::new(), - exe, + exe: None, cwd: None, root: None, status: ProcessStatus::Run, @@ -410,10 +355,7 @@ impl ProcessInner { old_written_bytes: 0, read_bytes: 0, written_bytes: 0, - }; - get_process_user_id(&mut p, refresh_kind); - get_process_params(&mut p, refresh_kind); - Some(p) + }) } } @@ -424,75 +366,36 @@ impl ProcessInner { virtual_memory: u64, name: OsString, now: u64, - refresh_kind: ProcessRefreshKind, ) -> Self { - if let Some(handle) = get_process_handler(pid) { - unsafe { - let exe = if refresh_kind.exe().needs_update(|| true) { - get_exe(&handle) - } else { - None - }; - let (start_time, run_time) = get_start_and_run_time(*handle, now); - let mut p = Self { - handle: Some(Arc::new(handle)), - name, - pid, - user_id: None, - parent, - cmd: Vec::new(), - environ: Vec::new(), - exe, - cwd: None, - root: None, - status: ProcessStatus::Run, - memory, - virtual_memory, - cpu_usage: 0., - cpu_calc_values: CPUsageCalculationValues::new(), - start_time, - run_time, - updated: true, - old_read_bytes: 0, - old_written_bytes: 0, - read_bytes: 0, - written_bytes: 0, - }; - - get_process_user_id(&mut p, refresh_kind); - get_process_params(&mut p, refresh_kind); - p - } + let (handle, start_time, run_time) = if let Some(handle) = get_process_handler(pid) { + let (start_time, run_time) = get_start_and_run_time(*handle, now); + (Some(Arc::new(handle)), start_time, run_time) } else { - let exe = if refresh_kind.exe().needs_update(|| true) { - get_executable_path(pid) - } else { - None - }; - Self { - handle: None, - name, - pid, - user_id: None, - parent, - cmd: Vec::new(), - environ: Vec::new(), - exe, - cwd: None, - root: None, - status: ProcessStatus::Run, - memory, - virtual_memory, - cpu_usage: 0., - cpu_calc_values: CPUsageCalculationValues::new(), - start_time: 0, - run_time: 0, - updated: true, - old_read_bytes: 0, - old_written_bytes: 0, - read_bytes: 0, - written_bytes: 0, - } + (None, 0, 0) + }; + Self { + handle, + name, + pid, + user_id: None, + parent, + cmd: Vec::new(), + environ: Vec::new(), + exe: None, + cwd: None, + root: None, + status: ProcessStatus::Run, + memory, + virtual_memory, + cpu_usage: 0., + cpu_calc_values: CPUsageCalculationValues::new(), + start_time, + run_time, + updated: true, + old_read_bytes: 0, + old_written_bytes: 0, + read_bytes: 0, + written_bytes: 0, } } @@ -501,6 +404,7 @@ impl ProcessInner { refresh_kind: crate::ProcessRefreshKind, nb_cpus: u64, now: u64, + refresh_parent: bool, ) { if refresh_kind.cpu() { compute_cpu_usage(self, nb_cpus); @@ -513,7 +417,7 @@ impl ProcessInner { } unsafe { get_process_user_id(self, refresh_kind); - get_process_params(self, refresh_kind); + get_process_params(self, refresh_kind, refresh_parent); } if refresh_kind.exe().needs_update(|| self.exe.is_none()) { unsafe { @@ -858,16 +762,25 @@ macro_rules! impl_RtlUserProcessParameters { impl_RtlUserProcessParameters!(RTL_USER_PROCESS_PARAMETERS32); impl_RtlUserProcessParameters!(RTL_USER_PROCESS_PARAMETERS); -unsafe fn get_process_params(process: &mut ProcessInner, refresh_kind: ProcessRefreshKind) { - if !(refresh_kind.cmd().needs_update(|| process.cmd.is_empty()) +fn has_anything_to_update(process: &ProcessInner, refresh_kind: ProcessRefreshKind) -> bool { + refresh_kind.cmd().needs_update(|| process.cmd.is_empty()) || refresh_kind .environ() .needs_update(|| process.environ.is_empty()) || refresh_kind.cwd().needs_update(|| process.cwd.is_none()) - || refresh_kind.root().needs_update(|| process.root.is_none())) - { + || refresh_kind.root().needs_update(|| process.root.is_none()) +} + +unsafe fn get_process_params( + process: &mut ProcessInner, + refresh_kind: ProcessRefreshKind, + refresh_parent: bool, +) { + let has_anything_to_update = has_anything_to_update(process, refresh_kind); + if !refresh_parent && !has_anything_to_update { return; } + let handle = match process.handle.as_ref().map(|handle| handle.0) { Some(h) => h, None => return, @@ -907,6 +820,17 @@ unsafe fn get_process_params(process: &mut ProcessInner, refresh_kind: ProcessRe } let pinfo = pbasicinfo.assume_init(); + let parent = if pinfo.InheritedFromUniqueProcessId as usize != 0 { + Some(Pid(pinfo.InheritedFromUniqueProcessId as _)) + } else { + None + }; + process.parent = parent; + + if !has_anything_to_update { + return; + } + let mut peb = MaybeUninit::::uninit(); if ReadProcessMemory( handle, @@ -950,6 +874,10 @@ unsafe fn get_process_params(process: &mut ProcessInner, refresh_kind: ProcessRe } // target is a 32 bit process in wow64 mode + if !has_anything_to_update { + return; + } + let mut peb32 = MaybeUninit::::uninit(); if ReadProcessMemory( handle, diff --git a/src/windows/system.rs b/src/windows/system.rs index 37730725d..3a1a0f222 100644 --- a/src/windows/system.rs +++ b/src/windows/system.rs @@ -184,8 +184,8 @@ impl SystemInner { } // We need to re-make the process because the PID owner changed. } - if let Some(mut p) = ProcessInner::new_from_pid(pid, now, refresh_kind) { - p.update(refresh_kind, nb_cpus, now); + if let Some(mut p) = ProcessInner::new_from_pid(pid, now) { + p.update(refresh_kind, nb_cpus, now, true); p.updated = false; self.process_list.insert(pid, Process { inner: p }); true @@ -325,7 +325,7 @@ impl SystemInner { proc_.memory = pi.WorkingSetSize as _; proc_.virtual_memory = pi.VirtualSize as _; } - proc_.update(refresh_kind, nb_cpus, now); + proc_.update(refresh_kind, nb_cpus, now, false); // Update the parent in case it changed. proc_.parent = parent; return None; @@ -339,16 +339,9 @@ impl SystemInner { } else { (0, 0) }; - let mut p = ProcessInner::new_full( - pid, - parent, - memory, - virtual_memory, - name, - now, - refresh_kind, - ); - p.update(refresh_kind.without_memory(), nb_cpus, now); + let mut p = + ProcessInner::new_full(pid, parent, memory, virtual_memory, name, now); + p.update(refresh_kind.without_memory(), nb_cpus, now, false); Some(Process { inner: p }) }) .collect::>(); @@ -533,7 +526,7 @@ fn refresh_existing_process( } else { return Some(false); } - proc_.update(refresh_kind, nb_cpus, now); + proc_.update(refresh_kind, nb_cpus, now, false); proc_.updated = false; Some(true) } diff --git a/src/windows/utils.rs b/src/windows/utils.rs index 1510f08a1..06fc69c0b 100644 --- a/src/windows/utils.rs +++ b/src/windows/utils.rs @@ -1,12 +1,16 @@ // Take a look at the license at the top of the repository in the LICENSE file. use windows::core::{PCWSTR, PWSTR}; -use windows::Win32::Foundation::{self, FILETIME}; +use windows::Win32::Foundation::{self, CloseHandle, FILETIME, HANDLE}; +use windows::Win32::Storage::FileSystem::{ + CreateFileW, FILE_ACCESS_RIGHTS, FILE_SHARE_READ, FILE_SHARE_WRITE, OPEN_EXISTING, +}; use windows::Win32::System::Registry::{ RegCloseKey, RegOpenKeyExW, RegQueryValueExW, HKEY, KEY_READ, REG_NONE, }; use std::ffi::OsStr; +use std::ops::Deref; use std::os::windows::ffi::OsStrExt; use std::time::SystemTime; @@ -132,3 +136,47 @@ pub(crate) fn get_reg_value_u32(hkey: HKEY, path: &str, field_name: &str) -> Opt .ok() } } + +pub(crate) struct HandleWrapper(pub(crate) HANDLE); + +impl HandleWrapper { + pub(crate) fn new(handle: HANDLE) -> Option { + if handle.is_invalid() { + None + } else { + Some(Self(handle)) + } + } + + pub(crate) unsafe fn new_from_file( + drive_name: &[u16], + open_rights: FILE_ACCESS_RIGHTS, + ) -> Option { + let lpfilename = PCWSTR::from_raw(drive_name.as_ptr()); + let handle = CreateFileW( + lpfilename, + open_rights.0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + None, + OPEN_EXISTING, + Default::default(), + HANDLE::default(), + ) + .ok()?; + Some(Self(handle)) + } +} + +impl Deref for HandleWrapper { + type Target = HANDLE; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Drop for HandleWrapper { + fn drop(&mut self) { + let _err = unsafe { CloseHandle(self.0) }; + } +} diff --git a/tests/process.rs b/tests/process.rs index a97f98c8b..f4dfe5798 100644 --- a/tests/process.rs +++ b/tests/process.rs @@ -807,7 +807,8 @@ fn test_process_run_time() { // updated as well. #[test] fn test_parent_change() { - if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") { + if !sysinfo::IS_SUPPORTED_SYSTEM || cfg!(feature = "apple-sandbox") || cfg!(windows) { + // Windows never updates its parent PID so no need to check anything. return; }