修复Nvidia GeForce Experience中3年的错误

2020-12-06 18:29:28

几个月前,我认为我应该尝试使用Microsoft Flight Simulator2020。在这些艰难的时期,Asobo通过摄影测量和机器学习创建的地球传真机似乎是放松的好地方。 ;我插入了可靠的Logitech Freedom 2.4无线游戏杆,然后飞向天空。

在我的母校和童年时代的家中呆了几个小时之后,就该将其称为“一天”了。我将计算机配置为在闲置几分钟后关闭显示器,然后我很快意识到它不再需要这样做了。

这不是我完全不熟悉的东西。有时是打开浏览器选项卡,尝试在后台播放偷偷摸摸的视频,有时是Web应用程序的问题,有时是系统任务,决定是否允许机器靠近Standby /位置很重要。完成时进入睡眠状态。在Windows上,应用程序可以从操作系统的内核模式电源管理器请求此特权-保持计算机处于唤醒状态。这很有用,因为您不想在看电影,玩游戏或复制文件时让机器进入睡眠状态或关闭显示器。您可以通过打开提升的命令提示符并运行powercfg / requests来查看此类请求。

我有个主意。我以前曾参与一个名为procrastitracker的项目,这是一个用于Windows的出色的时间跟踪应用程序,我实现的功能是XInput活动检测。可以看到,我使用该应用程序来跟踪我玩游戏的时间,并且它以为我在使用Xbox控制器时机器处于闲置状态,所以我所做的更改只是让该应用程序将XInput检测为用户活动。我注意到的一件事是Windows确实使用了控制器输入-不仅是鼠标&键盘输入-确定机器当前正在使用,并防止显示器休眠。我还注意到我的一位控制员模拟输入实际上会漂移很多,所以我没有死区的天真的实现无法正常工作,或者拖延追踪器会认为我睡觉时正在使用控制器。死区通过procrastitracker解决了该问题,并且该控制器从未使Windows保持清醒,因此很好。是我的罗技摇杆老怪吗?到现在已经15岁了。我拔下接收器的电源,数分钟后显示器进入睡眠状态。谜团已揭开!

从那以后的几个月中,我对计算机进行了一些升级,包括获得更好的操纵杆,节气门和一些飞行舵踏板。 Windows仍然不能让显示器在插入显示器后进入休眠状态,但是我决定我应该进一步研究这个问题。看着插入了新游戏杆的USB游戏控制器控制面板,我发现绝对没有漂移。没有模拟不稳定。无虚假输入。肯定不是设备的活动使机器保持清醒状态,我现在确定了这一点。

我决定做过去做得很好的事情-我问Google。

而且Google知道。人们已经将问题归结为NVIDIA的GeForce Experience叠加层,有时也称为ShadowPlay(或NVIDIA Share)。它是一款软件,可让您使用NVIDIA显卡。 NVENC编码器可实时捕获压缩视频。人们使用它来共享视频游戏中的瞬间视频,因为NVENC很好,所以非常方便。在保持质量的同时,在CPU上实时压缩高分辨率高帧率视频将是一项艰巨的任务,特别是对于已经负责运行视频游戏的机器,并且NVENC可以通过利用机器而在机器上不增加额外负载的情况下产生高质量的输出GPU中的固定功能编码硬件。它是很酷的东西,所以我不想摆脱它。

因此,问题是这样的:如果插入了操纵杆,并且启用了GeForce Experience覆盖,则显示器将无法进入睡眠状态。如果拔下操纵杆,显示屏将进入休眠状态。如果禁用覆盖,则显示器将休眠。您可以拥有一个,但不能同时拥有。

人们还没有找到问题的根源-人们在3年前就找到了它!

我真的不能相信这个问题已经解决了很长时间了。所以我报告了一个错误。

我确定他们会弄清楚的,但我想对此有所帮助。启用覆盖后,将启动许多进程-所有NVIDIA进程都位于顶部。

每个模块都加载许多模块:我的最初理论是,覆盖图可能正在探测控制器的输入,并将这些事件转换为Windows消息。如果它正在将消息注入其进程之一(可能作为键盘事件),则可能是某些默认事件处理例程正在重置系统空闲状态。我没有证据,但是我知道的不是Windows单独运行。 NVIDIA软件是造成此问题的原因,但是覆盖层无论如何都不会对操纵杆输入做出反应,因此我以某种方式怀疑这是故意操纵杆处理代码的意外副作用。在GPU制造商中滥用Win32并不少见,因此我希望这会很奇怪。在这一点上,我应该指出我不是Win32专家。另一方面,我确实拥有Raymond Chen的书,并且也已阅读。旧的新事物是一个很棒的博客。不过,我在这里仍然有些失落。

我离题了。首先,我需要一种方法来确定何时发生问题,而无需等待显示器进入休眠状态,因此我快速编写了一个简单的应用程序,该应用程序转储了GetLastInputInfo的输出。我不希望此功能在系统空闲状态下具有权威性-您希望为此从CallNtPowerInformation获取SYSTEM_POWER_INFORMATION-但事实证明它是有效的。

我附加到x64dbg中的NVIDIA Share.exe,并开始寻找与输入有关的内容。

