我正在尝试使用
C++
板条箱将此函数从 Rust
转换为 windows
:
#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;
}
我成功导入了必要的功能,但作为起点,我尝试简单地启动会话并关闭它,但出现错误
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(())
}
我想知道转换代码的流程应该是怎样的。
目前,我正在查看
docs.rs/windows
并跟踪每个函数,我正在尝试拟合参数,但看起来我在这个过程中错过了一些重要的东西。
在引发访问冲突之前,代码会打印出:
160 0x0
第一个数字 160 是
RmStartSession()
调用的返回值。这是由命名常量 ERROR_BAD_ARGUMENTS
标识的系统错误代码,可转换为错误文本“一个或多个参数不正确。”
防止访问冲突的第一步是观察
RmStartSession()
的返回值,如果它不是 ERROR_SUCCESS
则释放,如下所示:
let result = RmStartSession(
psessionhandle,
dwsessionflags,
windows::core::PWSTR(strsessionkey),
);
if result != ERROR_SUCCESS.0 {
return Err("Failed to start session".into());
}
解决了这个问题,我们就可以继续前进并找出哪些论点是错误的。必要的信息可从 Microsoft 详细介绍函数调用契约的文档中获得。
RmStartSession
的相关参数有:
:pSessionHandle
指向重新启动管理器会话句柄的指针。:strSessionKey
以结尾的字符串,其中包含新会话的会话密钥。 在调用null
函数之前必须分配字符串。RmStartSession
任一指针必须是有效并且引用调用者提供的内存。前者是指向
DWORD
(Rust 中的 u32
)的指针,而后者是指向长度为 WCHAR
的 u16
(Rust 中的 CCH_RM_SESSION_KEY + 1
)数组开头的指针。应用这些更改会生成以下代码:
fn release_file_lock(path: &str) -> Result<(), Box<dyn std::error::Error>> {
let mut session_handle = 0_u32;
let mut session_key = [0_u16; CCH_RM_SESSION_KEY as usize + 1];
unsafe {
let result = RmStartSession(&mut session_handle, 0, PWSTR(session_key.as_mut_ptr()));
if result != ERROR_SUCCESS.0 {
return Err("Failed to start session".into());
}
println!("{:?} {:?}", result, session_handle);
RmEndSession(session_handle);
};
Ok(())
}