GetDoubleClickTime意外返回0

问题描述

一些客户将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

佩德罗