前言
最近遇到个bug,问题一路捋上来,查到了windows权限相关,即我需要将应用改成默认使用管理员身份运行
我看了看代码,试了一下,果然tauri自带的开机启动插件[1]没能在开机时打开默认管理员权限运行的软件
经过一番搜索,找到了windows下有个名为Task Scheduler[2](任务计划程序)的东西,可以在用户登录时执行一个动作
既然可行,那说做就做
默认管理员身份
先把应用改成默认管理员身份,查了一下tauri相关issue[3],在build.rs
按以下修改即可
代码语言:javascript复制let mut windows = tauri_build::WindowsAttributes::new();
windows = windows.app_manifest(r#"
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
<security>
<requestedPrivileges>
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
</assembly>
"#);
tauri_build::try_build(
tauri_build::Attributes::new().windows_attributes(windows)
).expect("failed to run build script");
到rust仓库找了一圈,找到了 windows task scheduler API的封装planif[4],看了看案例,由于对 task scheduler完全不了解,花了一段时间摸索
关于使用planif实现开机自启动,具体见此处[5]
把引用和一些无关内容去除,大致见下面
懂rust的人会觉得代码有点奇怪,但下面我就要讲踩到的坑
代码语言:javascript复制fn spawn_autostart(enabled: bool) -> Result<()> {
let (tx, rx) = mpsc::channel();
thread::spawn(move || {
let _ = tx.send(autostart(enabled).map_err(|e| anyhow!(e.to_string())));
});
Ok(rx.recv().map_err(|e| anyhow!(e))??)
}
fn autostart(enabled: bool) -> std::result::Result<(), Box<dyn std::error::Error>> {
let ts = TaskScheduler::new()?;
let com = ts.get_com();
let sb = ScheduleBuilder::new(&com).unwrap();
let exe = current_exe()?;
let exe = exe.to_str().unwrap();
let settings = PrincipalSettings {
display_name: "".to_string(),
group_id: None,
id: "".to_string(),
logon_type: LogonType::InteractiveToken,
run_level: RunLevel::Highest,
user_id: Some(whoami::username()),
};
sb.create_logon()
.author("hanaTsuk1")?
.trigger("trigger", enabled)?
.action(Action::new("auto start", exe, "", ""))?
.in_folder("shion")?
.principal(settings)?
.delay(Duration {
seconds: Some(6),
..Default::default()
})?
.build()?
.register("auto start", TaskCreationFlags::CreateOrUpdate as i32)?;
Ok(())
}
pub fn enable() -> Result<()> {
spawn_autostart(true)
}
pub fn disable() -> Result<()> {
spawn_autostart(false)
}
创建任务
在创建任务时,单独测试非常正常。但一放到tauri里运行,就会报错
无法在设置线程模式后对其加以更改。 (0x80010106)
经过一番搜索,找到错误与CoInitializeEx[6]有关,它的线程模式存在COINIT_APARTMENTTHREADED和COINIT_MULTITHREADED两种,我使用的库planif和tauri都调用了CoInitializeEx,但选择的模式不同,于是报错
tauri官方给出的解决方案是issue[7],即开一个新线程调用CoInitializeEx
托盘消失
在成功创建完任务后,我尝试了重启看看效果,但非常奇怪并没有生效,于是我尝试了几次发现
程序的确运行了,但托盘有时存在,有时又不存在
我以为是tauri的问题,于是查了半天无果,但仔细一想平时托盘都不会出现这样的问题。会不会是开机启动时系统还没准备好,程序就运行了,于是我打开任务计划程序
发现我的程序上次运行时间总是比其他的软件要早
mmc_HSMrpjIz4O.png
于是找了找延迟相关的代码
延迟失效
我设置了延迟,但发现还是一样没有变化,折腾一圈,发现和planif[8]仓库最近的提交有关系
即作者改了bug,但crate最新版还没有改,于是把源指向github仓库
大功告成
总结
从一个坑爬出来又掉到另一个,但好歹实现了 说一下planif相关的代码
代码语言:javascript复制
代码语言:javascript复制run_level: RunLevel::Highest // 设置管理员权限
sb.create_logon() // 创建一个用户登录后执行的动作
.trigger("trigger", enabled)? // 通过控制enabled值来实现启用或禁用
.action(Action::new("auto start", exe, "", ""))? // action就是要运行的软件
.in_folder("shion")? // 看到上面那张图了吗?它会在左侧创建一个文件夹
// 名称,然后创建并更新
.register("auto start", TaskCreationFlags::CreateOrUpdate as i32)?;
更新
运行生效,正当我准备打包测试时,问题接踵而至
tauri nsis更新会下载setup程序,我在本地运行测试,发现setup程序更新完自行启动后软件却没有启动
setup是普通用户身份,包含的软件是管理员身份,是这个原因吗?
查了一下tauri的配置[9],将installMode设置为both,即可让setup程序默认以管理员身份运行
试了一下,成功
上传代码,一键github action启动,接下来便是等待release生成,然后测试真实打包情况了
不出意外的话,接下来就要出意外了