win_desktop_utils/
instance.rs1use std::ffi::OsStr;
2use std::os::windows::ffi::OsStrExt;
3
4use windows::core::PCWSTR;
5use windows::Win32::Foundation::{CloseHandle, GetLastError, ERROR_ALREADY_EXISTS, HANDLE};
6use windows::Win32::System::Threading::CreateMutexW;
7
8use crate::error::{Error, Result};
9
10#[derive(Debug)]
12pub struct InstanceGuard {
13 handle: HANDLE,
14}
15
16impl Drop for InstanceGuard {
17 fn drop(&mut self) {
18 unsafe {
19 let _ = CloseHandle(self.handle);
20 }
21 }
22}
23
24fn to_wide_str(value: &str) -> Vec<u16> {
25 OsStr::new(value)
26 .encode_wide()
27 .chain(std::iter::once(0))
28 .collect()
29}
30
31pub fn single_instance(app_id: &str) -> Result<Option<InstanceGuard>> {
36 if app_id.trim().is_empty() {
37 return Err(Error::InvalidInput("app_id cannot be empty"));
38 }
39
40 let mutex_name = format!("Local\\win_desktop_utils_{app_id}");
41 let mutex_name_w = to_wide_str(&mutex_name);
42
43 let handle =
44 unsafe { CreateMutexW(None, false, PCWSTR(mutex_name_w.as_ptr())) }.map_err(|e| {
45 Error::WindowsApi {
46 context: "CreateMutexW",
47 code: e.code().0,
48 }
49 })?;
50
51 let last_error = unsafe { GetLastError() };
52
53 if last_error == ERROR_ALREADY_EXISTS {
54 unsafe {
55 let _ = CloseHandle(handle);
56 }
57 Ok(None)
58 } else {
59 Ok(Some(InstanceGuard { handle }))
60 }
61}