我知道Xinput并不是问题的根源-仅适用于Xbox Controller和模拟它们的人,另外我知道procrastitracker一直在后台轮询Xinput并没有造成此问题。我确实注意到的是,即使在调试器中暂停了进程(并且我的小应用程序监视着空闲状态),我仍然可以看到它仍在重置。我认为,它产生了许多过程,因此我一一进行了暂停,并一一暂停了(一一杀死它们是行不通的-它们会立即重新启动)。空闲状态保持复位状态。这是一个巨大的线索-这意味着该应用程序没有运行任何代码来执行此操作。它不是在注入消息或任何类似的东西。它必须是初始化时要做的事情。

我想看看NVIDIA Share是如何在调试器中初始化的,但是它很复杂。您不能直接启动它,它需要由nvcontainer.exe启动。它开始三个副本,每个副本具有不同的参数。它们之间也可能相互通信,因此必须仔细管理其环境以手动启动它们。任何方式都不是无法克服的,但是还有其他尝试。我认为如果可以附加&该过程开始后立即中断x64dbg,并且一些提示将我引向了WinDbg的gflags.exe实用程序。

从理论上讲,您可以使用它在注册表中抛出一个键,该键告诉Windows执行特定的“图像”。 (可执行),当遇到调试器时。我无法使其正常工作-可能是因为该过程是由nvcontainer产生的,或者也许我只是没有正确地完成它。

幸运的是,我们有吉德拉。我做了与调试器中相同的愚蠢的事情,加载了最明显的可执行文件(NVIDIA Share.exe),并提出了最明显的问题。

这是立即有希望的!但是首先,我必须做一些阅读。原始输入不是我熟悉的东西。在过去的美好时光中,有了DirectInput。 DirectInput使您可以进行强制反馈,DirctInput使您拥有大量的按钮和轴,至少在Windows上,使用游戏控制器所获得的体验通常比过去需要用户支持特定控制器(或您的游戏)的体验更为流畅。控制器的驱动程序来模拟另一个更流行的控制器)。在DirectInput出现Xinput之后,Xinput很大程度上围绕Xbox Controller构建。您没有获得比Xbox Controller更多的按钮或轴。您无法连接比Xbox能够连接的控制器更多的控制器。它可以正常工作,但是它不是支持以下用途的API:

现在,重量级API是原始输入。符合HID标准的所有事件都将通过其事件,并且您作为应用程序开发人员的作用是支持您认为适当的HID使用页面。我特别喜欢“模拟控制”页面(0x02)中间的内容,它是Magic Carpet Simulation(0x0B)的使用ID。标准委员会考虑了一切。那么,NVIDIA Share如何处理原始输入? RegisterRawInputDevices。不用担心,我可以对其进行一些清理:它正在注册其窗口句柄,以随时接收来自键盘的原始事件(无论哪个窗口位于前台)。键盘,笨拙。不是操纵杆。但这给了我一个主意。如果我还要扩展小应用程序以请求原始输入怎么办?那么DirectInput呢?如果没有NVIDIA软件,是否可以复制该问题?我花了一整天的时间来实现各种输入方法,重新学习Win32,并再次学习DirectInput ...和COM...。

将原始输入启用为系统范围的输入接收器(dwFlags = RIDEV_INPUTSINK 0x100)或仅在前台焦点上启用(默认值dwFlags = 0x0)会导致设备使用WM_INPUT泛洪HWND消息队列,并阻止系统成为闲。我对Microsoft的建议是在文档中更清楚地说明这一点,并在powercfg / requests和WPA中显示一个请求原始输入的应用程序。我编写的用于演示该问题的应用程序可在以下位置找到:https://github.com/nuzayets/rawinput-debug/

无论如何,不​​是直接的。 NVIDIA Share部分基于CEF Chromium嵌入式框架。当您将令人沮丧的Web开发混为一谈时,为什么只为专注于深奥的桌面开发而高兴呢?我说,越多越好。无论如何,我们不需要那个RAM。

NVIDIA Share将Chromium嵌入式框架作为称为libcef.dll的大于100MB的模块加载。 Ghidra花了一些时间进行分析,但是我发现了有趣的一点。

他们要求原始输入作为其游戏手柄驱动程序的一部分,这很有意义。他们在任何情况下都称FUN_1842af9b4来设置其参数。这是该函数:

幸运的是,没有任何代码修补程序可做。使用ID的值位于可执行文件(在Ghidra的反编译中为DAT_1861e16e8)的.rdata节中。该文件为C:\ Program Files \ NVIDIA Corporation \ NVIDIA GeForce Experience \ libcef.dll,在我的GeForce Experience版本(3.20.5.70)中,有问题的字节位于0x61e0ae8。将0x04更改为0x06意味着他们不是尝试从操纵杆获取原始输入,而是从键盘获取原始输入。我仍然不确定为什么NVIDIA叠加层要求从Chromium输入原始操纵杆。

我花了两天的时间,最后结果是一个字节。至少现在我的电脑可以入睡了。

如果您不想尝试使用十六进制编辑器,则此Powershell脚本将为您完成此操作。

请首先禁用该覆盖,并确保Powershell以管理员身份运行,以便您能够写入该目录。