0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Rust+各種 Win32 API

Last updated at Posted at 2025-01-11

共通(Common)

type GenericError = Box<dyn std::error::Error + Send + Sync>;
type Result<T> = std::result::Result<T, GenericError>;
Cargo.toml
[target.'cfg(windows)'.dependencies]
windows = { version = "0", features = [
    "Win32_Security_Authorization",
    "Win32_Graphics_Gdi",
    "Win32_Security",
    "Win32_Storage_FileSystem",
    "Win32_System_Environment",
    "Win32_System_IO",
    "Win32_System_Pipes",
    "Win32_System_Registry",
    "Win32_System_RemoteDesktop",
    "Win32_System_Services",
    "Win32_System_Shutdown",
    "Win32_System_SystemServices",
    "Win32_System_Threading",
    "Win32_UI_Input_KeyboardAndMouse",
    "Win32_UI_Shell",
    "Win32_UI_WindowsAndMessaging"
] }

Rust の文字列と C++/wchar_t, Win32/WSTR の相互変換(Interconversion between Rust strings and C++/wchar_t, Win32/WSTR)

wstr.rs
#![allow(dead_code)]
use std::{char::REPLACEMENT_CHARACTER, marker::PhantomData};

use windows::core::{PCWSTR, PWSTR};

pub struct CWSTR<'a> {
    pub s: PCWSTR,
    phantom: PhantomData<&'a ()>,
}

pub struct WSTR<'a> {
    pub s: PWSTR,
    phantom: PhantomData<&'a ()>,
}

pub trait WStrExt<'a> {
    fn to_cw(&'a self) -> CWSTR<'a>;
    fn to_w(&'a mut self) -> WSTR<'a>;
}

impl<'a> WStrExt<'a> for Vec<u16> {
    fn to_cw(&'a self) -> CWSTR<'a> {
        CWSTR {
            s: PCWSTR::from_raw(self.as_ptr()),
            phantom: PhantomData,
        }
    }

    fn to_w(&'a mut self) -> WSTR<'a> {
        WSTR {
            s: PWSTR::from_raw(self.as_mut_ptr()),
            phantom: PhantomData,
        }
    }
}

impl<'a> From<&'a Vec<u16>> for CWSTR<'a> {
    fn from(v: &'a Vec<u16>) -> Self {
        v.to_cw()
    }
}

impl<'a> From<&'a mut Vec<u16>> for WSTR<'a> {
    fn from(v: &'a mut Vec<u16>) -> Self {
        v.to_w()
    }
}

pub fn encode_utf16(source: &str) -> Vec<u16> {
    source.encode_utf16().chain(Some(0)).collect()
}

pub fn decode_utf16(source: &[u16]) -> String {
    std::char::decode_utf16(source.iter().cloned())
        .map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
        .collect()
}

URL を開く(Open URL)

pub fn browse_url(hwnd: HWND, url: &str, process: Option<&mut HANDLE>) -> Result<()> {
    let param_u16 = wstr::encode_utf16(&format!("url.dll,FileProtocolHandler {}", url));
    let mut si = SHELLEXECUTEINFOW {
        cbSize: mem::size_of::<SHELLEXECUTEINFOW>() as u32,
        fMask: SEE_MASK_NOCLOSEPROCESS,
        hwnd,
        lpFile: w!("rundll32.exe"),
        lpParameters: param_u16.to_cw().s,
        nShow: SW_SHOWNORMAL.0,
        ..Default::default()
    };

    unsafe { ShellExecuteExW(&mut si)?; }

    match process {
        None => {
            unsafe { let _ = CloseHandle(si.hProcess); }
        },
        Some(process) => {
            *process = si.hProcess;
        },
    }

    Ok(())
}

実行ファイルのパスを取得(Get the path to the executable file)

pub fn query_full_process_path(process_id: u32) -> Result<String> {
    unsafe {
        let mut process_id = 0u32;
        let process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, process_id)?;
        defer! {
            let _ = CloseHandle(process);
        }

        let mut len = 4096u32;
        let mut path = [0u16; 4096];

        QueryFullProcessImageNameW(process, PROCESS_NAME_WIN32, PWSTR::from_raw(path.as_mut_ptr()), &mut len)?;

        return Ok(wstr::decode_utf16(&path[0..len as usize]));
    }

    Err(Box::new(Error::other("Error message here")))
}

