In my application I need to know when the user switches the virtual Desktop, e.g. by pressing Ctrl+Win+→.
I though it'd be a great idea to do this via Hooking. I have listed an example class I wrote to test my idea. I thought when the virtual Desktop changes, I would get a callback. However, there is no callback no matter how I change the virtual Desktop.
I also wrote a test application that creates, opens, switches and closes Desktops. It works fine, but my code below detects none of the Desktop switches.
public class SwitchDesktopMonitor
{
private delegate void CreateHookDelegate();
private delegate void SetWinEventHookCallback(IntPtr hWinEventHook, uint eventType, IntPtr hWnd, uint objectId, int childId, uint dwEventThread, uint dwmsEventTime);
[DllImport("user32.dll")]
private static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, SetWinEventHookCallback lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
private static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll", SetLastError = true)]
private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint processId);
private IntPtr _setWinEventHook;
private readonly SetWinEventHookCallback _hookingCallback;
private readonly Window _window;
public SwitchDesktopMonitor(Window window)
{
_window = window;
_hookingCallback = (hWinEventHook, eventType, hWnd, objectId, childId, dwEventThread, dwmsEventTime) =>
{
Console.WriteLine("-> _hookingCallback - hWinEventHook = {0}, eventType = {1}, hWnd = {2}, objectId = {3}, childId = {4}, dwEventThread = {5}, dwmsEventTime = {6}",
hWinEventHook, eventType, hWnd, objectId, childId, dwEventThread, dwmsEventTime);
};
}
public void Start()
{
Console.WriteLine("{0}.Start", this);
if (_window == null || _window.Dispatcher == null)
{
return;
}
if (_window.Dispatcher.CheckAccess())
{
CreateHook();
}
else
{
_window.Dispatcher.Invoke(new CreateHookDelegate(CreateHook));
}
}
public void Stop()
{
Console.WriteLine("{0}.Stop", this);
if (_setWinEventHook != null)
{
Console.WriteLine("\tUnhookWinEvent = {0}", UnhookWinEvent(_setWinEventHook));
}
}
private void CreateHook()
{
var windowHandle = new WindowInteropHelper(_window).Handle;
uint processId;
uint threadId = GetWindowThreadProcessId(windowHandle, out processId);
Console.WriteLine("\twindowHandle = {0}, processId = {1}, threadId = {2}", windowHandle, processId, threadId);
_setWinEventHook = SetWinEventHook(0x0020, 0x0020, IntPtr.Zero, _hookingCallback, processId, threadId, 0x0000);
Console.WriteLine("\t_setWinEventHook = {0}", _setWinEventHook);
}
}
I don't have to do this way. I am thankful or other approaches. The only important thing is that I need to detect Windows Desktop switches.
You install a hook for the event EVENT_SYSTEM_DESKTOPSWITCH (0x0020). However, the term desktop here does not refer to "virtual desktops" as a feature in Windows 10 to switch between different sets of windows, but to desktops as a system concept in Windows to separate different operating environments (Normal, Logon, Screensaver).
So, you cannot detect a switching of virtual desktops this way.
Instead, use the way as shown in this open source library https://github.com/Grabacr07/VirtualDesktop which uses a currently undocumented VirtualDesktopNotificationService COM service in the Windows Shell to be notified when the current virtual desktop changes.
Related
The goal
I developed a keyboard in Unity3D (C#) and want it to pop up when the users click on "EDIT" type control such as a address bar or an input field. Therefore, I need to detect when an "EDIT" control is clicked.
What I've tried
Currently I use SetWinEventHook and listen to event EVENT_OBJECT_FOCUS to get the handle of the object which gets the focus. After that, I use GetClassName to see if the focused object is an "EDIT" control which displays a flashing caret when clicked. However, take Google Chrome as an example, I always get Chrome_WidgetWin_1 whether I click the address bar or the plain text of the page. After doing some googling I found this blog post What makes RealGetWindowClass so much more real than GetClassName? saying that RealGetWindowClass can get the base class which I think it will be something like "EDIT" or "COMBOBOX" listed here. Things were not going so well. I tried using RealGetWindowClass and still get the same result Chrome_WidgetWin_1.
The problem
Why do GetClassName and RealGetWindowClass return the same value? How should I make RealGetWindowClass return the base class?
The code
[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);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
[DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
static extern uint RealGetWindowClass(IntPtr hwnd, [Out] StringBuilder pszType, uint cchType);
private delegate void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
private const int WINEVENT_SKIPOWNPROCESS = 2;
private IntPtr windowEventHook;
private const int EVENT_OBJECT_FOCUS = 0x8005;
private void Start()
{
if (windowEventHook == IntPtr.Zero)
{
windowEventHook = SetWinEventHook(
EVENT_OBJECT_FOCUS,
EVENT_OBJECT_FOCUS,
IntPtr.Zero,
WindowEventCallback,
0,
0,
WINEVENT_SKIPOWNPROCESS);
if (windowEventHook == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
private void OnDestroy()
{
UnhookWinEvent(windowEventHook);
}
private void WindowEventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
UnityEngine.Debug.Log($"[EventType]: {(EventEnum)eventType} [Class Name]: {GetClassName(hWnd)} [Real Class]: {RealGetWindowClassM(hWnd)}");
// Will print out the same log whether I click an address bar or plain text.
// [EventType]: EVENT_OBJECT_FOCUS [Class Name]: Chrome_WidgetWin_1 [Real Class]: Chrome_WidgetWin_1
}
private string GetClassName(IntPtr hWnd)
{
StringBuilder className = new StringBuilder(256);
GetClassName(hWnd, className, className.Capacity);
return className.ToString();
}
private string RealGetWindowClassM(IntPtr hWnd)
{
StringBuilder className = new StringBuilder(256);
RealGetWindowClass(hWnd, className, (UInt32)className.Capacity);
return className.ToString();
}
I am trying to add to my time tracker window caption tracking using SetWindowsHookEx, but it is works only partially. Here is my code I using to provide subscribers with coresponding event:
public class Hooks
{
#region DllImport
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);
private const uint WINEVENT_SKIPOWNPROCESS = 2;
private const uint WINEVENT_SKIPOWNTHREAD = 1;
private const uint WINEVENT_OUTOFCONTEXT = 0;
private const uint EVENT_SYSTEM_FOREGROUND = 3;
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
#endregion DllImport
public static event EventHandler<EventArgs<string>> WindowActivated;
public void Bind()
{
var listener = new WinEventDelegate(EventCallback);
var result = SetWinEventHook(EVENT_SYSTEM_FOREGROUND,
EVENT_SYSTEM_FOREGROUND,
IntPtr.Zero,
listener,
0,
0,
(WINEVENT_OUTOFCONTEXT));
}
private static void EventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd,
int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
System.Diagnostics.Debug.Write("EventCallback enter!");
if (eventType == EVENT_SYSTEM_FOREGROUND)
{
var buffer = new StringBuilder(300);
var handle = GetForegroundWindow();
System.Diagnostics.Debug.Write(string.Format("EventCallback GetWindowText: {0}, '{1}'",
GetWindowText(handle, buffer, 300),
buffer));
if (GetWindowText(handle, buffer, 300) > 0 && WindowActivated != null)
{
System.Diagnostics.Debug.Write(string.Format(
"Calling handlers for WindowActivated with buffer='{0}'",
buffer));
WindowActivated(handle, new EventArgs<string>(buffer.ToString()));
}
}
System.Diagnostics.Debug.Write("EventCallback leave!");
}
}
I have main ui WPF application with single Textbox and I bind Hook's event to text box content. When I run it it looks work fine, until itself focused. I.e. if I clicked texbox of WPF window becomes active hook stops working for about 30-40 seconds. After that events capturing becomes work, but again - until main WPF windows becomes active.
Any ideas what is it and how to fix it?
I created a new WPF application using the standard Visual Studio template (VS2012; .NET 4.5). I then replaced the XAML and code-behind as follows:
MainWindow.xaml
<Window x:Class="stackoverflowtest.MainWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Title="MainWindow">
<Grid>
<StackPanel VerticalAlignment="Top" Margin="15">
<TextBox x:Name="_tb" />
<Button Content="Does Nothing" HorizontalAlignment="Left" Margin="0,15" />
</StackPanel>
</Grid>
</Window>
MainWindow.xaml.cs
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
using System.Windows.Controls;
namespace stackoverflowtest
{
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
_hooks = new Hooks(_tb);
_hooks.Bind();
}
readonly Hooks _hooks;
}
public class Hooks
{
public Hooks(TextBox textbox)
{
_listener = EventCallback;
_textbox = textbox;
}
readonly WinEventDelegate _listener;
readonly TextBox _textbox;
IntPtr _result;
public void Bind()
{
_result = SetWinEventHook(
EVENT_SYSTEM_FOREGROUND,
EVENT_SYSTEM_FOREGROUND,
IntPtr.Zero,
_listener,
0,
0,
WINEVENT_OUTOFCONTEXT
);
}
void EventCallback(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild,
uint dwEventThread, uint dwmsEventTime)
{
var windowTitle = new StringBuilder(300);
GetWindowText(hwnd, windowTitle, 300);
_textbox.Text = windowTitle.ToString();
}
#region P/Invoke
const uint WINEVENT_OUTOFCONTEXT = 0;
const uint EVENT_SYSTEM_FOREGROUND = 3;
[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 int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject,
int idChild, uint dwEventThread, uint dwmsEventTime);
#endregion
}
}
For me, this works fine. I can switch between different applications and their window titles are reflected in the textbox of the WPF app. There is no delay even when the textbox is focused. I added a do-nothing Button control just to prove that whether the texbox or the button is focused it does not change the behaviour.
I can only assume the problem you are having is related to something you are doing in your event handler, or failing that some kind of threading issue.
Probably the reason is that the delegate was garbage collected since it is assigned into a local variable. That's why it stopped to work after 30-40 sec.
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?
How can I prevent WPF window from minimizing when users clicks on show desktop button?
This link will help you : Get the minimize box click of a WPF window
you need to catch the event and handle it yourself.
Edit : This method will alert you once the state is changed, so it might not be the "best" solution but it could work.
Windows are not minimized when "Show Desktop" is issued. Instead the "WorkerW" and "Desktop" windows are brought to the foreground.
I ended up developing my own solution.
I scoured the internet for weeks trying to find an answer so I'm kind of proud of this one.
So what we do is use pinvoke to create a hook for the EVENT_SYSTEM_FOREGROUND window event.
This event triggers whenever the foreground window is changed.
Now what I noticed is when the "Show Desktop" command is issued, the WorkerW window class becomes foreground.
Note this WorkerW window is not the desktop and I confirmed the hwnd of this WorkerW window is not the Desktop hwnd.
So what we do is whenever the WorkerW window becomes the foreground, we set our "WPF Gadget Window" to be topmost!
Whenever a window other the WorkerW window becomes the foreground, we remove topmost from our "WPF Gadget Window".
If you want to take it a step further, you can uncomment out the part where I check if the new foreground window is also "PROGMAN", which is the Desktop window.
However, this will lead to your window becoming topmost if the user clicks their desktop on a different monitor. In my case, I did not want this behavior, but I figured some of you might.
Confirmed to work in Windows 10. Should work in older versions of Windows.
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows;
namespace YourNamespace
{
internal static class NativeMethods
{
[DllImport("user32.dll")]
internal static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, ShowDesktop.WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, uint dwFlags);
[DllImport("user32.dll")]
internal static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll")]
internal static extern int GetClassName(IntPtr hwnd, StringBuilder name, int count);
}
public static class ShowDesktop
{
private const uint WINEVENT_OUTOFCONTEXT = 0u;
private const uint EVENT_SYSTEM_FOREGROUND = 3u;
private const string WORKERW = "WorkerW";
private const string PROGMAN = "Progman";
public static void AddHook(Window window)
{
if (IsHooked)
{
return;
}
IsHooked = true;
_delegate = new WinEventDelegate(WinEventHook);
_hookIntPtr = NativeMethods.SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, _delegate, 0, 0, WINEVENT_OUTOFCONTEXT);
_window = window;
}
public static void RemoveHook()
{
if (!IsHooked)
{
return;
}
IsHooked = false;
NativeMethods.UnhookWinEvent(_hookIntPtr.Value);
_delegate = null;
_hookIntPtr = null;
_window = null;
}
private static string GetWindowClass(IntPtr hwnd)
{
StringBuilder _sb = new StringBuilder(32);
NativeMethods.GetClassName(hwnd, _sb, _sb.Capacity);
return _sb.ToString();
}
internal delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
private static void WinEventHook(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (eventType == EVENT_SYSTEM_FOREGROUND)
{
string _class = GetWindowClass(hwnd);
if (string.Equals(_class, WORKERW, StringComparison.Ordinal) /*|| string.Equals(_class, PROGMAN, StringComparison.Ordinal)*/ )
{
_window.Topmost = true;
}
else
{
_window.Topmost = false;
}
}
}
public static bool IsHooked { get; private set; } = false;
private static IntPtr? _hookIntPtr { get; set; }
private static WinEventDelegate _delegate { get; set; }
private static Window _window { get; set; }
}
}
You can change your window's parent to not be affected by Show Desktop. (as stated here: Window "on desktop")
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
Loaded += MainWindowLoaded;
}
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpszClass, string lpszWindow);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr SetParent(IntPtr hWndChild, IntPtr hWndNewParent);
private void MainWindowLoaded(object sender, RoutedEventArgs e)
{
var hwnd = new WindowInteropHelper(this).Handle;
var ProgmanHwnd = FindWindowEx(FindWindowEx(FindWindow("Progman", "Program Manager"), IntPtr.Zero, "SHELLDLL_DefView",""), IntPtr.Zero,"SysListView32", "FolderView");
SetParent(hwnd, ProgmanHwnd);
}
}
For example if the user is currently running VS2008 then I want the value VS2008.
I am assuming you want to get the name of the process owning the currently focused window. With some P/Invoke:
// The GetForegroundWindow function returns a handle to the foreground window
// (the window with which the user is currently working).
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
// The GetWindowThreadProcessId function retrieves the identifier of the thread
// that created the specified window and, optionally, the identifier of the
// process that created the window.
[System.Runtime.InteropServices.DllImport("user32.dll")]
private static extern Int32 GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
// Returns the name of the process owning the foreground window.
private string GetForegroundProcessName()
{
IntPtr hwnd = GetForegroundWindow();
// The foreground window can be NULL in certain circumstances,
// such as when a window is losing activation.
if (hwnd == null)
return "Unknown";
uint pid;
GetWindowThreadProcessId(hwnd, out pid);
foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
{
if (p.Id == pid)
return p.ProcessName;
}
return "Unknown";
}
using System;
using System.Windows;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace FGHook
{
class ForegroundTracker
{
// Delegate and imports from pinvoke.net:
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 Int32 GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
[DllImport("user32.dll")]
static extern bool UnhookWinEvent(IntPtr hWinEventHook);
// Constants from winuser.h
const uint EVENT_SYSTEM_FOREGROUND = 3;
const uint WINEVENT_OUTOFCONTEXT = 0;
// Need to ensure delegate is not collected while we're using it,
// storing it in a class field is simplest way to do this.
static WinEventDelegate procDelegate = new WinEventDelegate(WinEventProc);
public static void Main()
{
// Listen for foreground changes across all processes/threads on current desktop...
IntPtr hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero,
procDelegate, 0, 0, WINEVENT_OUTOFCONTEXT);
// MessageBox provides the necessary mesage loop that SetWinEventHook requires.
MessageBox.Show("Tracking focus, close message box to exit.");
UnhookWinEvent(hhook);
}
static void WinEventProc(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
Console.WriteLine("Foreground changed to {0:x8}", hwnd.ToInt32());
//Console.WriteLine("ObjectID changed to {0:x8}", idObject);
//Console.WriteLine("ChildID changed to {0:x8}", idChild);
GetForegroundProcessName();
}
static void GetForegroundProcessName()
{
IntPtr hwnd = GetForegroundWindow();
// The foreground window can be NULL in certain circumstances,
// such as when a window is losing activation.
if (hwnd == null)
return;
uint pid;
GetWindowThreadProcessId(hwnd, out pid);
foreach (System.Diagnostics.Process p in System.Diagnostics.Process.GetProcesses())
{
if (p.Id == pid)
{
Console.WriteLine("Pid is: {0}",pid);
Console.WriteLine("Process name is {0}",p.ProcessName);
return;
}
//return;
}
Console.WriteLine("Unknown");
}
}
}