You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

uiAccess=true时UAC进程中Keyboard.IsKeyDown返回false的调试方法

调试uiAccess功能失效的步骤

咱们一步步来排查你的问题,毕竟uiAccess的生效条件卡得很严,哪怕一个细节没做好都可能失效:

1. 先确认uiAccess的核心启用条件是否完全满足

虽然你提到了签名和路径,但还是要再核对几个容易忽略的点:

  • 证书的信任状态与EKU:
    • 确保你的Comodo代码签名证书在本地机器的「受信任的根证书颁发机构」存储里,而不是用户存储。可以通过certmgr.msc查看,找到你的证书,检查它的路径是否能追溯到受信任的根CA。
    • 检查证书是否包含「代码签名」的增强型密钥用法(EKU)。双击证书→「详细信息」→「增强型密钥用法」,必须有「代码签名 (1.3.6.1.5.5.7.3.3)」这一项,否则Windows会不认这个签名用于uiAccess。
  • 清单文件的正确性:
    • 确认清单里的uiAccess="true"是小写的(XML大小写敏感),而且requestedExecutionLevel必须是level="asInvoker"——如果你的程序请求了requireAdministrator,uiAccess会直接失效,因为它的设计是让普通权限程序访问高权限窗口,而不是给高权限程序加权限。
    • 用mt.exe导出嵌入的清单验证:
      mt.exe -inputresource:Shapeshifter.exe;#1 -out:extracted.manifest
      
      打开extracted.manifest,仔细核对上述两个配置项是否正确。
  • 路径的UAC保护状态:
    • 确认C:\Program Files\Shapeshifter没有被设置为「兼容模式」或者「虚拟化」。右键文件夹→「属性」→「兼容性」,确保「以兼容模式运行这个程序」没勾选;再到「安全」标签,查看是否有「虚拟化」相关的权限设置(一般默认的Program Files文件夹是没有虚拟化的,但手动修改过就会有问题)。

2. 验证uiAccess是否真的在进程中生效

有时候配置看似正确,但Windows并没有给进程授予uiAccess权限,咱们可以用工具或代码确认:

  • 用Process Explorer查看进程属性:
    下载Process Explorer,找到你的程序进程,右键→「属性」→「安全」标签,查看「完整性级别」是否为High,并且「UIAccess」选项是否被勾选。如果没勾选,说明uiAccess根本没启用,得回到第一步找原因。
  • 用代码检查UIAccess状态:
    在你的程序里加一段日志,调用Windows API GetTokenInformation来获取进程的UIAccess标记:
    using System;
    using System.Runtime.InteropServices;
    
    public static bool IsProcessUIAccessEnabled()
    {
        IntPtr hToken = IntPtr.Zero;
        try
        {
            if (!OpenProcessToken(System.Diagnostics.Process.GetCurrentProcess().Handle, 0x0008 /*TOKEN_QUERY*/, out hToken))
                return false;
    
            uint returnLength;
            GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUIAccess, IntPtr.Zero, 0, out returnLength);
            IntPtr buffer = Marshal.AllocHGlobal((int)returnLength);
            try
            {
                if (GetTokenInformation(hToken, TOKEN_INFORMATION_CLASS.TokenUIAccess, buffer, returnLength, out returnLength))
                {
                    return Marshal.ReadInt32(buffer) != 0;
                }
                return false;
            }
            finally
            {
                Marshal.FreeHGlobal(buffer);
            }
        }
        finally
        {
            if (hToken != IntPtr.Zero)
                CloseHandle(hToken);
        }
    }
    
    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool OpenProcessToken(IntPtr ProcessHandle, uint DesiredAccess, out IntPtr TokenHandle);
    
    [DllImport("advapi32.dll", SetLastError = true)]
    private static extern bool GetTokenInformation(IntPtr TokenHandle, TOKEN_INFORMATION_CLASS TokenInformationClass, IntPtr TokenInformation, uint TokenInformationLength, out uint ReturnLength);
    
    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern bool CloseHandle(IntPtr hObject);
    
    private enum TOKEN_INFORMATION_CLASS
    {
        TokenUIAccess = 26
    }
    
    调用这个方法,如果返回false,说明uiAccess确实没生效,继续排查配置问题;如果返回true,那问题可能出在Keyboard.IsKeyDown的使用上。

3. 调试Keyboard.IsKeyDown失效的问题

如果确认uiAccess已经生效,但Keyboard.IsKeyDown还是返回false,那得考虑WPF的Keyboard类的局限性:

  • 替换为低级键盘钩子测试:
    WPF的Keyboard.IsKeyDown依赖于程序的消息循环,当高权限窗口获得焦点时,普通权限(即使有uiAccess)的程序可能无法接收到键盘消息。试试用SetWindowsHookEx注册WH_KEYBOARD_LL钩子,这个钩子是全局的,不受权限限制(只要有uiAccess):
    using System;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    
    public class GlobalKeyboardHook
    {
        private const int WH_KEYBOARD_LL = 13;
        private const int WM_KEYDOWN = 0x0100;
    
        private LowLevelKeyboardProc _proc;
        private IntPtr _hookID = IntPtr.Zero;
    
        public event EventHandler<KeyEventArgs> KeyDown;
    
        public GlobalKeyboardHook()
        {
            _proc = HookCallback;
            _hookID = SetHook(_proc);
        }
    
        ~GlobalKeyboardHook()
        {
            UnhookWindowsHookEx(_hookID);
        }
    
        private IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (var curProcess = System.Diagnostics.Process.GetCurrentProcess())
            using (var curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }
    
        private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);
    
        private IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
            {
                int vkCode = Marshal.ReadInt32(lParam);
                KeyDown?.Invoke(this, new KeyEventArgs((Keys)vkCode));
            }
            return CallNextHookEx(_hookID, nCode, wParam, lParam);
        }
    
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);
    
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool UnhookWindowsHookEx(IntPtr hhk);
    
        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
    
        [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr GetModuleHandle(string lpModuleName);
    }
    
    在程序里初始化这个钩子,注册KeyDown事件,看看是否能捕获到高权限窗口焦点时的按键。如果能捕获到,说明是Keyboard.IsKeyDown的问题;如果还是不行,那可能是uiAccess的权限还有遗漏。
  • 检查进程权限:
    打开命令提示符,运行whoami /priv,查看你的进程是否有SeTcbPrivilege权限(虽然uiAccess不强制要求,但有些场景下需要)。如果没有,可以尝试在程序里启用这个权限(需要管理员权限,但你的程序是asInvoker,所以可能不行,不过可以测试一下)。
  • 查看系统事件日志:
    打开「事件查看器」→「Windows日志」→「安全」,查找是否有「审核失败」的事件,事件ID可能是4672或4673,这些事件会告诉你进程是否被拒绝了某些权限。另外,「应用程序」日志里也可能有你的程序抛出的错误信息。

4. 重新签名程序并测试

有时候签名过程中会出现问题,比如没有加时间戳,或者签名被损坏:

  • 用signtool重新签名,加上时间戳:
    signtool sign /f YourCert.pfx /p YourPassword /t http://timestamp.comodoca.com/authenticode Shapeshifter.exe
    
    时间戳很重要,否则证书过期后,uiAccess会失效。
  • 签名后,右键程序→「属性」→「数字签名」,确认签名是有效的,并且没有警告。

内容的提问来源于stack exchange,提问作者Mathias Lykkegaard Lorenzen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:28:31