LoginSignup
11
11

More than 1 year has passed since last update.

Rust で多重起動を防ぐ(Windows 版)

Last updated at Posted at 2021-06-27

こちらの記事にインスパイアされて、Windows だったらどう書くのかなというを残しておきます。

GUI アプリならEnumWindows関数でウィンドウタイトルを調べて多重起動を防止してましたが、CUI アプリではどうするのか知りませんでした。どうやらCreateMutexという WinAPI を使えば良いようです。

CreateMutexA function (synchapi.h) - Win32 apps | Microsoft Docs

Rust から WinAPI を呼ぶには winapi crate を使用します。

winapi - crates.io: Rust Package Registry

コードはこのようになるでしょうか。

Cargo.toml
[package]
name = "multiboot_lock"
version = "0.1.0"
authors = ["benki"]
edition = "2018"

[dependencies.winapi]
version = "0.3"
features = [
    "errhandlingapi",
    "handleapi",
    "minwindef",
    "ntdef",
    "synchapi",
    "winerror",
]
main.rs
use std::ptr;
use std::thread;
use std::time::Duration;
use winapi::{
    shared::{minwindef::TRUE, ntdef::HANDLE, winerror::ERROR_ALREADY_EXISTS},
    um::{
        errhandlingapi::GetLastError,
        handleapi::CloseHandle,
        synchapi::{CreateMutexA, ReleaseMutex},
    },
};

struct Handle(HANDLE);

impl Drop for Handle {
    fn drop(&mut self) {
        if !self.0.is_null() {
            println!("後始末");
            unsafe {
                ReleaseMutex(self.0);
                CloseHandle(self.0);
            }
        }
    }
}

fn main() {
    let handle = unsafe {
        println!("Mutex の生成");
        CreateMutexA(
            ptr::null_mut(),
            TRUE,
            "Mutext Sample\0".as_ptr() as *const i8,
        )
    };
    let _handle = Handle(handle);

    if unsafe { GetLastError() } == ERROR_ALREADY_EXISTS {
        println!("二つ目の起動");
        return;
    }

    thread::sleep(Duration::from_secs(10));
    println!("終了");
}

コンパイルして実行してみると排他制御できました。やったぜ。

windows-rs 版も書いておきます。2021/01/02 追記。

Cargo.toml
[package]
name = "multiboot_lock"
version = "0.1.0"
authors = ["benki"]
edition = "2021"

[dependencies.windows]
version = "0.30"
features = [
    "alloc",
    "Win32_Foundation",
    "Win32_System_Threading",
    "Win32_Foundation",
    "Win32_Security"
]
main.rs
use std::ptr;
use std::thread;
use std::time::Duration;
use windows::{
    core::{Error, Result},
    Win32::{
        Foundation::{CloseHandle, ERROR_ALREADY_EXISTS, HANDLE},
        System::Threading::{CreateMutexW, ReleaseMutex},
    },
};

struct Handle(HANDLE);

impl Drop for Handle {
    fn drop(&mut self) {
        if !self.0.is_invalid() {
            println!("後始末");
            unsafe {
                ReleaseMutex(self.0);
                CloseHandle(self.0);
            }
        }
    }
}

fn main() -> Result<()> {
    println!("Mutext の生成");
    let handle = unsafe { CreateMutexW(ptr::null_mut(), true, "Mutext Sample").ok()? };
    let _handle = Handle(handle);

    if let Some(err) = Error::from_win32().win32_error() {
        if err.eq(&ERROR_ALREADY_EXISTS) {
            println!("二つ目の起動");
            return Ok(());
        }
    }

    thread::sleep(Duration::from_secs(10));

    println!("終了");
    Ok(())
}
11
11
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
11
11