如何让 Windows 设置功能对应的注册表更改实时生效?

背景

由于产品特殊的诉求, 我需要去禁用 Windows 触摸键盘。 一搜索, 答案满天飞,都是说去更改按钮的状态就行。
输入

网友诚不欺我, 当在设置应用关闭功能后, 触摸点击输入框,确实不会出现触摸键盘。那回到技术层面,其实就是去更改注册表 HKEY_CURRENT_USER\SOFTWARE\Microsoft\TabletTip\1.7EnableDesktopModeAutoInvoke 的值

过程

抱着试试看的状态, 手动更改注册表为 0, 但发现并不会生效, 当点击输入框的时候, 触摸键盘依旧还会出现。基于此,请出了 Process Monitor 工具, 来看看在设置应用操作开关能立马生效的时候, TabTip.exe 做了啥? 而我手动更改注册表, TabTip.exe 又做了啥?

我在过滤器中仅仅筛选了 TabTip.exe, 当我操作开关按钮时,可以发现它重新读取了注册表, 并且是很精准的读取, 没有其它多余的注册表路径。而手动更改注册表, TabTip.exe 没有重新读取。很显然, TabTip.exe 并没有去监听注册表的变化。

看到这里,我开始猜想是不是 TabTip.exe 提供了什么接口出来, 但很快就验证了我的想法是错误的。因为我无法从网上或者好伙伴(ChatGPT) 中找到任何相关文档。于是,我通过 CMD 启动 TabTip.exe, 虽然可以看到他有操作注册表的逻辑, 但唯独没有我期望的注册表路径出现

网上的很多方案都是在设置注册表后,直接重启 TabTip.exe, 虽然开关从开启到关闭的时候,该方案能满足我的诉求, 但是开关从关闭到开启时,会弹出触摸键盘,这是我所不期望的场景。好在的是,我通过任务管理器,知道了它的启动参数,于是我给他传递了相同的参数,这时候就不会弹出触摸键盘了。

走到这里,我已经有一个最坏的解决方案, 就是操作注册表后搭配重启进程来玩, 但想到 Windows 能做到不杀进程就能让 TabTip.exe 更新状态,我不得不请出了另一款工具 Spy++。

鉴于跟触摸键盘打过交道, 我知道了触摸键盘的窗口类名是 IPTIP_Main_Window, 于是我开始监听该窗口的任何消息(此处需要注意的是,由于 TabTip 此时是 64 位进程,需要选择 64 位数的 Spy++)
步骤一

步骤二

由于不清楚消息的类型,为了防止错过某些消息,我全选了消息组,。当我操作开关按钮时, 我会发现,触摸键盘的窗口会收到 WM_SETTINGCHANGE 消息, 看到这里,一阵狂喜。
Spy++

于是我也照葫芦画瓢,发送WM_SETTINGCHANGE 消息出去。果不其然,这样操作后, 我对注册表的更改就能及时响应到 TabTip.exe 应用中。

[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam, uint fuFlags, uint uTimeout, out IntPtr lpdwResult);

// 声明广播消息常量
public const int HWND_BROADCAST = 0xFFFF;
public const int WM_SETTINGCHANGE = 0x001A;

// 声明发送消息的超时时间
public const uint SMTO_ABORTIFHUNG = 0x0002;
public const uint SMTO_NORMAL = 0x0000;

private void Button_Click(object sender, RoutedEventArgs e)
{
    int resultCode = (int)SendMessageTimeout((IntPtr)HWND_BROADCAST, WM_SETTINGCHANGE, IntPtr.Zero, IntPtr.Zero, SMTO_ABORTIFHUNG, 1000, out IntPtr result);
    if (resultCode == 0)
    {
        int error = Marshal.GetLastWin32Error();
        Console.WriteLine("Failed to send message. Error code: " + error);
    }
    else
    {
        Console.WriteLine("Message sent successfully.");
    }

}

结论

  • 工欲善其事,必先利其器,合理利用好工具,无疑是站在巨人的肩膀看事情
  • 当知道某些原理后,别太着急,好好研读下文档,能少走点弯路。(小插曲,当时没看 WM_SETTINGCHANGE 的用法,为了不阻塞调用线程,采用了 PostMessage 的方式,发现不会生效,真是傻得可爱)
  • 上述的分析过程,不仅仅适用于让触摸键盘跟随系统按钮实时失效, 当后续存在类似的需求, 改了注册表但又未实际生效时, 此文亦可做为参考。
posted @ 2024-04-10 19:46  学,然后知不足  阅读(338)  评论(0)    收藏  举报