【问题标题】:getting active window name based on mouse clicks in c#在c#中根据鼠标点击获取活动窗口名称
【发布时间】:2014-09-24 15:54:05
【问题描述】:

我正在尝试让应用程序获取用户单击的窗口的鼠标单击位置和标题(名称)。我目前使用的是 LowLevelMouseProc,它提供了很好的结果,但是每当我点击 Google chrome 时它都会使应用程序崩溃。 代码如下:

 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.Text;
 using System.Runtime.InteropServices;
  using System.Windows.Forms;
 using System.Diagnostics;

        //An attempt to print the screen name of the active window and mouse coordinates at every mouse click
namespace Project1
 {
class InterceptMouse
{

    private static LowLevelMouseProc _proc = HookCallback;
    private static IntPtr _hookID = IntPtr.Zero;

    public static void Main()
    {
        _hookID = SetHook(_proc);
        Application.Run();
        UnhookWindowsHookEx(_hookID);
        Application.Exit();
    }

    private static IntPtr SetHook(LowLevelMouseProc proc)
    {
        using (Process curProcess = Process.GetCurrentProcess())
        using (ProcessModule curModule = curProcess.MainModule)
        {
            return SetWindowsHookEx(WH_MOUSE_LL, proc,
                GetModuleHandle(curModule.ModuleName), 0);
        }
    }

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

    private static IntPtr HookCallback(
        int nCode, IntPtr wParam, IntPtr lParam)
    {
        int nodeCount;
        LinkedListNode<StringBuilder> nodefirst = new LinkedListNode<StringBuilder>(null);
        LinkedListNode<StringBuilder> nodeprev = new LinkedListNode<StringBuilder>(null);
        LinkedList<StringBuilder> windowlist = new LinkedList<StringBuilder>();


        if (nCode >= 0 &&
            MouseMessages.WM_LBUTTONDOWN == (MouseMessages)wParam)
        {
            MSLLHOOKSTRUCT hookStruct = (MSLLHOOKSTRUCT)Marshal.PtrToStructure(lParam, typeof(MSLLHOOKSTRUCT));
            Console.WriteLine(hookStruct.pt.x + ", " + hookStruct.pt.y);
           IntPtr hwnd = GetForegroundWindow();
           StringBuilder windowtitle = new StringBuilder();
           if(GetWindowText(hwnd, windowtitle, 2000)>0)
           Console.WriteLine(windowtitle);
           //Console.WriteLine(nodeCount);

           if (nodeCount == 0)
           {
               nodefirst = windowlist.AddFirst(windowtitle);
               nodeCount++;
           }
           else
           {
               if (nodeCount == 1)
               {
                   nodeprev = windowlist.AddAfter(nodefirst, windowtitle);
                   nodeCount++;
               }
               if (nodeCount > 1)
               {
                   nodeprev = windowlist.AddAfter(nodeprev, windowtitle);
                   nodeCount++;
               }

           }

        }

        return CallNextHookEx(_hookID, nCode, wParam, lParam);

    }

    private const int WH_MOUSE_LL = 14;

    private enum MouseMessages
    {
        WM_LBUTTONDOWN = 0x0201,
        WM_LBUTTONUP = 0x0202,
        WM_MOUSEMOVE = 0x0200,
        WM_MOUSEWHEEL = 0x020A,
        WM_RBUTTONDOWN = 0x0204,
        WM_RBUTTONUP = 0x0205
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct POINT
    {
        public int x;
        public int y;
    }

    [StructLayout(LayoutKind.Sequential)]
    private struct MSLLHOOKSTRUCT
    {
        public POINT pt;
        public uint mouseData;
        public uint flags;
        public uint time;
        public IntPtr dwExtraInfo;
        IntPtr hwnd;

    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook,
        LowLevelMouseProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

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

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    public static extern IntPtr GetForegroundWindow();

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
    }
  }

虽然当我不使用低级挂钩而只使用 Thread.sleep(5000) 并每 5 秒不断获取活动窗口名称时,它不会崩溃。 请帮我找出原因? .请帮帮我。

【问题讨论】:

  • 您可以使用GetCursorPos 获取点击的屏幕坐标,WindowFromPoint 检索与该点击关联的窗口,GetWindowText 获取窗口标题,如果您使用ScreenToClient需要坐标相对于窗口。如果您需要窗口本身,而不是孩子,您可以使用GetAncestor。希望其中一些能派上用场。绝对不需要钩子。
  • @chris :我非常感谢您的方法,但我不明白如何跟踪所有鼠标点击(这将发生在各种窗口上,我需要检索的标题)如果我不要勾住鼠标点击。假设我使用了一个 GetWindowUnderCursor() 来调用 ScreenToClient 和 WindowFromPoint,那么我必须每 5 秒调用一次这个函数 (GetWindowsUnderCursor) 以使光标现在位于哪个窗口上,或者钩住鼠标单击以便我得到通知用户单击窗口时的窗口名称,为此使用了 GetForegroundWindow。

标签: c# winapi


【解决方案1】:

您需要为 StringBuilder 指定容量。为了更彻底,您可以使用GetWindowTextLength,如here 所述。

StringBuilder windowtitle = new StringBuilder(256);
if (GetWindowText(hwnd, windowtitle, windowtitle.Capacity) > 0)
    Console.WriteLine(windowtitle);

【讨论】:

    【解决方案2】:

    此答案是我在此处发布的答案的副本: How to get active window name based on mouse[...]
    此代码超出了监视鼠标的主题。它关于哪个窗口获得了焦点。另请注意:这正是您所要求的 - 窗口名称/标题 -。


    参考:How to get active window handle and title

    命名空间:

    using System;
    using System.Runtime.InteropServices;
    using System.Text;
    using System.Threading;
    

    方法:

    [DllImport("user32.dll")]
    private static extern IntPtr GetForegroundWindow();
    
    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
    

    呼唤:

    // get handle
    IntPtr handle = GetForegroundWindow();
    
    // get title
    const int count = 512;
    var text = new StringBuilder(count);
    
    if (GetWindowText(handle, text, count) > 0)
    {
        MessageBox.Show(text.ToString());
    }
    

    我多次使用此代码。真的很容易使用。您可以设置一个每 10 毫秒触发一次的计时器。保存 2 个变量。一个带有活动窗口,一个带有最后一个聚焦的窗口。在伪代码中表示:If newWindow != oldWindow -> listView.Add(Window).


    最后可能是这样的:

    public partial class Form1 : Form
        {
            // DECLARE GLOBALS //
            [DllImport("user32.dll")]
            private static extern IntPtr GetForegroundWindow();
    
            [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
            private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
    
            public static string oldWindow = "";
            public static string currentWindow = "";
    
            // TIMER EVENT //
            private void timerCheckFocus_Tick(object sender, EventArgs e)
            {
                // get handle
                IntPtr handle = GetForegroundWindow();
    
                // get title
                const int count = 512;
                var currentWindow = new StringBuilder(count);
    
                // if the current title is NOT the old title - so if its a new window //
                if (currentWindow.ToString() != oldWindow)
                {
                    // add your window to a listView //
                    listView1.Add(currentWindow.ToString());
                    // save your currentWindow as oldWindow cuz it got handled //
                    oldWindow = currentWindow.ToString();
                }
            }
    

    【讨论】:

      【解决方案3】:

      这很奇怪,应该尽可能避免挂钩。为什么不直接使用GetForeGroundWindow。一旦用户点击一个窗口,它就会变成前台窗口,对吧?那么为什么需要勾住鼠标呢?

      至于你的应用程序在 Chrome 上崩溃,你应该在你的 API 调用中将 GetLastError 设置为 true,然后在每次 api 调用时打印错误代码,这样我们就可以尝试找出哪个 api 方法失败以及为什么失败。 此外,您没有准确指定调用 Thread.Sleep 失败的位置。

      其次,您可能想看看 CodeProject 上的两个非常详细的项目,它们可以完成您正在做的事情甚至更多:.NET Object SpyWinForm Spy

      【讨论】:

      • 如果你仔细观察我只使用 GetForeGroundWindow 的代码,我钩鼠标的主要目的是,我需要获取用户单击/打开的所有窗口的列表,现在这个可以通过 2 种方式完成: 1) 在每 5 秒左右(即使用 Thread.Sleep(5000))后循环调用 GetWindowUnderCursor(内部调用 GetForeGroundWindow),以便我知道在哪里用户每 5 秒或 2) 勾住鼠标点击,以便我在用户在窗口上点击鼠标时立即知道用户在哪里。这就是我使用钩子的原因
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多