I have been using the following code to detect a change of active window with C# but on the odd occasion it simply doesn't register the change of an active window when it definitely occurs. In most instances it works absolutely fine. I've had a look into it but it seems to me that the only thing I can do is turn to C++ to get a reliable solution but before doing that I thought I would ask the question of the code below.
GlobalEventHook.Start();
GlobalEventHook.WinEventActive += new EventHandler(GlobalEventHook_WinEventActive); //set up the global hook events
private void GlobalEventHook_WinEventActive(object sender, EventArgs e)
{
// do whatever
}
using System;
using System.Collections;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace Pro
{
public static class GlobalEventHook
{
[DllImport("user32.dll")]
internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc,
WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);
public static event EventHandler WinEventActive = delegate { };
public static event EventHandler WinEventContentScrolled = delegate { };
public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject,
int idChild, uint dwEventThread, uint dwmsEventTime);
private static WinEventDelegate dele = null;
private static IntPtr _hookID = IntPtr.Zero;
public static void Start()
{
dele = new WinEventDelegate(WinEventProc);
_hookID = SetWinEventHook(Win32API.EVENT_SYSTEM_FOREGROUND, Win32API.EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, Win32API.WINEVENT_OUTOFCONTEXT);
}
public static void stop()
{
UnhookWinEvent(_hookID);
}
public static void restart()
{
_hookID = SetWinEventHook(Win32API.EVENT_SYSTEM_FOREGROUND, Win32API.EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, Win32API.WINEVENT_OUTOFCONTEXT);
}
public static void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
try
{
if (eventType == Win32API.EVENT_SYSTEM_FOREGROUND)
{
if (hwnd == Win32API.GetForegroundWindow())
{
WinEventActive(null, new EventArgs());
}
}
}
catch (Exception exc)
{
}
}
Related
I created an overlay for an application. I just need this 2 to be working:
The overlay is to be minimised if other windows are opened and maximise if it is the active window. I believe i got this working. In the code below if Notepad is opened, the overlay will minimize.
A notification if the application window is moved or resized. I cant get it to worked.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Runtime.InteropServices;
using System.ComponentModel;
using System.Windows.Forms;
using System.Diagnostics;
namespace TriageUserHelp
{
/// <summary>
/// Interaction logic for HomePage.xaml
/// </summary>
partial class HomePage : Page
{
private const uint WINEVENT_OUTOFCONTEXT = 0x0000;
private const uint EVENT_SYSTEM_FOREGROUND = 0x0003;
private const uint EVENT_SYSTEM_MOVESIZESTART = 0x000A;
private const uint EVENT_SYSTEM_MOVESIZEEND = 0x000B;
WinEventDelegate dele = null;
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
[DllImport("user32.dll")]
public static extern bool GetWindowRect(IntPtr hwnd, ref Rect rectangle);
public HomePage()
{
InitializeComponent();
StartButton.Click += StartButton_Click;
dele = new WinEventDelegate(WinEventProc);
IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
int processId = Process.GetProcessesByName("Notepad")[0].Id;
IntPtr m_hhook2 = SetWinEventHook(EVENT_SYSTEM_MOVESIZESTART, EVENT_SYSTEM_MOVESIZEEND, IntPtr.Zero, WinEventProc, (uint)processId, 0, WINEVENT_OUTOFCONTEXT);
}
private void StartButton_Click(object sender, RoutedEventArgs e)
{
var ProcessPage1 = new ProcessPage1();
NavigationService?.Navigate(ProcessPage1);
}
private string GetActiveWindowTitle()
{
const int nChars = 256;
IntPtr handle = IntPtr.Zero;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
String windowTitle = Buff.ToString();
return windowTitle;
}
return null;
}
public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
Rect move = new Rect();
if (String.Equals(GetActiveWindowTitle(), "Untitled - Notepad")) // example if notepad is opened
{
if (eventType == EVENT_SYSTEM_MOVESIZESTART)
{
GetWindowRect(hwnd, ref move);
Console.WriteLine("Window Moved Start");
}
else if (eventType == EVENT_SYSTEM_MOVESIZEEND)
{
GetWindowRect(hwnd, ref move);
Console.WriteLine("Window Moved End");
}
System.Windows.Application.Current.MainWindow.WindowState = WindowState.Maximized;
}
else
{
System.Windows.Application.Current.MainWindow.WindowState = WindowState.Minimized;
}
}
}
public struct Rect
{
public int Left { get; set; }
public int Top { get; set; }
public int Right { get; set; }
public int Bottom { get; set; }
}
}
Try this:
private void Window_SizeChanged(object sender, SizeChangedEventArgs e){/*YOUR CODE*/}
But if you using VisualStudio, it's easier to create a XAML Project, and register it.
I need your guidance masters.
Bellow follow my code. I'm capturing the title of the window when the event 3 or 8 occur and creating a counter when the event 9 occur.
My code is working great, but, when I try to get the name of the owner of windows' exe with my function GetProcessName, I'm receiving the error "CallbackOnCollectedDelegate was detected". I already did and tried all I knowed, but nothing resolve the error. The error occur after some time of start the use of the application.
When I not call my function GetProcessName, the error not happen.
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint ProcessId);
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
private const uint EVENT_SYSTEM_FOREGROUND = 3;
private const uint EVENT_SYSTEM_CAPTUREEND = 9;
private const uint EVENT_SYSTEM_CAPTURESTART = 8;
int counter = 0;
public Form1()
{
InitializeComponent();
IntPtr handle = IntPtr.Zero;
SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_CAPTUREEND, IntPtr.Zero, new WinEventDelegate(WinEventProc), 0, 0, 0);
}
public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
try
{
if (eventType == EVENT_SYSTEM_FOREGROUND || eventType == EVENT_SYSTEM_CAPTURESTART && idObject == 0)
{
aux1 = GetActiveWindowTitle(hwnd);//A function tha get a name of the title of the window
if (aux1 != aux2 && string.IsNullOrEmpty(aux1) == false)
{
GetWindowThreadProcessId(hwnd, out pid);
pnomelast = GetProcessName((int)pid);//This is the function!!
aux2 = aux1;
aux1 = "";
}
}
else if (eventType == EVENT_SYSTEM_CAPTUREEND)
{
counter = counter + 1;
}
}
catch (Exception e)
{
};
}
public static string GetProcessName(int processId)
{
try
{
return Process.GetProcessById(processId).MainModule.FileName.ToString().Split('\\').Last();
}
catch (Exception)
{
return "";
}
}
Thanks a lot.
Your issue is that SetWinEventHook is creating a new delegate on the fly which has no reference. Since it is orphaned, it will be destroyed on the next garbage collection.
First create a new WinEventDelegate
private static winEventDelegate = new WinEventDelegate(WinEventProc);
Then change:
SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_CAPTUREEND, IntPtr.Zero, new WinEventDelegate(WinEventProc), 0, 0, 0);
TO:
SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_CAPTUREEND, IntPtr.Zero, winEventDelegate, 0, 0, 0);
I made a complete and compact version of the code, that represents the problem, the error happen when the Process.GetProcessById is called. Thanks.
namespace stackoverflow1 {
public partial class Form1 : Form {
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint ProcessId);
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
private const uint EVENT_SYSTEM_FOREGROUND = 3;
private const uint EVENT_SYSTEM_CAPTURESTART = 8;
uint pid = 0;
string pnamelast = "";
public Form1()
{
InitializeComponent();
WinEventDelegate dele = new WinEventDelegate(WinEventProc);
SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_CAPTURESTART, IntPtr.Zero, dele, 0, 0, 0);
}
private void Form1_Load(object sender, EventArgs e)
{
}
public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
try
{
if (eventType == EVENT_SYSTEM_FOREGROUND || eventType == EVENT_SYSTEM_CAPTURESTART && idObject == 0)
{
GetWindowThreadProcessId(hwnd, out pid);
pnamelast = Process.GetProcessById((int)pid).MainModule.FileName.ToString().Split('\\').Last();
textBox1.AppendText(pnamelast + "\r\n");
}
}
catch
{
}
}
}
}
Is there any static event that is triggered when a handle for a Winforms control is created or when it is loaded for the first time?
This is a contrived example:
using (Form f1 = new Form())
{
f1.HandleCreated += (sender, args) => { MessageBox.Show("hi"); };
f1.ShowDialog();
}
using (Form f2 = new Form())
{
f2.HandleCreated += (sender, args) => { MessageBox.Show("hi"); };
f2.ShowDialog();
}
And this is what I want:
Form.StaticHandleCreated += (sender, args) => { MessageBox.Show("hi"); };
using (Form f1 = new Form())
{
f1.ShowDialog();
}
using (Form f2 = new Form())
{
f2.ShowDialog();
}
(I want this because I have several hundred controls, and I need to customize the default behavior of a third party control, and the vendor does not provide a better way. They specifically say to handle the OnLoad event to do the customization, but this is a massive workload.)
Thanks to xtu, here is the working version that doesn't hold onto the forms longer than necessary, to avoid memory leaks.
public class WinFormMonitor : IDisposable
{
private readonly IntPtr _eventHook;
private List<Form> _detectedForms = new List<Form>();
public event Action<Form> NewFormDetected;
private WinEventDelegate _winEventDelegate;
public WinFormMonitor()
{
_winEventDelegate = WinEventProc;
_eventHook = SetWinEventHook(
EVENT_OBJECT_CREATE,
EVENT_OBJECT_CREATE,
IntPtr.Zero,
_winEventDelegate,
0,
0,
WINEVENT_OUTOFCONTEXT);
}
public void Dispose()
{
_detectedForms.Clear();
UnhookWinEvent(_eventHook);
}
private void WinEventProc(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (idObject != 0 || idChild != 0) return;
var currentForms = Application.OpenForms.OfType<Form>().ToList();
var newForms = currentForms.Except(_detectedForms);
foreach (var f in newForms)
{
NewFormDetected?.Invoke(f);
}
_detectedForms = currentForms;
}
private const uint EVENT_OBJECT_CREATE = 0x8000;
private const uint WINEVENT_OUTOFCONTEXT = 0;
private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr
hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess,
uint idThread, uint dwFlags);
[DllImport("user32.dll")]
private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
}
I was inspired by this answer and used SetWinEventHook API to create a new WinForm monitor. Basically, it monitors the EVENT_OBJECT_CREATE event and fires an event whenever a new window is found.
The usage is very simple. You create a new instance and then attach a handler to the NewFormCreated event. Here is an example:
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
using (var monitor = new WinFormMonitor())
{
monitor.NewFormCreated += (sender, form) => { MessageBox.Show($"hi {form.Text}"); };
Application.Run(new Form1());
}
}
And here is the source of the WinFormMonitor:
public class WinFormMonitor : IDisposable
{
private readonly IntPtr _eventHook;
private readonly IList<int> _detectedFormHashes = new List<int>();
public event EventHandler<Form> NewFormCreated = (sender, form) => { };
public WinFormMonitor()
{
_eventHook = SetWinEventHook(
EVENT_OBJECT_CREATE,
EVENT_OBJECT_CREATE,
IntPtr.Zero,
WinEventProc,
0,
0,
WINEVENT_OUTOFCONTEXT);
}
public void Dispose()
{
_detectedFormHashes.Clear();
UnhookWinEvent(_eventHook);
}
private void WinEventProc(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
// filter out non-HWND namechanges... (eg. items within a listbox)
if (idObject != 0 || idChild != 0) return;
if (!TryFindForm(hwnd, out var foundForm)) return;
RaiseIfNewFormFound(foundForm);
}
private void RaiseIfNewFormFound(Form foundForm)
{
var formHash = foundForm.GetHashCode();
if (_detectedFormHashes.Contains(formHash)) return;
NewFormCreated(this, foundForm);
_detectedFormHashes.Add(formHash);
}
private static bool TryFindForm(IntPtr handle, out Form foundForm)
{
foreach (Form openForm in Application.OpenForms)
{
if (openForm.Handle != handle) continue;
foundForm = openForm;
return true;
}
foundForm = null;
return false;
}
private const uint EVENT_OBJECT_CREATE = 0x8000;
private const uint WINEVENT_OUTOFCONTEXT = 0;
private delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr
hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess,
uint idThread, uint dwFlags);
[DllImport("user32.dll")]
private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
}
I have some code here that is targeting windows event hooks in order to write to a log file when triggered. I am running this in powershell. I have successfully used this code to log mouse/keyboard events however when I use WH_CBT 5 using the CBTProc callback I receive no events. Even when using a mouse target of WH_MOUSE_LL 14 works just fine... can someone explain why? Have I missed something... or is it not possible for some reason?
https://msdn.microsoft.com/en-us/library/windows/desktop/ms644990(v=vs.85).aspx
Add-Type -TypeDefinition #"
using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace MyLogger {
public static class Program {
private const int HOOK_CODE = 5;
private const int CALLBACK_CODE = 9;
private const string logPath = #"c:\MyTest.txt";
private const string logFileName = "log.txt";
private static StreamWriter logFile;
private static HookProc hookProc = HookCallback;
private static IntPtr hookId = IntPtr.Zero;
public static void Main() {
logFile = File.AppendText(logPath);
logFile.AutoFlush = true;
hookId = SetHook(hookProc);
Application.Run();
UnhookWindowsHookEx(hookId);
}
private static IntPtr SetHook(HookProc hookProc) {
IntPtr moduleHandle = GetModuleHandle(Process.GetCurrentProcess().MainModule.ModuleName);
return SetWindowsHookEx(HOOK_CODE, hookProc, moduleHandle, 0);
}
private delegate IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam);
private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam) {
logFile.WriteLine("gg");
return CallNextHookEx(hookId, nCode, wParam, lParam);
}
[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, 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);
[DllImport("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
}
"# -ReferencedAssemblies System.Windows.Forms
[MyLogger.Program]::Main();
Modded code
Add-Type -TypeDefinition #"
using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace MyLogger {
public static class Program {
private const int WINEVENT_OUTOFCONTEXT = 0;
private const int EVENT_OBJECT_FOCUS = 0x8005;
private const string logPath = #"c:\MyTest.txt";
private const string logFileName = "log.txt";
private static StreamWriter logFile;
private static HookProc hookProc = HookCallback;
private static IntPtr hookId = IntPtr.Zero;
public static void Main() {
logFile = File.AppendText(logPath);
logFile.AutoFlush = true;
hookId = SetHook(hookProc);
Application.Run();
}
private static IntPtr SetHook(HookProc hookProc) {
return SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, null, hookProc, 0, 0, WINEVENT_OUTOFCONTEXT);
}
private delegate IntPtr HookProc(IntPtr hWinEventHook, int iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime);
private static IntPtr HookCallback(IntPtr hWinEventHook, int iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime) {
logFile.WriteLine("gg");
}
internal enum SetWinEventHookFlags
{
WINEVENT_INCONTEXT = 4,
WINEVENT_OUTOFCONTEXT = 0,
WINEVENT_SKIPOWNPROCESS = 2,
WINEVENT_SKIPOWNTHREAD = 1
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWinEventHook(int eventMin, int eventMax, IntPtr hmodWinEventProc, HookProc lpfnWinEventProc, int idProcess, int idThread, int dwflags);
private static extern int UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll")]
private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);
[DllImport("kernel32.dll")]
private static extern IntPtr GetModuleHandle(string lpModuleName);
}
}
"# -ReferencedAssemblies System.Windows.Forms
[MyLogger.Program]::Main();
This code logs the HWND that has the focus.
Add-Type -TypeDefinition #"
using System;
using System.IO;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace MyLogger {
public static class Program {
private const int WINEVENT_OUTOFCONTEXT = 0;
private const int EVENT_OBJECT_FOCUS = 0x8005;
private const int WM_GETTEXT = 0x000D;
private const string logPath = #"c:\Temp\MyTest.txt";
private static StreamWriter logFile;
private static HookProc hookProc = HookCallback;
private static IntPtr hookId = IntPtr.Zero;
public static void Main() {
logFile = File.AppendText(logPath);
logFile.AutoFlush = true;
hookId = SetHook(hookProc);
Application.Run();
}
private static IntPtr SetHook(HookProc hookProc) {
return SetWinEventHook(EVENT_OBJECT_FOCUS, EVENT_OBJECT_FOCUS, IntPtr.Zero, hookProc, 0, 0, WINEVENT_OUTOFCONTEXT);
}
private delegate void HookProc(IntPtr hWinEventHook, int iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime);
private static void HookCallback(IntPtr hWinEventHook, int iEvent, IntPtr hWnd, int idObject, int idChild, int dwEventThread, int dwmsEventTime) {
logFile.WriteLine(string.Format("{0}", hWnd));
}
internal enum SetWinEventHookFlags
{
WINEVENT_INCONTEXT = 4,
WINEVENT_OUTOFCONTEXT = 0,
WINEVENT_SKIPOWNPROCESS = 2,
WINEVENT_SKIPOWNTHREAD = 1
}
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWinEventHook(int eventMin, int eventMax, IntPtr hmodWinEventProc, HookProc lpfnWinEventProc, int idProcess, int idThread, int dwflags);
[DllImport("user32.dll")]
private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
}
}
"# -ReferencedAssemblies System.Windows.Forms
[MyLogger.Program]::Main();
The hWnd passed to HookCallback can be a child window (like a list control or tree control, etc.), it is not always the outermost application window like you might be expecting from WH_CBT.
If you need the outermost application window, you can simply do something like:
HWND hwnd = hWndPassedToHookCallback;
HWND hwndApp;
do
{
hwndApp = hwnd;
hwnd = GetParent(hwnd)
} while(hwnd);
// hwndApp now is the outermost application window
Unlike WH_MOUSE_LL, the callback for WH_CBT must be in the process that is hooked; hence the callback must be in a DLL.
I have a problem in last two days i want to get processes of users which he clicked. like if a user clicks Notepad my program should tell me that user clicked Notepad. Notepad is opened. And if user clicks Calculator my program also tell that user clicked Calculator. Calcultor process is runing.
For this purpose i used this code. Hook manager which gives me mouse click events but not giving me the process.
I am only getting mouse intptr event.
private static void WindowEventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
Console.WriteLine("Event {0}", hwnd);// it is giving me mouse event
/*uint pid;
GetWindowThreadProcessId(hwnd, out pid);// it gives me process id
Process p = Process.GetProcessById((int)pid);// now here exception occured not in vs studio but when i run its exe then its gives me access violation exception
if (!my.ContainsKey(p.MainWindowTitle.ToString()))
{
my.Add(p.MainWindowTitle.ToString(), p.Id.ToString());
Console.WriteLine("\r\n");
Console.WriteLine("Status = Running");
Console.WriteLine("\r\n Window Title:" + p.MainWindowTitle.ToString());
Console.WriteLine("\r\n Process Name:" + p.ProcessName.ToString());
Console.WriteLine("\r\n Process Starting Time:" + p.StartTime.ToString());
}*/
}
the full code is
static void Main(string[] args)
{
HookManager.SubscribeToWindowEvents();
EventLoop.Run();
}
public static class HookManager
{
[DllImport("user32.dll")]
public static extern IntPtr GetWindowThreadProcessId(IntPtr hWnd, out uint ProcessId);
public static void SubscribeToWindowEvents()
{
if (windowEventHook == IntPtr.Zero)
{
windowEventHook = SetWinEventHook(
EVENT_SYSTEM_FOREGROUND, // eventMin
EVENT_SYSTEM_FOREGROUND, // eventMax
IntPtr.Zero, // hmodWinEventProc
WindowEventCallback, // lpfnWinEventProc
0, // idProcess
0, // idThread
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
if (windowEventHook == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
static Dictionary<string, string> my = new Dictionary<string, string>();
private static void WindowEventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
Console.WriteLine("Event {0}", hwnd);
/*uint pid;
GetWindowThreadProcessId(hwnd, out pid);
Process p = Process.GetProcessById((int)pid);
if (!my.ContainsKey(p.MainWindowTitle.ToString()))
{
my.Add(p.MainWindowTitle.ToString(), p.Id.ToString());
Console.WriteLine("\r\n");
Console.WriteLine("Status = Running");
Console.WriteLine("\r\n Window Title:" + p.MainWindowTitle.ToString());
Console.WriteLine("\r\n Process Name:" + p.ProcessName.ToString());
Console.WriteLine("\r\n Process Starting Time:" + p.StartTime.ToString());
}*/
}
}
private static IntPtr windowEventHook;
private delegate void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll", SetLastError = true)]
private static extern IntPtr SetWinEventHook(int eventMin, int eventMax, IntPtr hmodWinEventProc, WinEventProc lpfnWinEventProc, int idProcess, int idThread, int dwflags);
[DllImport("user32.dll", SetLastError = true)]
private static extern int UnhookWinEvent(IntPtr hWinEventHook);
private const int WINEVENT_INCONTEXT = 4;
private const int WINEVENT_OUTOFCONTEXT = 0;
private const int WINEVENT_SKIPOWNPROCESS = 2;
private const int WINEVENT_SKIPOWNTHREAD = 1;
private const int EVENT_SYSTEM_FOREGROUND = 3;
public static class EventLoop
{
public static void Run()
{
MSG msg;
while (true)
{
if (PeekMessage(out msg, IntPtr.Zero, 0, 0, PM_REMOVE))
{
if (msg.Message == WM_QUIT)
break;
TranslateMessage(ref msg);
DispatchMessage(ref msg);
}
}
}
[StructLayout(LayoutKind.Sequential)]
private struct MSG
{
public IntPtr Hwnd;
public uint Message;
public IntPtr WParam;
public IntPtr LParam;
public uint Time;
}
const uint PM_NOREMOVE = 0;
const uint PM_REMOVE = 1;
const uint WM_QUIT = 0x0012;
[DllImport("user32.dll")]
private static extern bool PeekMessage(out MSG lpMsg, IntPtr hwnd, uint wMsgFilterMin, uint wMsgFilterMax, uint wRemoveMsg);
[DllImport("user32.dll")]
private static extern bool TranslateMessage(ref MSG lpMsg);
[DllImport("user32.dll")]
private static extern IntPtr DispatchMessage(ref MSG lpMsg);
}
}
If you want to put your logic inside the mouse click handler you can simply call GetActiveWindow to get window handle (if you dont already have it). Then you can use GetWindowThreadProcessId to get process id from window handle.
Doing this with every mouse click looks like an overkill, however. You should probably think about hooking to active window change. Check this for details: Is there Windows system event on active window changed?