use super::stream::StreamState;
use crate::com::HandleMtaLease;
use crate::container::WslcContainerHandle;
use crate::error::WslcError;
use std::os::windows::raw::HANDLE;
use std::sync::Arc;
use windows_sys::Win32::Foundation::{GetLastError, WAIT_FAILED, WAIT_OBJECT_0, WAIT_TIMEOUT};
use windows_sys::Win32::System::Threading::WaitForSingleObject;
use wslcsdk_sys::types::{WslcProcess, WslcProcessIOHandle, WslcProcessState, WslcSignal};
use wslcsdk_sys::{
WslcGetProcessExitCode, WslcGetProcessExitEvent, WslcGetProcessIOHandle, WslcGetProcessPid,
WslcGetProcessState, WslcReleaseProcess, WslcSignalProcess,
};
#[derive(Debug)]
pub(crate) enum ProcessOwnership {
Owned {
container: WslcContainerHandle,
},
Borrowed {
container: WslcContainerHandle,
},
}
#[derive(Debug)]
pub(crate) struct ProcessInner {
pub(crate) raw: WslcProcess,
pub(crate) ownership: ProcessOwnership,
pub(crate) _stream_state: Option<Arc<StreamState>>,
_mta: HandleMtaLease,
}
impl Drop for ProcessInner {
fn drop(&mut self) {
if matches!(self.ownership, ProcessOwnership::Owned { .. }) && !self.raw.is_null() {
log::debug!("释放 WSLC 进程句柄");
unsafe {
let _ = WslcReleaseProcess(self.raw);
}
self.raw = WslcProcess::NULL;
}
}
}
unsafe impl Send for ProcessInner {}
unsafe impl Sync for ProcessInner {}
#[derive(Clone, Debug)]
pub struct WslcProcessHandle {
pub(crate) inner: Arc<ProcessInner>,
}
impl WslcProcessHandle {
pub unsafe fn from_raw(raw: WslcProcess, container: WslcContainerHandle) -> Self {
Self::from_raw_with_stream(raw, container, None)
}
pub(crate) fn from_raw_with_stream(
raw: WslcProcess,
container: WslcContainerHandle,
stream_state: Option<Arc<StreamState>>,
) -> Self {
Self::new_inner(raw, ProcessOwnership::Owned { container }, stream_state)
}
pub(crate) fn from_borrowed(
raw: WslcProcess,
container: WslcContainerHandle,
stream_state: Option<Arc<StreamState>>,
) -> Self {
Self::new_inner(raw, ProcessOwnership::Borrowed { container }, stream_state)
}
fn new_inner(
raw: WslcProcess,
ownership: ProcessOwnership,
stream_state: Option<Arc<StreamState>>,
) -> Self {
let _mta = HandleMtaLease::acquire().unwrap_or_else(|e| {
log::error!("为进程句柄获取 MTA 租约失败,析构时可能崩溃: {e}");
HandleMtaLease::degraded()
});
Self {
inner: Arc::new(ProcessInner {
raw,
ownership,
_stream_state: stream_state,
_mta,
}),
}
}
pub fn container(&self) -> WslcContainerHandle {
match &self.inner.ownership {
ProcessOwnership::Owned { container } | ProcessOwnership::Borrowed { container } => {
container.clone()
}
}
}
pub fn is_borrowed(&self) -> bool {
matches!(self.inner.ownership, ProcessOwnership::Borrowed { .. })
}
pub fn as_raw(&self) -> WslcProcess {
self.inner.raw
}
pub fn pid(&self) -> Result<u32, WslcError> {
let mut pid = 0u32;
let hr = unsafe { WslcGetProcessPid(self.inner.raw, &mut pid) };
WslcError::check_hr(hr, "获取进程 PID 失败")?;
Ok(pid)
}
pub fn exit_event(&self) -> Result<HANDLE, WslcError> {
let mut event: HANDLE = std::ptr::null_mut();
let hr = unsafe { WslcGetProcessExitEvent(self.inner.raw, &mut event) };
WslcError::check_hr(hr, "获取进程退出事件句柄失败")?;
Ok(event)
}
pub fn state(&self) -> Result<WslcProcessState, WslcError> {
let mut state = WslcProcessState::Unknown;
let hr = unsafe { WslcGetProcessState(self.inner.raw, &mut state) };
WslcError::check_hr(hr, "获取进程状态失败")?;
Ok(state)
}
pub fn exit_code(&self) -> Result<i32, WslcError> {
let mut code = 0i32;
let hr = unsafe { WslcGetProcessExitCode(self.inner.raw, &mut code) };
WslcError::check_hr(hr, "获取进程退出码失败")?;
Ok(code)
}
pub fn signal(&self, sig: WslcSignal) -> Result<(), WslcError> {
let hr = unsafe { WslcSignalProcess(self.inner.raw, sig) };
WslcError::check_hr(hr, "向进程发送信号失败")?;
log::debug!("成功向容器内进程发送 POSIX 信号: {:?}", sig);
Ok(())
}
pub fn io_handle(&self, io: WslcProcessIOHandle) -> Result<HANDLE, WslcError> {
if self.inner._stream_state.is_some() {
return Err(WslcError::InvalidConfiguration(format!(
"进程 {io:?} 已启用流式 IO 回调,官方规定此时无法再获取对应 IO 句柄;\
请改用流式通道消费输出,或在未注册回调的进程上调用本方法"
)));
}
let mut handle: HANDLE = std::ptr::null_mut();
let hr = unsafe { WslcGetProcessIOHandle(self.inner.raw, io, &mut handle) };
if hr < 0 || handle.is_null() {
Err(WslcError::from_hresult(hr, "获取进程标准流句柄失败"))
} else {
Ok(handle)
}
}
pub fn wait(&self, timeout_ms: u32) -> Result<Option<i32>, WslcError> {
let event = self.exit_event()?;
let wait_res = unsafe { WaitForSingleObject(event, timeout_ms) };
match wait_res {
WAIT_OBJECT_0 => {
let code = self.exit_code()?;
Ok(Some(code))
}
WAIT_TIMEOUT => Ok(None),
WAIT_FAILED => {
let err = unsafe { GetLastError() };
Err(WslcError::Hresult(err, "等待进程退出事件失败".to_string()))
}
other => Err(WslcError::Hresult(
other,
format!("等待进程退出返回非预期状态: {other}"),
)),
}
}
pub async fn wait_async(&self, timeout_ms: u32) -> Result<Option<i32>, WslcError> {
if self.inner.raw.is_null() {
return Err(WslcError::InvalidHandle);
}
let event = self.exit_event()?;
let is_signaled = crate::async_ops::wait_win32_event_async(event, timeout_ms).await?;
if is_signaled {
let code = self.exit_code()?;
Ok(Some(code))
} else {
Ok(None)
}
}
}