问题描述
一些客户将Windows 10安装升级到2004版(2020年5月更新)后,我们在软件中发现了问题。
Web服务应用程序中的服务器端代码创建了一个DevExpress PDF Viewer控件,但失败,并显示错误消息“间隔不能为0”。 (我知道在Web服务中创建可视控件不是一个好主意,但这是旧版代码,更改起来并不容易)。
分析表明,问题在于该控件尝试创建.net WinForms Timer
组件并将其Interval
属性设置为Systeminformation.DoubleClickTime
的结果(这只是一个包装器) GetDoubleClickTime
winapi函数)。发生错误是因为DoubleClickTime
的值意外为0。
为什么会发生这种情况,并且有一些解决方法?
解决方法
Windows API函数“ GetDoubleClickTime”用于查询两次鼠标单击之间的最大毫秒数,以便将两次单击计为一次双击。
在 Windows 10版本2004 中,此函数在批量登录的上下文中执行时意外返回0,例如:
- Windows服务中的代码
- 从Windows服务启动的应用程序中的代码
- IIS中Web应用程序中的服务器端代码
- 应用程序中的代码从Windows中的“计划任务”开始,带有 设置“无论用户是否登录都运行”
在所有这些情况下,都没有用户界面。执行的代码(在我的情况下,这是非常旧的旧代码,用户界面和业务规则之间还没有分隔)仍然可以查询该值,然后如果意外返回0则失败。例如,以我为例,第三方用户界面控件被实例化,当尝试根据系统双击时间设置.net Windows Forms Timer时,该控件将失败。
在更新到2004版之前,相同的方案在Windows 10中返回的值> 0。
如何重现错误
使用以下简单的C#应用程序
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
namespace WindowsFormsApp14
{
static class Program
{
[DllImport("user32.dll")]
static extern uint GetDoubleClickTime();
[STAThread]
static void Main()
{
// Same result with var doubleClickTime =
// System.Windows.Forms.SystemInformation.DoubleClickTime;
var doubleClickTime = GetDoubleClickTime();
Trace.WriteLine("DoubleClickTime: " + doubleClickTime);
}
}
}
使用Microsoft工具DebugView(启用了“捕获全局Win32”设置)查看Trace.WriteLine的输出。
然后在Windows Task Scheduler中创建一个Task,以执行已编译的应用程序。使用设置“无论用户是否登录都运行”。您将看到输出为
DoubleClickTime:0
如果直接执行已编译的应用程序,则会看到正确的输出
DoubleClickTime:500
(或其他一些值,具体取决于您的鼠标设置)。
在以前的Windows 10版本中,在两种情况下均返回大于0的值。
当代码在Windows服务或IIS应用程序池中的Web应用程序中运行时,可以看到相同的效果。在其他情况下,也可能在批处理登录的上下文中运行代码时。
如何解决问题
- 等待Microsoft在以后的更新中解决此问题。
- 建议客户不要安装Windows 10 2004更新或在必要时返回以前的Windows 10版本
- 修补您的代码,并确保它可以处理GetDoubleClickTime返回0的情况。如有必要,请与第三方库的供应商联系以修补其库。
参考文献
我也将其发布为博客文章:
DevExpress正在为其PDF Viewer组件发布补丁:
,主要原因是您位于不同的Windows工作站下,该操作需要交互式Windows工作站。
在直接运行的情况下,您将在交互式窗口站上运行:"WinSta0"
;
在“无论用户是否登录都运行”的情况下,您将在后台运行,并且站点名称类似于"Service -0x*-*$"
。
我可以重现此问题,并获得错误代码ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION(1459)
:此操作需要一个交互式窗口站。(尽管文档中没有提到可以使用GetLastError
来获得它,但错误代码仅供参考)
作为一种解决方法,数据存储在注册表中:
HKEY_CURRENT_USER\Control Panel\Mouse\DoubleClickSpeed
Type = REG_SZ
Data = 500
您可以尝试将字符串转换为UINT
。
我面临同样的问题。我什至尝试过直接使用 WinAPI:
[DllImport("user32.dll",ExactSpelling = true,CharSet = CharSet.Auto)]
public static extern int GetDoubleClickTime();
[DllImport("user32.dll",CharSet = CharSet.Auto)]
public static extern bool SetDoubleClickTime(int newValue);
但令人惊讶的是,SetDoubleClickTime 没有效果(返回 false)并且 Marshal.GetLastWin32Error() 返回 ERROR_REQUIRES_INTERACTIVE_WINDOWSTATION(1459)...
当我从交互式会话中运行时,一切都按预期进行。我的问题在于遗留的第 3 部分组件,我们无法修改构造函数... :-(
到目前为止,一种解决方法是使用“psexec -s -i”运行构建过程(我们通过 LC.exe 在构建中获取它)......但也许任何人都有另一种/更好的方法?
我忘了说,这是发生在一个
Windows 10 20H2 (Build 19042.985)
BR
佩德罗