检测 Visual Studio Extension (VSIX) 中特定于应用程序的用户活动

问题描述 投票:0回答:1

我想以编程方式检测用户何时从 Visual Studio 扩展 (VSIX) 中与 Visual Studio 交互。我不需要任何按键或鼠标事件信息,只是通知用户正在安装 VSIX 的正在运行的 Visual Studio 实例中执行某些操作。我寻求一个“信号”来了解用户是否在 VS IDE 上移动鼠标,或单击任何鼠标按钮,或按正在运行的 VS 实例将“看到”的任何键或组合键。

我为什么想知道这个? 我有一个正在运行的不活动计时器,如果不活动达到或超过定义的时间段,它将触发一些行为。当用户在此之前执行任何形式的键盘/鼠标输入时,我需要能够重置计时器的开始时间。这不仅限于代码编辑器窗口,还包括与正在运行的 Visual Studio 实例的任何形式的用户交互。因此,它同样不是 Windows 系统范围的,但它超出了扩展 (VSIX) 本身。

目前,我正在使用系统范围的挂钩,并尝试将从 EnvDTE80 对象获取的事件消息过滤到 Visual Studio 主窗口句柄。这似乎过于密集,导致需要某种评估的过多 Windows 消息,并且会产生周期性减慢整个环境响应速度的副作用。 (当系统速度变慢时,它不会执行垃圾收集)。

这是我正在使用的内容的经过清理/简化的要点:

using System;
using System.Runtime.InteropServices;

public class UserActivityMonitor
{
    [DllImport("user32.dll")]
    private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelInputProc callback, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll")]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll")]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    private delegate IntPtr LowLevelInputProc(int nCode, IntPtr wParam, IntPtr lParam);

    private const int WH_KEYBOARD_LL = 13;
    private const int WH_MOUSE_LL = 14;

    private const int WM_KEYDOWN = 0x0100;
    private const int WM_LBUTTONDOWN = 0x0201;
    private const int WM_RBUTTONDOWN = 0x0204;
    private const int WM_MOUSEMOVE = 0x0200;

    private static IntPtr keyboardHookID = IntPtr.Zero;
    private static IntPtr mouseHookID = IntPtr.Zero;

    public void StartMonitoring()
    {
        // Set up the keyboard hook
        keyboardHookID = SetWindowsHookEx(WH_KEYBOARD_LL, HookKeyboardCallback, IntPtr.Zero, 0);

        // Set up the mouse hook
        mouseHookID = SetWindowsHookEx(WH_MOUSE_LL, HookMouseCallback, IntPtr.Zero, 0);
    }

    public void StopMonitoring()
    {
        // Unhook the keyboard hook
        UnhookWindowsHookEx(keyboardHookID);

        // Unhook the mouse hook
        UnhookWindowsHookEx(mouseHookID);
    }

    private IntPtr HookKeyboardCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
        {
            // Handle keyboard event - reset the start time to now (not shown) 
        }
        return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
    }

    private IntPtr HookMouseCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && (wParam == (IntPtr)WM_LBUTTONDOWN || wParam == (IntPtr)WM_RBUTTONDOWN || wParam == (IntPtr)WM_MOUSEMOVE))
        {
            // Handle mouse move event - reset the start time to now (not shown) 
        }
        return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
    }
}

与我的实际专有代码的区别在于,当我在钩子回调中有代码时,我还尝试从

lParam
确定窗口句柄,并将其与使用
EnvDTE80
引用获得的 Visual Studio 主窗口句柄进行比较,因此本质上,我只会重置我的不活动计时器,以便与安装了扩展的正在运行的 Visual Studio 实例进行有效的用户交互。

我想要一种更高级别的方法来实现相同的目的,最好避免 P/Invoke 调用并仅使用托管代码。

c# visual-studio pinvoke visual-studio-extensions
1个回答
0
投票

我放弃了这种方法,而是根据检测用户活动

简单地询问系统每秒的空闲时间

每当计时器结束时,我都会进行一个简单的调用来检索用户不活动时间段的时间跨度(理论上可能为零)。

private TimeSpan GetElapsedIdleTime()
{
    LASTINPUTINFO lastInputInfo = new LASTINPUTINFO();
    lastInputInfo.cbSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf(lastInputInfo);
    GetLastInputInfo(ref lastInputInfo);
    long ticksElapsed = ((long)Environment.TickCount - (long)lastInputInfo.dwTime);
    return new TimeSpan(ticksElapsed);
}

我仍然无法仅检测到 Visual Studio 的空闲状态,例如用户可能正忙于使用另一个应用程序,但从 Visual Studio 的角度来看,用户与它交互的时间超过了配置的不活动时间。但这个解决方案目前就可以了。

为了解决这个问题,我计划跟踪 Visual Studio 何时是最顶层窗口,并在不是最顶层窗口时启动一个单独的计时器,如果达到配置的不活动时间段,将触发我想要的操作。同样,当 Visual Studio 切换到并成为最顶层窗口时,我将停止这个辅助计时器。

© www.soinside.com 2019 - 2024. All rights reserved.