Search code examples
windowswinapirustwindows-rs

Unlocking file using Restart Manager from Rust


I'm trying to convert this function from C++ to Rust using the windows crate:

#include <iostream>
#include "windows.h"

#include <RestartManager.h>
#pragma comment(lib ,"Rstrtmgr.lib")

BOOL ReleaseFileLock(LPCTSTR pFilePath)
{
    BOOL bResult = FALSE;

    DWORD dwSession;
    WCHAR szSessionKey[CCH_RM_SESSION_KEY + 1] = { 0 };
    DWORD dwError = RmStartSession(&dwSession, 0, szSessionKey);
    if (dwError == ERROR_SUCCESS)
    {
        dwError = RmRegisterResources(dwSession, 1, &pFilePath,
            0, NULL, 0, NULL);
        if (dwError == ERROR_SUCCESS)
        {
            UINT nProcInfoNeeded = 0;
            UINT nProcInfo = 0;
            RM_PROCESS_INFO rgpi[1];
            DWORD dwReason;

            dwError = RmGetList(dwSession, &nProcInfoNeeded,
                &nProcInfo, rgpi, &dwReason);
            if (dwError == ERROR_SUCCESS ||
                dwError == ERROR_MORE_DATA)
            {
                if (nProcInfoNeeded > 0)
                {
                    //If current process does not have enough privileges to close one of
                    //the "offending" processes, you'll get ERROR_FAIL_NOACTION_REBOOT
                    dwError = RmShutdown(dwSession, RmForceShutdown, NULL);
                    if (dwError == ERROR_SUCCESS)
                    {
                        bResult = TRUE;
                    }
                }
                else
                    bResult = TRUE;
            }
        }
    }

    RmEndSession(dwSession);

    SetLastError(dwError);
    return bResult;
}

I successfully imported the necessary functions for that, but as a starting point I tried simply to start the session and close it and I got error STATUS_ACCESS_VIOLATION:

use windows_sys::core::PWSTR;
use windows::Win32::System::RestartManager::{
    RmStartSession,
    RmEndSession,
    RmRegisterResources,
    RmGetList,
    RmShutdown,
    RmForceShutdown,
};

fn release_file_lock(path: &str) -> Result<(), Box<dyn std::error::Error>> {    

    let psessionhandle = std::ptr::null_mut();
    let dwsessionflags: u32 = 0;
    let strsessionkey = std::ptr::null_mut();
    unsafe {
        let result = RmStartSession(psessionhandle, dwsessionflags, windows::core::PWSTR(strsessionkey));
        println!("{:?} {:?}", result, psessionhandle);
        RmEndSession(*psessionhandle); // < --- STATUS_ACCESS_VIOLATION
    };
    Ok(())
}

I would like to know what the flow of converting the code should be.

Currently, I'm looking in docs.rs/windows and follow every function and I'm trying to fit the parameters, but it looks like I missed something important in that process.


Solution

  • Thanks to your assistance, I was able to delve deeper and successfully created the whole function and it works.

    use windows::{Win32::{System::RestartManager::{
        RmStartSession,
        RmRegisterResources,
        RmEndSession, 
        RmGetList,
        RmShutdown,
        RmForceShutdown,
        CCH_RM_SESSION_KEY,
        RM_PROCESS_INFO
    }, Foundation::{ERROR_SUCCESS, WIN32_ERROR, ERROR_MORE_DATA}}, core::{PWSTR, PCWSTR, w}};
    
    unsafe fn release_file_lock(file_path: PCWSTR) -> bool {
        let mut session: u32 = 0;
        let mut session_key_buffer = [0_u16; (CCH_RM_SESSION_KEY as usize) + 1];
        let session_key = PWSTR(session_key_buffer.as_mut_ptr());
        let result = RmStartSession(&mut session, 0, session_key);
        if WIN32_ERROR(result) == ERROR_SUCCESS {
            let result = RmRegisterResources(
                session,
                 Some(&[file_path]), 
                 None, 
                 None
            );
            if WIN32_ERROR(result) == ERROR_SUCCESS {
                let mut pnprocinfoneeded: u32 = 0;
                let mut rgaffectedapps: [RM_PROCESS_INFO; 1] = [RM_PROCESS_INFO{..Default::default()}];
                let mut lpdwrebootreasons: u32 = 0;
                let mut pnprocinfo: u32 = 0;
                let result = RmGetList(session, &mut pnprocinfoneeded, &mut pnprocinfo, Some(rgaffectedapps.as_mut_ptr()), &mut lpdwrebootreasons);
                if WIN32_ERROR(result) == ERROR_SUCCESS || WIN32_ERROR(result) == ERROR_MORE_DATA {
                    if pnprocinfoneeded > 0 {
                        // If current process does not have enough privileges to close one of
                        // the "offending" processes, you'll get ERROR_FAIL_NOACTION_REBOOT
                        let result = RmShutdown(session, RmForceShutdown.0 as u32, None);
                        if WIN32_ERROR(result) == ERROR_SUCCESS {
                            // success
                            RmEndSession(session);
                            return true;
                        }
                    } else {
                        // success
                        RmEndSession(session);
                        return true;
                    }
                }
            }
            RmEndSession(session);
            return false;
        }
        return false;
    }