目录
去年 9 月结束实习的时候,顺手在狗东内销上看上了性价比超高的 MateBook E Go。于是光速拿下。这台机器的骁龙 8cx Gen3 让我十分满意,屏幕和键盘也都很优秀,唯一美中不足是它的笔体验远远不如 Surface。
M-Pencil 唯一的交互只有双击,这一操作可以在华为电脑管家的选件中心自定义操作。这里我选择了和 Apple Pencil 一样唐氏的双击切换橡皮。本以为这个功能会十分好用,但当我在速写白板和 Word 中切换橡皮时,却始终无法正常切换。
查阅华为官网后发现,原来华为唐氏地对双击切换功能设置了白名单…
白名单和笔驱动原理
既然有白名单,我们首先需要查找白名单的位置。在研究这个之前,我们首先要了解 M-Pencil 在 MateBook E 系列机型上运行的原理。
不知基于什么考虑,华为使用用户态程序来负责设备的驱动逻辑。我们吸附 M-Pencil 和响应双击操作等事件都是由华为电脑管家的选件中心插件处理。它对应的后台进程是 AcAppDaemon.exe。(吐槽华为这帮懒狗,ARM64 的机器这些 OEM 套件全是 x64 的…)
进入它所在的路径 C:\Program Files\Huawei\PCManager\components\accessories_center\accessories_app\AccessoryApp
。考虑白名单往往是通过进程名称判断,我们以 onenoteim
搜索,可以发现在 .\Lib\Plugins
下的 AlitaPenApp.dll
,CD52PenApp.dll
,CD54PenApp.dll
和 CD54RPenApp.dll
中均对应有这些白名单信息。这些 DLL 对应了不同的笔型号,对于我的二代雪域白 M-Pencil 型号为 CD54,则使用 CD54PenApp.dll
。
我们首先考虑能不能修改这些 DLL 来避免白名单呢?很遗憾不能,因为 DLL 都经过了华为的数字签名,修改 DLL 会导致数字签名错误而无法加载。
既然不能通过修改 DLL 达成目的,我们可以考虑自行实现一个程序来控制笔的切换。先从日志入手进一步分析,文件 C:\ProgramData\Comms\PCManager\log\AccessoryCenter\AccessoryApp\InfoAcAppDaemon.log
是 AcAppDaemon 的日志输出,触发几次 OneNote 等程序切换以及橡皮擦切换,观察日志内容变化。
日志内容
2024-02-16 22:06:22.8937 P12204 INFO T23 PenMistouchPreventionProc.TransferPenMode MISTAKEN_TOUCH_PREVENTION_MODE---->onenoteim
2024-02-16 22:06:22.8937 P12204 INFO T20 PenMistouchPreventionProc.CallbackTransferPenMode Enter MISTAKEN_TOUCH_PREVENTION_MODE!
2024-02-16 22:06:26.4830 P12204 INFO T23 PenMistouchPreventionProc.TransferPenMode NORMAL_MODE---->WindowsTerminal
2024-02-16 22:06:26.4830 P12204 INFO T20 PenMistouchPreventionProc.CallbackTransferPenMode Enter NORMAL_MODE!
2024-02-16 22:06:34.1585 P12204 INFO T23 PenMistouchPreventionProc.TransferPenMode MISTAKEN_TOUCH_PREVENTION_MODE---->onenoteim
2024-02-16 22:06:34.1614 P12204 INFO T20 PenMistouchPreventionProc.CallbackTransferPenMode Enter MISTAKEN_TOUCH_PREVENTION_MODE!
2024-02-16 22:06:39.7719 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc CallbackPenCurrentFunc, current pen func is 1
2024-02-16 22:06:39.7719 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Enter Show EraserWindowShow.
2024-02-16 22:06:39.7719 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Show EraserWindowShow.
2024-02-16 22:06:47.9331 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc CallbackPenCurrentFunc, current pen func is 0
2024-02-16 22:06:47.9331 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Enter Close EraserWindowShow.
2024-02-16 22:06:47.9331 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Close EraserWindowShow.
2024-02-16 22:06:47.9331 P12204 INFO T20 Window_Eraser.CloseWindow CloseWindow
2024-02-16 22:06:52.2172 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc CallbackPenCurrentFunc, current pen func is 1
2024-02-16 22:06:52.2172 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Enter Show EraserWindowShow.
2024-02-16 22:06:52.2250 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Show EraserWindowShow.
2024-02-16 22:06:54.0669 P12204 INFO T20
PenMistouchPreventionProc.CallbackPenCurrentFunc CallbackPenCurrentFunc, current pen func is 0
2024-02-16 22:06:54.0669 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Enter Close EraserWindowShow.
2024-02-16 22:06:54.0669 P12204 INFO T20 PenMistouchPreventionProc.CallbackPenCurrentFunc Close EraserWindowShow.
2024-02-16 22:06:54.0669 P12204 INFO T20 Window_Eraser.CloseWindow CloseWindow
2024-02-16 22:06:56.4327 P12204 INFO T20 PenMistouchPreventionProc.CallbackChargingStatusEvent CallbackUpdatePenChargingStatus, charge status is 1
2024-02-16 22:06:57.2490 P12204 INFO T23 PenMistouchPreventionProc.TransferPenMode NORMAL_MODE---->WindowsTerminal
2024-02-16 22:06:57.2490 P12204 INFO T20 PenMistouchPreventionProc.CallbackTransferPenMode Enter NORMAL_MODE!
从中我们可以发现一些函数名称,例如 CallbackPenCurrentFunc
标识了笔的输入状态。我们基于此进行分析,通过 IDA 可以得知笔的相关服务函数是来自 Depend\PenService.dll
。
于是使用 dumpbin 查看导出函数:
Microsoft (R) COFF/PE Dumper Version 14.37.32825.0
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file Depend\PenService.dll
File Type: DLL
Section contains the following exports for PenService.dll
00000000 characteristics
FFFFFFFF time date stamp
0.00 version
1 ordinal base
60 number of functions
60 number of names
ordinal hint RVA name
1 0 0000AA50 CommandSendDoubleFuncChr
2 1 00009D60 CommandSendGetPenBattery
3 2 00009E00 CommandSendGetPenChargingStatus
4 3 00009DB0 CommandSendGetPenConnectStatus
5 4 00009B50 CommandSendGetPenFirmwareVersion
6 5 00009940 CommandSendGetPenHardwareVersion
7 6 0000A020 CommandSendGetPenKeyFunc
8 7 00009F50 CommandSendGetPenKeySupport
9 8 000094B0 CommandSendGetPenModule
10 9 00009710 CommandSendGetPenSerialNo
11 A 0000AA20 CommandSendGlobalPreventionChr
12 B 00009EA0 CommandSendNewConnectRespond
13 C 0000A160 CommandSendOskPrevensionMode
14 D 0000A090 CommandSendPenCloseConnectWindow
15 E 0000A9B0 CommandSendPenCurrentFunc
16 F 00009FC0 CommandSendSetPenKeyFunc
17 10 0000AAA0 CommandSendSysLang
18 11 0000A9F0 CommandSendTouchChr
19 12 0000A1E0 CommandSendTpRotateAngle
20 13 00009500 CommandSendTransferPenMode
21 14 00002990 FwUpdateStart
22 15 00002EC0 FwUpdateStop
23 16 00007DA0 GetInterruptPipeMsg
24 17 00008980 GetMcuVersion
25 18 00009BA0 GetPenFirmwareVersion
26 19 00009990 GetPenHardwareVersion
27 1A 00009560 GetPenModule
28 1B 00009760 GetPenSerialNo
29 1C 0000B620 IsShopDisplayVersion
30 1D 00007630 IsTabTipKeyboardVisble
31 1E 0000AE60 ProcPipeMsg
32 1F 00007710 RegisterCallBack
33 20 00009F30 RegisterCallBackNewPenConnectRequest
34 21 0000A050 RegisterCallBackNewPenConnectResult
35 22 00009540 RegisterCallBackTransferPenMode
36 23 00009D90 RegisterCallBackUpdateBatteryVolume
37 24 00009E30 RegisterCallBackUpdatePenChargingStatus
38 25 00009DE0 RegisterCallBackUpdatePenConnectStatus
39 26 00009B80 RegisterCallBackUpdatePenFirmwareVersion
40 27 00009970 RegisterCallBackUpdatePenHardwareVersion
41 28 0000A000 RegisterCallBackUpdatePenKeyFuncGet
42 29 00009FA0 RegisterCallBackUpdatePenKeyFuncSet
43 2A 00009F80 RegisterCallBackUpdatePenKeySupport
44 2B 000094E0 RegisterCallBackUpdatePenModule
45 2C 00009740 RegisterCallBackUpdatePenSerialNo
46 2D 0000A0C0 RegisterCallbackPenCloseConnectWindow
47 2E 0000AA80 RegisterCallbackPenCurrentFunc
48 2F 0000A0E0 RegisterCallbackPenDeviationReminder
49 30 0000A1A0 RegisterCallbackPenFirstBatAfterConn
50 31 0000A100 RegisterCallbackPenOskPrevensionMode
51 32 0000A070 RegisterCallbackPenTopBatteryWindow
52 33 0000A120 RegisterCallbackPenUpdateProgress
53 34 0000A140 RegisterCallbackPenUpdateRessult
54 35 0000A990 RegisterCallbackSysLang
55 36 0000A1C0 RegisterCallbackTpRotateAngle
56 37 00003580 RegisterLogFunc
57 38 00008750 RetriveMcuSwVersion
58 39 000076D0 StopLoop
59 3A 0000B000 StopProcPipeMsg
60 3B 00003590 UnRegisterLogFunc
Summary
A000 .data
1000 .pdata
6000 .rdata
1000 .reloc
1000 .rsrc
D000 .text
我们观察到先前 CD54PenApp.dll
是通过 RegisterCallbackPenCurrentFunc
得到的笔输入功能状态变更回调。那么相应的,CommandSendPenCurrentFunc
应当就是笔输入状态的修改。
测试后果然如此,CommandSendPenCurrentFunc(1)
代表切换进入橡皮擦模式,CommandSendPenCurrentFunc(0)
代表切换进入笔模式。
于是接下来的难点就在于得到双击事件了。
双击事件获取
之前我在 GitHub 上发现一个 gist 文档 dantmnf/Huawei MateBook E Go (Wi-Fi).md 中,提到华为旧版的笔控制程序 HuaweiPenApp,在安装它之后我发现它比起新版选件中心多了一个“使用 Windows Ink 双击按键设置”。
而我在研究时发现有一个类似的笔增强项目 eiyooooo/MateBook-E-Pen,它的第三方 app 橡皮切换是通过获取笔和橡皮切换控件实现的,这不优雅。但是我发现它能得到点击事件,这让我好奇它是如何实现的。
同时,我注意到使用 MateBook-E-Pen.exe 后,旧版笔控制程序将设置自动切换为了“使用 Windows Ink 双击按键设置”。这不禁让我怀疑其中有着某些联系。虽然这个项目的代码实在有些混乱,但是至少这一功能应当是和 PenService 有关。观察几个参数后,我作出了以下猜测:
- 由于旧版笔控制程序按键设置,“截屏”、“打开智慧语音功能”、“使用 Windows Ink 双击按键设置”和“关闭”按 0,1,2,3 顺序排列,“使用 Windows Ink 双击按键设置”的下标或许为 2。
- 这个项目唯一我不确定功能的导出函数调用是
CommandSendSetPenKeyFunc(2);
,或许它就是切换设置的根源。
经过测试,我的猜测是正确的,那么只需要知道如何监听 Windows Ink 的双击按键事件即可。
查阅微软官方文档《Windows 笔设计》后,我得知双击操作在 Windows 中是绑定为 Win+F19
的特殊虚拟组合键的。
那么现在一切思路就清晰了。
自制切换程序实现
为了方便,我使用 Python 编写了一个自制的切换程序,它具备以下功能:
- 加载 PenService DLL,能够控制笔的输入模式以及双击按键设置
- 监听
Win+F19
快捷键组合,切换笔的输入模式 - 提供一个图标显示笔的当前输入模式的状态
然后我使用 PyInstaller 打了一个包,在使用它之前,你需要先结束掉 AcAppDaemon.exe,然后去 C:\Program C:\Program Files\Huawei\PCManager\components\accessories_center\accessories_app\AccessoryApp\Lib\Plugins
下面删除和你的笔相关的 DLL 来阻止原先的笔事件响应程序加载。
大佬,用您的软件,在其他笔记软件里面双击切橡皮,会强制弹出OneNote,这是为啥呀
你需要在 Windows Ink 设置里面把双击触发事件设置为无,这个程序只监听双击操作
删除那些DDl的话,电脑管家里的华为优选中心就用不了,不过实测好像不删也能起作用