Closed mirtlebot closed 4 months ago
关于崩服务,可以看看#1234,我禁掉了服务上无用的ui更新,理论上会少些崩的机会,也可能让开机启动不成功的问题少些概率
另外,如果在服务没有运行的时候左键点击一下语言栏可以运行服务这样会缓解一点疼痛吗?这样会比右键重启服务要少点击一下了
谢谢,我会在 PR 合并后测试反馈的。
不过因为不太清楚崩溃的时机和机制,崩溃也是不定期的。很难给出「好一些了的定论」。之前我要用做安全测试,有各种版本的虚拟机,有两个屏幕,有软件和脚本。小狼毫一天可以崩 4 - 5 次。
直觉上,从虚拟机切回来、一个脚本执行完、换个屏幕,小狼毫偶尔就炸了。但仍不清楚如何复现。
如果在服务没有运行的时候左键点击一下语言栏可以运行服务这样会缓解一点疼痛吗
从体验说上,啥都不做它就能自己拉起来最好。
这个解决办法,和几年前有经验分享说 将小狼毫服务快捷方式固定到任务栏
差不多。
我可以写一个外部程序监控守护,提在这里只是想讨论下。
我会提好点的初步判断,是之前做ThreadFocusSink事件的时候意外发现两个窗口之间高速切换会引发server内的空指针调用,但是报错的位置是dwrite.dll,然后才有后面限制了ui更新context,status的修改,但是那样还是有一定的概率在两个窗口之间快速切换中输入一个按键的时候崩
昨日有留意有用户试验到如果将小狼毫放到启动项最后一项最后启动,就可以在自启后不退出。进而想到如果小狼毫先于一些应用自启,之后的过程就会类似几个窗口之间快速切换,里面会有update ui的操作,就有机会引起我之前手工测出的崩溃。
于是我上面提到的那个pr里,限制了如果session是tsf session的话,只在message的状态下调用server里的update ui,其他都跳过。目前猜测的方向应该是有可能会减少一点触发我之前发现的那种崩溃的概率。
就差开机不能启动的用户来试了
我之前都在评论底下直接 at 他们名字了,根本不理人的。
至于守护,#1126 里他们有讨论过一些内容,大致流可用。要改一下目录获取的逻辑,还有我觉得用ipc判断server 有没有运行不够好,named mutex 应该更合理高效
其实进程守护的核心逻辑只需要几行代码,不过我为了实现重启Server后恢复不同窗口的状态,比如中英文,emoji开关这种,魔改了很多地方,甚至librime也改了下,免得恢复时弹出状态切换的通知
再者我比较完美主义,不仅希望崩溃后在我不输入的时候,能自动重启,如果在我输入时,刚好崩溃了,也能立刻重启,所以这里就有两个逻辑,要防止同时执行,就用到了进程间的mutex,你可以直接看我的仓库代码,我也是边学C++边查微软文档搞出来的
https://github.com/ccyybn/weasel/commit/88a02ddbdf385226e005f899fbdf259418177a3b
唯一的问题就是,如果server是以管理员身份运行的(第一次安装后),如果竞争到监控任务的TSF进程没有管理员权限,就无法OpenProcess 成功,不能原地等待server崩溃,只能定时检查进程列表,这时如果server崩溃了,而TSF刚好处于检查间隙,则是由输入逻辑触发重启
如果你只是需要简单的重启,就用当时Techince 教我的截图里的代码就好了,最好再加一个开关,免得关不掉服务 https://github.com/rime/weasel/issues/1126#issuecomment-1975203421
如果你只是需要简单的重启,就用当时Techince 教我的截图里的代码就好了,最好再加一个开关,免得关不掉服务 https://github.com/rime/weasel/issues/1126#issuecomment-1975203421
这个代码我尝试过,引用了很多本库中不存在的函数。无法编译出来。
尝试添加这些本身不存在的函数和 moudle,diff 了一下,又发现涉及了很多别的文件。不太敢改了,可能超出了能力范围
引用的其他东西不用管,核心就是echo 不成功就调用server 启动他,然后sleep 一下,再echo 一下,最后connect ,因为是按键触发,实际用的时候会感觉小卡一下,所以我是用的另一种, WaitForSingleObject 等到崩溃就立刻重启,这个思路就和写外部的监控程序是一样的了,就是在里面开个线程,随便干什么都可以
好吧,我尝试搞懂他的代码,但似乎发现比给出的要复杂不少。
不是专业搞这方面的,只知道里面引入了大量主仓没有的函数、对象之类,搜索的时候发现这些对象又在几个文件中出现,没敢往下改了,不然之后 merge 主仓代码的时候查冲突要花太长的时间。
+if (!m_client.Echo()) // Echo 未成功
+{
+ // UpdateGlobalCompartment(); // 这是做什么的?
+ if (GetBit(WeaselFlag::DAEMON_ENABLE)) // 判断服务是否运行?
+ {
+ execute(std::format(LR"({}\WeaselServer.exe)", WeaselRootPath())); // 启动服务
+ Sleep(150); // 等待
+ if (!m_client.Echo()) // 再次尝试 Echo
+ {
+ m_client.Disconnect();
+ m_client.Connect(NULL);
+ m_client.StartSession();
+ }
+ }
+}
'WeaselFlag': is not a class or namespace name
'DAEMON_ENABLE': undeclared identifier [D:\a\weasel\weasel\WeaselTSF\WeaselTSF.vcxproj]
'GetBit': identifier not found [D:\a\weasel\weasel\WeaselTSF\WeaselTSF.vcxproj]
'format': is not a member of 'std' [D:\a\weasel\weasel\WeaselTSF\WeaselTSF.vcxproj]
'WeaselRootPath': identifier not found [D:\a\weasel\weasel\WeaselTSF\WeaselTSF.vcxproj]
'format': identifier not found [D:\a\weasel\weasel\WeaselTSF\WeaselTSF.vcxproj]
'execute': identifier not found [D:\a\weasel\weasel\WeaselTSF\WeaselTSF.vcxproj]
我目前是给 WeaselServer 的监控程序(WinSW)做成系统服务用,以当前用户的权限运行,但有时候重启的不那么及时。
+if (!m_client.Echo()) // Echo 未成功
+{
+ // UpdateGlobalCompartment(); // 同步他加的一些全局的量,包括这里守护的开关
+ if (GetBit(WeaselFlag::DAEMON_ENABLE)) // 判断守护的开关
+ {
+ execute(std::format(LR"({}\WeaselServer.exe)", WeaselRootPath())); // 启动服务
+ Sleep(150); // 等待
+ if (!m_client.Echo()) // 再次尝试 Echo
+ {
+ m_client.Disconnect();
+ m_client.Connect(NULL);
+ m_client.StartSession();
+ }
+ }
+}
因为TSF是多进程,这取决于你在多少进程里用了输入法,所以需要全局共享这个守护的开关,这里的Compartment就是微软为输入法提供一个全局的工具,他这个函数封装的比较复杂,把全局变量在本地缓存了一下,实际上每次直接去Compartment取值就好了,右键菜单切换开关时,就去改Compartment里面的这个值
我没记错的话,就是这两个 setvalue 和 getvalue,我代码里也用到了的,你可以对比一下
https://learn.microsoft.com/en-us/windows/win32/api/msctf/nf-msctf-itfcompartment-getvalue https://learn.microsoft.com/en-us/windows/win32/api/msctf/nf-msctf-itfcompartment-setvalue
https://github.com/ccyybn/weasel/blob/d4180bfa5b5015f92c3e4fdde9d674c18f8c8a20/WeaselTSF/WeaselTSF.cpp#L26 https://github.com/ccyybn/weasel/blob/d4180bfa5b5015f92c3e4fdde9d674c18f8c8a20/WeaselTSF/WeaselTSF.cpp#L65
https://github.com/fxliang/weasel/actions/runs/9140885344
自用试验分支上简单加了一点,基本能用了,没做开关,够用来切换输入法了(如果真不想拉起的话) 如果崩了,会在%TEMP%\rime.weasel下形成dmp文件
@mirtlebot
效果很不错,但不太理解为什么要做 6 次重试才拉起来,感觉时间有些长。
fxliang@680df09#diff-fcf1995c0aa5f5dc9534d787f174781f2d1888e1d623af3258154c7b5335003eR235
给你机会键盘切换输入法之后做换Server之类的操作 3个按键down up
这个守护方法,我碰到一个没法稳定复现的小问题:
是手动退出算法,然后测试的,不代表真实崩溃情况:
我会提好点的初步判断,是之前做ThreadFocusSink事件的时候意外发现两个窗口之间高速切换会引发server内的空指针调用,但是报错的位置是dwrite.dll,然后才有后面限制了ui更新context,status的修改,但是那样还是有一定的概率在两个窗口之间快速切换中输入一个按键的时候崩
昨日有留意有用户试验到如果将小狼毫放到启动项最后一项最后启动,就可以在自启后不退出。进而想到如果小狼毫先于一些应用自启,之后的过程就会类似几个窗口之间快速切换,里面会有update ui的操作,就有机会引起我之前手工测出的崩溃。
于是我上面提到的那个pr里,限制了如果session是tsf session的话,只在message的状态下调用server里的update ui,其他都跳过。目前猜测的方向应该是有可能会减少一点触发我之前发现的那种崩溃的概率。
就差开机不能启动的用户来试了
这个版本后,我用虚拟机,多屏再也没有崩过了
非常感谢 fxliang 和其他开发者的近年来的奉献。
以下是一些 feature request,个人想法,不一定可行:
但作为用户,我从四五年前的 14.3 开始,就深受小狼毫毫无征兆崩溃的困扰,
我并无能力修复或找出原因,只想其能正常工作。
尝试了外部脚本、进程守护,发现还是类似 Techince 提供的那种写入程序内部的效果最好。