根据c#中的鼠标点击获取活动窗口名称

时间:2012-03-13 18:46:59

标签: c# winapi

我正在尝试让应用程序获取用户单击的窗口的鼠标单击位置和标题(名称)。我目前使用了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秒继续获得活动窗口名称时它就不会崩溃。 请帮我找出原因? 。请帮帮我。

3 个答案:

答案 0 :(得分:2)

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

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

答案 1 :(得分:1)

此答案是我在此处发布的答复副本: 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());
}

我几次使用这段代码。真的很容易使用。你可以设置一个每10ms启动一次的计时器。节省2个变量。一个是活动窗口,另一个是最后一个窗口。在伪代码中说:如果是newWindow!= oldWindow - &gt; listView.Add(窗口)。


最后看起来像这样:

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();
            }
        }

答案 2 :(得分:0)

这很奇怪,应该尽可能避免挂钩。为什么不使用GetForeGroundWindow。一旦用户点击一个窗口,它就变成了前景窗口吗?那为什么需要挂钩鼠标?

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

其次,您可能需要查看有关CodeProject的两个非常详细的项目,它们可以执行您正在执行的操作,甚至更多:.NET Object SpyWinForm Spy