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。
- 确保你的Comodo代码签名证书在本地机器的「受信任的根证书颁发机构」存储里,而不是用户存储。可以通过
- 清单文件的正确性:
- 确认清单里的
uiAccess="true"是小写的(XML大小写敏感),而且requestedExecutionLevel必须是level="asInvoker"——如果你的程序请求了requireAdministrator,uiAccess会直接失效,因为它的设计是让普通权限程序访问高权限窗口,而不是给高权限程序加权限。 - 用
mt.exe导出嵌入的清单验证:
打开mt.exe -inputresource:Shapeshifter.exe;#1 -out:extracted.manifestextracted.manifest,仔细核对上述两个配置项是否正确。
- 确认清单里的
- 路径的UAC保护状态:
- 确认
C:\Program Files\Shapeshifter没有被设置为「兼容模式」或者「虚拟化」。右键文件夹→「属性」→「兼容性」,确保「以兼容模式运行这个程序」没勾选;再到「安全」标签,查看是否有「虚拟化」相关的权限设置(一般默认的Program Files文件夹是没有虚拟化的,但手动修改过就会有问题)。
- 确认
2. 验证uiAccess是否真的在进程中生效
有时候配置看似正确,但Windows并没有给进程授予uiAccess权限,咱们可以用工具或代码确认:
- 用Process Explorer查看进程属性:
下载Process Explorer,找到你的程序进程,右键→「属性」→「安全」标签,查看「完整性级别」是否为High,并且「UIAccess」选项是否被勾选。如果没勾选,说明uiAccess根本没启用,得回到第一步找原因。 - 用代码检查UIAccess状态:
在你的程序里加一段日志,调用Windows APIGetTokenInformation来获取进程的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):
在程序里初始化这个钩子,注册KeyDown事件,看看是否能捕获到高权限窗口焦点时的按键。如果能捕获到,说明是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); }Keyboard.IsKeyDown的问题;如果还是不行,那可能是uiAccess的权限还有遗漏。 - 检查进程权限:
打开命令提示符,运行whoami /priv,查看你的进程是否有SeTcbPrivilege权限(虽然uiAccess不强制要求,但有些场景下需要)。如果没有,可以尝试在程序里启用这个权限(需要管理员权限,但你的程序是asInvoker,所以可能不行,不过可以测试一下)。 - 查看系统事件日志:
打开「事件查看器」→「Windows日志」→「安全」,查找是否有「审核失败」的事件,事件ID可能是4672或4673,这些事件会告诉你进程是否被拒绝了某些权限。另外,「应用程序」日志里也可能有你的程序抛出的错误信息。
4. 重新签名程序并测试
有时候签名过程中会出现问题,比如没有加时间戳,或者签名被损坏:
- 用signtool重新签名,加上时间戳:
时间戳很重要,否则证书过期后,uiAccess会失效。signtool sign /f YourCert.pfx /p YourPassword /t http://timestamp.comodoca.com/authenticode Shapeshifter.exe - 签名后,右键程序→「属性」→「数字签名」,确认签名是有效的,并且没有警告。
内容的提问来源于stack exchange,提问作者Mathias Lykkegaard Lorenzen
相关产品推荐
相关产品推荐