プロセスID, 名前列挙(Process ID, Name Enumeration)

pub fn enum_processes() -> Result<Vec<(u32, String)>> {
    let mut processes = vec![0; 1024];
    let mut num_processes: u32 = 0;

    if let Err(e) = unsafe { EnumProcesses(processes.as_mut_ptr(), (mem::size_of::<u32>() * processes.len()) as u32, &mut num_processes) } {
        eprintln!("{e}");
        return Err(Box::new(Error::other("Error message here")));
    }

    num_processes /= mem::size_of::<u32>() as u32;

    let mut result = Vec::with_capacity(num_processes as usize);

    for i in 0..num_processes {
        let pid = processes[i as usize];

        match unsafe { OpenProcess(PROCESS_QUERY_INFORMATION, false, pid) } {
            Ok(handle) => {
                let mut path = [0; 1024];
                unsafe { GetModuleFileNameExW(handle, HINSTANCE::default(), path.as_mut_slice()); }

                let path_str = match path.iter().position(|&x| x == 0) {
                    Some(pos) => OsString::from_wide(&path[..pos]),
                    None => OsString::from_wide(&path),
                };

                if let Ok(path) = path_str.into_string() {
                    result.push((pid, path));
                }
            },
            Err(e) => {
                eprintln!("{e}");
            },
        }
    }

    Ok(result)
}

カレントディレクトリ(Current directory, Working directory)

pub fn get_wd() -> Result<String> {
    let mut current_exe = env::current_exe()?;

    if current_exe.pop() {
        Ok(current_exe.display().to_string())
    } else {
        Err(Box::new(Error::other("Error message here")))
    }
}

ShellExecute

pub fn shell_execute(file: &str, param: Option<&str>, show_cmd: SHOW_WINDOW_CMD) -> HINSTANCE {
    let file_u16 = wstr::encode_utf16(file);

    match param {
        Some(param) => {
            let param_u16 = wstr::encode_utf16(param);

            unsafe { ShellExecuteW(Some(HWND::default()), None, file_u16.to_cw().s, param_u16.to_cw().s, None, show_cmd) }
        },
        None => {
            unsafe { ShellExecuteW(Some(HWND::default()), None, file_u16.to_cw().s, None, None, show_cmd) }
        },
    }
}

CreateProcess

pub fn create_process(app: &str, param: Option<&str>, cd: Option<&str>, pi: Option<&mut PROCESS_INFORMATION>) -> Result<()> {
    let mut arg_u16 = match param {
        Some(param) => {
            wstr::encode_utf16(&(app.to_string() + " " + param))
        },
        None => {
            wstr::encode_utf16(app)
        },
    };
    let cd_u16 = match cd {
        Some(cd) => {
            wstr::encode_utf16(cd)
        },
        None => {
            Vec::new()
        },
    };
    let si = STARTUPINFOW {
        cb: mem::size_of::<STARTUPINFOW>() as u32,
        ..Default::default()
    };

    match pi {
        Some(pi) => {
            unsafe {
                match cd {
                    Some(_) => {
                        CreateProcessW(None, Some(arg_u16.to_w().s), None, None, false, PROCESS_CREATION_FLAGS(0), None, cd_u16.to_cw().s, &si, pi)?;
                    },
                    None => {
                        CreateProcessW(None, Some(arg_u16.to_w().s), None, None, false, PROCESS_CREATION_FLAGS(0), None, None, &si, pi)?;
                    },
                }
            }
        },
        None => {
            let mut pi = PROCESS_INFORMATION::default();

            unsafe {
                match cd {
                    Some(_) => {
                        CreateProcessW(None, Some(arg_u16.to_w().s), None, None, false, PROCESS_CREATION_FLAGS(0), None, cd_u16.to_cw().s, &si, &mut pi)?;
                    },
                    None => {
                        CreateProcessW(None, Some(arg_u16.to_w().s), None, None, false, PROCESS_CREATION_FLAGS(0), None, None, &si, &mut pi)?;
                    },
                }
                let _ = CloseHandle(pi.hThread);
                let _ = CloseHandle(pi.hProcess);
            }
        },
    }

    Ok(())
}

