2

I am trying to make an application get the mouse click location and titles(names ) of the windows on which the user clicks. I at present have used the LowLevelMouseProc, which gives fine results but it makes the application crash whenever I click on Google chrome. Here is the code:

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

Though when I don't use low level hook and just use Thread.sleep(5000) and keep getting active window names every 5 seconds then it does not crash. Please help me find out why? . Please help me.

Ajit
  • 964
  • 10
  • 17
  • You can use `GetCursorPos` to get the screen coordinates of a click, `WindowFromPoint` to retrieve the window associated with that click, `GetWindowText` to get the window's title, and `ScreenToClient` if you need the coordinates to be relative to the window. If you need the window itself, and not a child, you can use `GetAncestor`. Hope some of that comes in handy. A hook is definitely unnecessary. – chris Mar 14 '12 at 15:20
  • @chris : I really appreciate your approach , but I do not understand how can I track all the mouse clicks (which will happen over various windows, the the titles of which I need to retrieve) if I don't hook the mouse clicks. Assume I use a GetWindowUnderCursor() which does ScreenToClient and WindowFromPoint calls, then either i'll have to call this function (GetWindowsUnderCursor) every 5 seconds to cursor is on which window now, or else hook the mouse click so that I get notified of the window name as soon as the user clicks on a window, have used GetForegroundWindow for that. – Ajit Mar 15 '12 at 12:37

3 Answers3

2

You need to specify capacity for StringBuilder. To be more thorough you can use GetWindowTextLength as described here.

StringBuilder windowtitle = new StringBuilder(256);
if (GetWindowText(hwnd, windowtitle, windowtitle.Capacity) > 0)
    Console.WriteLine(windowtitle);
Raj Ranjhan
  • 3,869
  • 2
  • 19
  • 29
1

This answer is a copy of my answere posted here: How to get active window name based on mouse[...]
This code is out of the topic monitoring the mouse. Its about which window got the focus. Also notice: This is just about what you asked for - the window name/title -.


Reference: How to get active window handle and title

Namespaces:

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

Methods:

[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);

The Call:

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

I used this code a few times. Really easy to use. You could set up an timer which fires up every 10ms. Save up 2 variables. One with the active window and one with the last window that was focused. In pseudo-code said: If newWindow != oldWindow -> listView.Add(Window).


Finally it could look like this:

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();
            }
        }
Community
  • 1
  • 1
C4d
  • 3,183
  • 4
  • 29
  • 50
0

That's very strange, hooking should be avoided whenever possible. Why not just use GetForeGroundWindow. Once the user clicks a window, it becomes the foreground window right? So why the need to hook the mouse?

As for your application crashing on Chrome, you should set GetLastError as true in your API calls, then print the error codes at each api call, that way we can try to find out which api method is failing and why. Also you didn't exactly specify where you call your Thread.Sleep which fails.

Secondly, you might want to take a look at two really detailed projects on CodeProject that does what you're doing and even more: .NET Object Spy and WinForm Spy.

Chibueze Opata
  • 9,856
  • 7
  • 42
  • 65
  • 1
    if you carefully observe the code i have used GetForeGroundWindow only, the main intention of me hooking the mouse was , I needed to get a list of all windows on which the user was clicking/opening, now this could be done in 2 ways: 1) either call the GetWindowUnderCursor( which'd internally call GetForeGroundWindow) in a loop after every 5 secs or so (that is use a Thread.Sleep(5000)) so that i get to know where the user is every 5 seconds or 2) hook the mouse clicks so that I get to know where the user is as soon as he clicks the mouse on a window. That is why i have used the hook – Ajit Mar 15 '12 at 12:41