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)]
14pub struct InstanceGuard {
15 handle: HANDLE,
16}
17
18impl Drop for InstanceGuard {
19 fn drop(&mut self) {
20 unsafe {
21 let _ = CloseHandle(self.handle);
22 }
23 }
24}
25
26fn to_wide_str(value: &str) -> Vec<u16> {
27 OsStr::new(value)
28 .encode_wide()
29 .chain(std::iter::once(0))
30 .collect()
31}
32
33pub fn single_instance(app_id: &str) -> Result<Option<InstanceGuard>> {
46 if app_id.trim().is_empty() {
47 return Err(Error::InvalidInput("app_id cannot be empty"));
48 }
49
50 let mutex_name = format!("Local\\win_desktop_utils_{app_id}");
51 let mutex_name_w = to_wide_str(&mutex_name);
52
53 let handle =
54 unsafe { CreateMutexW(None, false, PCWSTR(mutex_name_w.as_ptr())) }.map_err(|e| {
55 Error::WindowsApi {
56 context: "CreateMutexW",
57 code: e.code().0,
58 }
59 })?;
60
61 let last_error = unsafe { GetLastError() };
62
63 if last_error == ERROR_ALREADY_EXISTS {
64 unsafe {
65 let _ = CloseHandle(handle);
66 }
67 Ok(None)
68 } else {
69 Ok(Some(InstanceGuard { handle }))
70 }
71}