pub fn create_process_as_user(app: &str, param: Option<&str>, token: HANDLE, creation_flags: PROCESS_CREATION_FLAGS, environment: *const c_void, pi: Option<&mut PROCESS_INFORMATION>) -> Result<()> {
    let mut desktop_u16 = wstr::encode_utf16(r#"winsta0\default"#);
    let si = STARTUPINFOW {
        cb: mem::size_of::<STARTUPINFOW>() as u32,
        lpDesktop: desktop_u16.to_w().s,
        ..Default::default()
    };
    let arg = match param {
        Some(param) => {
            app.to_string() + " " + param
        },
        None => {
            app.to_string()
        },
    };
    let mut arg_u16 = wstr::encode_utf16(&arg);

    match pi {
        None => {
            let mut pi = PROCESS_INFORMATION::default();

            unsafe {
                CreateProcessAsUserW(Some(token), None, Some(arg_u16.to_w().s), None, None, false, creation_flags, Some(environment), None, &si, &mut pi)?;
                let _ = CloseHandle(pi.hThread);
                let _ = CloseHandle(pi.hProcess);
            }
        },
        Some(pi) => {
            unsafe { CreateProcessAsUserW(Some(token), None, Some(arg_u16.to_w().s), None, None, false, creation_flags, Some(environment), None, &si, pi)?; }
        },
    }

    Ok(())
}

pub fn create_process_as_active_user(app: &str, param: Option<&str>, pi: Option<&mut PROCESS_INFORMATION>) -> Result<()> {
    let session_id = get_active_session_id()?;
    let mut user_token = HANDLE::default();

    unsafe { WTSQueryUserToken(session_id, &mut user_token)? };
    defer! {
        unsafe { let _ = CloseHandle(user_token); }
    }

    unsafe {
        let mut size = mem::size_of::<HANDLE>() as u32;
        let mut linked_token = HANDLE::default();

        GetTokenInformation(user_token, TokenLinkedToken, Some(&mut linked_token as *mut _ as *mut _), size, &mut size)?;
        defer! {
            let _ = CloseHandle(linked_token);
        }

        let mut duplicated_token = HANDLE::default();

        DuplicateTokenEx(linked_token, TOKEN_ACCESS_MASK(MAXIMUM_ALLOWED), None, SecurityImpersonation, TokenPrimary, &mut duplicated_token)?;
        defer! {
            let _ = CloseHandle(duplicated_token);
        }

        let mut environment = ptr::null_mut();
        let mut creation_flags = CREATE_NEW_CONSOLE | NORMAL_PRIORITY_CLASS;

        match CreateEnvironmentBlock(&mut environment, Some(duplicated_token), true) {
            Ok(_) => {
                creation_flags |= CREATE_UNICODE_ENVIRONMENT;
            },
            Err(_) => {
                environment = ptr::null_mut();
            },
        }
        defer! {
            let _ = DestroyEnvironmentBlock(environment);
        }

        create_process_as_user(app, param, duplicated_token, creation_flags, environment, pi)?;
    };

    Ok(())
}

pub fn get_active_session_id() -> Result<u32> {
    let mut si: *mut WTS_SESSION_INFOW = ptr::null_mut();
    let mut count = 0u32;

    unsafe { WTSEnumerateSessionsW(Some(WTS_CURRENT_SERVER_HANDLE), 0, 1, &mut si, &mut count)?; }
    defer! {
        unsafe { WTSFreeMemory(si as *mut _); }
    }

    let mut iter = si;

    unsafe {
        for _ in 0..count {
            if (*iter).State == WTSActive {
                return Ok((*iter).SessionId);
            }

            iter = iter.offset(1);
        }
    }

    Err(Box::new(Error::new(ErrorKind::NotFound, "Error message here")))
}

ホットキー(Hot Key)

#[derive(FromPrimitive)]
enum IdHotKey {
    ABC,
}

// 登録削除
RegisterHotKey(Some(hwnd), IdHotKey::ABC as i32, MOD_CONTROL | MOD_SHIFT, VK_SPACE.0 as u32);
RegisterHotKey(Some(hwnd), IdHotKey::ABC as i32, MOD_ALT | MOD_CONTROL, 'A' as u32);
UnregisterHotKey(Some(hwnd), IdHotKey::ABC as i32);

// イベント処理
WM_HOTKEY => {
    match FromPrimitive::from_usize(w_param.0) {
        Some(IdHotKey::ABC) => { ... }
        ...
    }
    // enum に FromPrimitive を使わない場合
    // match w_param.0 {
    //     x if x == IdHotKey::ABC as usize => { ... }
    //     ...
    // }
}

ウィンドウ作成/メッセージループ(Window creation/Message loop)

pub fn create_window() -> Result<HWND> {
    let class_name = w!("class name here");
    let winc = WNDCLASSW {
        lpfnWndProc: Some(wnd_proc),
        lpszClassName: class_name,
        ..Default::default()
    };

    if unsafe { RegisterClassW(&winc) } == 0 {
        return Err(Box::new(Error::other("Error message here")))
    }

    match unsafe { CreateWindowExW(WINDOW_EX_STYLE(0), class_name, w!("window name here"), WS_OVERLAPPEDWINDOW | WS_MINIMIZE, 0, 0, 0, 0, None, None, None, None) } {
        Ok(hwnd) => {
            Ok(hwnd)
        },
        Err(e) => {
            Err(Box::new(e))
        },
    }
}

unsafe extern "system" fn wnd_proc(hwnd: HWND, msg: u32, w_param: WPARAM, l_param: LPARAM) -> LRESULT {
    match msg {
        WM_CREATE => {
        },
        WM_CLOSE => {
            let _ = DestroyWindow(hwnd);
        },
        WM_DESTROY => {
            PostQuitMessage(0);
        }
        _ => {
            return DefWindowProcW(hwnd, msg, w_param, l_param);
        }
    }

    LRESULT(0)
}

fn main() -> Result<()> {
    unsafe {
        let hwnd = create_window()?;

        let _ = ShowWindow(hwnd, SW_SHOWNORMAL);
        let _ = UpdateWindow(hwnd);

        let mut msg = MSG::default();

        while GetMessageW(&mut msg, None, 0, 0).as_bool() {
            let _ = TranslateMessage(&msg);
            DispatchMessageW(&msg);
        }
    }

    Ok(())
}

フォント(Font)

let log_font = LOGFONTW {
    lfHeight: 20,
    lfWidth: 0,
    lfEscapement: 0,
    lfOrientation: 0,
    lfWeight: FW_NORMAL.0 as i32,
    lfItalic: 0,
    lfUnderline: 0,
    lfStrikeOut: 0,
    lfCharSet: DEFAULT_CHARSET,
    lfOutPrecision: OUT_TT_PRECIS,
    lfClipPrecision: CLIP_DEFAULT_PRECIS,
    lfQuality: ANTIALIASED_QUALITY,
    lfPitchAndFamily: DEFAULT_PITCH.0 | FF_DONTCARE.0,
    lfFaceName: {
        let mut face_name_u16 = wstr::encode_utf16("Meiryo");
        face_name_u16.resize(32, 0);
        face_name_u16.try_into().unwrap()
    },
};
let font = CreateFontIndirectW(&log_font);

名前付きパイプ(Named Pipe)

pub fn create_security_named_pipe(name: &str) -> Result<HANDLE> {
    let mut desc = SECURITY_DESCRIPTOR::default();

    unsafe {
        InitializeSecurityDescriptor(PSECURITY_DESCRIPTOR(&mut desc as *mut _ as *mut _), SECURITY_DESCRIPTOR_REVISION)?;

        let mut accesses: [EXPLICIT_ACCESS_W; 2] = [EXPLICIT_ACCESS_W::default(); 2];

        // 設定例
        BuildExplicitAccessWithNameW(&mut accesses[0], w!("Everyone"), FILE_ALL_ACCESS.0, GRANT_ACCESS, ACE_FLAGS(0));
        BuildExplicitAccessWithNameW(&mut accesses[1], w!("ANONYMOUS LOGON"), FILE_ALL_ACCESS.0, GRANT_ACCESS, ACE_FLAGS(0));

        let mut dacl: *mut ACL = ptr::null_mut();

        SetEntriesInAclW(Some(&accesses), None, &mut dacl).ok()?;
        defer! {
            LocalFree(Some(HLOCAL(dacl as _)));
        }

        SetSecurityDescriptorDacl(PSECURITY_DESCRIPTOR(&mut desc as *mut _ as *mut _), true, Some(dacl), false)?;

        let mut attr = SECURITY_ATTRIBUTES::default();

        attr.lpSecurityDescriptor = &mut desc as *mut _ as *mut _;
        attr.bInheritHandle = BOOL(0);

        let name_u16 = wstr::encode_utf16(name);
        let pipe = CreateNamedPipeW(name_u16.to_cw().s, PIPE_ACCESS_INBOUND | FILE_FLAG_OVERLAPPED, PIPE_READMODE_BYTE, 1, 1024, 1024, 1000, Some(&attr));

        if pipe == INVALID_HANDLE_VALUE {
            return Err(Box::new(Error::other("Error message here")));
        }

        Ok(pipe)
    }
}

特権(Privilege)

pub fn adjust_token_privilege(token: HANDLE, privilege: PCWSTR) -> Result<()> {
    let mut tp = TOKEN_PRIVILEGES::default();

    tp.PrivilegeCount = 1;

    unsafe {
        LookupPrivilegeValueW(None, privilege, &mut tp.Privileges[0].Luid)?;
        tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        AdjustTokenPrivileges(token, false, Some(&tp), 0, None, None)?;
    }

    Ok(())
}

pub fn adjust_process_privilege(process: HANDLE, privilege: PCWSTR) -> Result<()> {
    let mut token = HANDLE::default();

    unsafe {
        OpenProcessToken(process, TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &mut token)?;
        defer! {
            let _ = CloseHandle(token);
        }

        adjust_token_privilege(token, privilege)
    }
}

fn main() -> Result<()> {
    adjust_process_privilege(unsafe { GetCurrentProcess() }, SE_DEBUG_NAME) // 使用例
}

ExitWindows

pub fn exit_windows(flags: EXIT_WINDOWS_FLAGS) -> Result<()> {
    if flags.0 & EWX_LOGOFF.0 == 0 {
        adjust_process_privilege(unsafe { GetCurrentProcess() }, SE_SHUTDOWN_NAME)?;
    }

    unsafe { ExitWindowsEx(flags, SHTDN_REASON_NONE)?; }

    Ok(())
}

サービス(Service)

開始(Start)

let mut service_name_u16 = wstr::encode_utf16("Service name");
let services: &[SERVICE_TABLE_ENTRYW] = &[
    SERVICE_TABLE_ENTRYW {
        lpServiceName: service_name_u16.to_w().s,
        lpServiceProc: Some(service_main), // service_main は下記参照
    },
    SERVICE_TABLE_ENTRYW {
        ..Default::default()
    },
];

unsafe { StartServiceCtrlDispatcherW(services.as_ptr())?; }

登録/削除(Register/Unregister)

pub fn register_service() -> Result<()> {
    let manager = unsafe { OpenSCManagerW(None, None, SC_MANAGER_CREATE_SERVICE | SC_MANAGER_LOCK)? };

    if manager.is_invalid() {
        return Err(Box::new(Error::other("Error message here")));
    }
    defer! {
        unsafe { let _ = CloseServiceHandle(manager); }
    }

    let current_exe = env::current_exe()?;
    let current_exe_path = current_exe.display().to_string();
    let current_exe_path_u16 = wstr::encode_utf16(&current_exe_path);
    let service = unsafe { CreateServiceW(manager, w!("Service name"), w!("Display name"), SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, current_exe_path_u16.to_cw().s, None, None, None, None, None)? };

    if service.is_invalid() {
        return Err(Box::new(Error::other("Error message here")));
    }
    defer! {
        unsafe { let _ = CloseServiceHandle(service); }
    }

    let mut description_u16 = wstr::encode_utf16("Service description");
    let desc = SERVICE_DESCRIPTIONW {
        lpDescription: description_u16.to_w().s,
    };

    unsafe {
        {
            let lock = LockServiceDatabase(manager);
            defer! {
                let _ = UnlockServiceDatabase(lock);
            }

            ChangeServiceConfig2W(service, SERVICE_CONFIG_DESCRIPTION, Some(&desc as *const _ as *mut _))?;
        }

        std::thread::sleep(std::time::Duration::from_millis(500));
        StartServiceW(service, None)?;
    }

    Ok(())
}

pub fn unregister_service() -> Result<()> {
    let manager = unsafe { OpenSCManagerW(None, None, SC_MANAGER_CREATE_SERVICE | SC_MANAGER_LOCK)? };

    if manager.is_invalid() {
        return Err(Box::new(Error::other("Error message here")));
    }
    defer! {
        unsafe { let _ = CloseServiceHandle(manager); }
    }

    let service = unsafe { OpenServiceW(manager, w!("Service name"), SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE.0)? };

    if service.is_invalid() {
        return Err(Box::new(Error::other("Error message here")));
    }
    defer! {
        unsafe { let _ = CloseServiceHandle(service); }
    }

    let mut ss = SERVICE_STATUS::default();

    unsafe {
        QueryServiceStatus(service, &mut ss)?;
        if ss.dwCurrentState == SERVICE_RUNNING {
            ControlService(service, SERVICE_CONTROL_STOP, &mut ss)?;
        }

        DeleteService(service)?;
    }

    Ok(())
}

ハンドラ(Service handler)

#[derive(Debug, PartialEq, Default)]
struct ServiceData {
    stop_event: HANDLE,
    service_status: SERVICE_STATUS,
    service_handle: SERVICE_STATUS_HANDLE,
}

unsafe impl Send for ServiceData {}

static SERVICE_DATA: LazyLock<Mutex<ServiceData>> = LazyLock::new(|| {
    Mutex::new(ServiceData::default())
});

fn handler_ex_stub(control: u32, _event_type: u32, _event_data: *mut core::ffi::c_void, _context: *mut core::ffi::c_void) -> Result<u32> {
    match control {
        SERVICE_CONTROL_STOP => {
            let mut sd = SERVICE_DATA.lock().unwrap();

            sd.service_status.dwCurrentState = SERVICE_STOP_PENDING;
            sd.service_status.dwCheckPoint   = 0;
            sd.service_status.dwWaitHint     = 1000;

            unsafe {
                SetServiceStatus(sd.service_handle, &sd.service_status)?;
                SetEvent(sd.stop_event)?;
            }
        },
        SERVICE_CONTROL_INTERROGATE => {
            let sd = SERVICE_DATA.lock().unwrap();
            unsafe { SetServiceStatus(sd.service_handle, &sd.service_status)?; }
        },
        _ => {
        },
    }

    Ok(NO_ERROR.0)
}

unsafe extern "system" fn handler_ex(control: u32, event_type: u32, event_data: *mut core::ffi::c_void, context: *mut core::ffi::c_void) -> u32 {
    match handler_ex_stub(control, event_type, event_data, context) {
        Ok(n) => {
            n
        },
        Err(e) => {
            // Error handling here
            NO_ERROR.0
        },
    }
}

fn service_main_stub(_num_services_args: u32, _service_arg_vectors: *mut PWSTR) -> Result<()> {
    {
        let mut sd = SERVICE_DATA.lock().unwrap();
        sd.stop_event = unsafe { CreateEventW(None, false, false, None)? };

        sd.service_handle = unsafe { RegisterServiceCtrlHandlerExW(w!("Service name"), Some(handler_ex), None)? };
        sd.service_status.dwServiceType             = SERVICE_WIN32_OWN_PROCESS;
        sd.service_status.dwCurrentState            = SERVICE_RUNNING;
        sd.service_status.dwControlsAccepted        = SERVICE_ACCEPT_STOP;
        sd.service_status.dwWin32ExitCode           = NO_ERROR.0;
        sd.service_status.dwServiceSpecificExitCode = 0;
        sd.service_status.dwCheckPoint              = 0;
        sd.service_status.dwWaitHint                = 0;

        unsafe { SetServiceStatus(sd.service_handle, &sd.service_status)?; }
    }
    defer! {
        unsafe { let _ = CloseHandle(SERVICE_DATA.lock().unwrap().stop_event); }
    }

    {
        let stop_event = SERVICE_DATA.lock().unwrap().stop_event;

        unsafe { WaitForSingleObject(stop_event, INFINITE); }

        let mut sd = SERVICE_DATA.lock().unwrap();

        sd.service_status.dwCurrentState = SERVICE_STOPPED;
        sd.service_status.dwCheckPoint   = 0;
        sd.service_status.dwWaitHint     = 0;

        unsafe { SetServiceStatus(sd.service_handle, &sd.service_status)?; }
    }

    return Ok(());
}

pub unsafe extern "system" fn service_main(num_services_args: u32, service_arg_vectors: *mut PWSTR) {
    if let Err(e) = service_main_stub(num_services_args, service_arg_vectors) {
        // Error handling here
    }
}
0
0
0

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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?