C#中SendKeys.Send偶发“Access denied...”错误的排查求助
排查与解决建议
先捋清楚核心诱因
从你说的「离岗才出问题」来看,大概率是系统锁屏/会话切换搞的鬼——老旧程序基本都依赖桌面交互式会话,Windows锁屏后会把当前会话切换到非交互模式,SendKeys这种模拟按键的操作完全吃不到权限,直接触发“Access denied”;另外定时任务反复调用,搞不好残留了僵尸进程或者未释放的文件句柄,攒一天就炸一次。
先做这几步排查
- 查系统日志:打开「事件查看器」→「Windows日志」→「应用程序」和「系统」,找报错时间点的权限相关事件(比如ID4656、4672),看是文件被拒还是进程交互权限不够。
- 盯进程状态:用Process Explorer挂着,定时抓转换程序的进程,看报错时是不是有没关干净的进程占着文件或会话。
- 手动复现:锁屏后手动触发一次转换,看能不能直接复现,坐实会话交互的问题。
直接上解决办法
把SendKeys扔了,改用进程重定向IO
模拟按键是最不靠谱的方案,直接给程序传标准输入输出才是正道:var psi = new ProcessStartInfo("你的转换程序.exe") { RedirectStandardInput = true, RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true }; using (var proc = Process.Start(psi)) { // 直接把文件名写入标准输入 proc.StandardInput.WriteLine("要转的文件名"); proc.StandardInput.Close(); // 拿输出结果 var result = proc.StandardOutput.ReadToEnd(); proc.WaitForExit(30000); // 加个超时,防止进程挂死 if (!proc.HasExited) proc.Kill(); }这种方式不依赖桌面会话,稳得一批。
确保进程死透,资源全释放
每次调用完必须等进程退出,甚至加超时强制杀进程,别留僵尸:using (var proc = Process.Start(psi)) { // 执行输入输出操作 proc.WaitForExit(30000); // 30秒超时 if (!proc.HasExited) { proc.Kill(); // 记个日志,方便排查为啥进程挂了 } }如果必须用桌面交互(比如老程序不支持标准输入)
- 别让程序在锁屏后会话断开:把程序设为开机自启,保持用户登录(别注销,仅锁屏的话部分系统能保留交互式会话,自己测下);
- 用Windows任务计划:创建任务时勾选「不管用户是否登录都要运行」+「最高权限运行」,但注意这种方式可能看不到程序窗口,得确认老程序能不能无头运行。
文件权限和占用要兜底
- 给转换程序所在目录、目标文件目录加运行用户的「完全控制」权限;
- 转换前先测文件能不能访问,避免被其他进程临时占用:
bool canAccess = false; try { using (var fs = File.Open("目标文件路径", FileMode.Open, FileAccess.ReadWrite, FileShare.None)) { canAccess = true; } } catch (IOException) { // 记日志,稍后重试 } if (canAccess) { // 执行转换 } else { // 加入重试队列,比如5分钟后再试 }
内容的提问来源于stack exchange,提问作者gdu000
相关产品推荐
相关产品推荐

