This question is very similar to this one (Detect active window changed using C# without polling), and the code on that accepted answer works fine with a Windows Forms Application, but no way with a Console Application.
¿How can I detect that active window has changed without doing infinite iterations (or any type of polling) with a Console Application?
Thanks in advance.
It can be changed to run as a console application with a few changes. Here is a working code.
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace ConsoleApplication1
{
class Program
{
static void Main(string[] args)
{
dele = new WinEventDelegate(WinEventProc);
IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
Application.Run(); //<----
}
static WinEventDelegate dele = null; //STATIC
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_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);
private static string GetActiveWindowTitle() //STATIC
{
const int nChars = 256;
IntPtr handle = IntPtr.Zero;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
public static void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) //STATIC
{
Console.WriteLine(GetActiveWindowTitle());
}
}
}
Related
I have this class that listens for when the CTRL + ALT + DEL screen is visible. When I run my app it only works one time then the callback is never hit again. Occasionally it appears to cause a memory leak giving me a System.AccessViolationException. I know this Exception is related to this hook because when I remove the hook code it never raises this exceptions.
What am I doing wrong? Why would it only execute the callback once?
public static void StartListeningForDesktopSwitch()
{
SetWinEventHook(EVENT_SYSTEM_DESKTOPSWITCH, EVENT_SYSTEM_DESKTOPSWITCH,
IntPtr.Zero, EventCallback, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNTHREAD);
}
public static void EventCallback(IntPtr hWinEventHook, uint eventType,
IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
//do stuff when secure desktop is shown or hidden
Log.LogEvent("Info", "Secure Desktop Event", "", "", null);
}
public 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);
const uint WINEVENT_OUTOFCONTEXT = 0x0000;
const uint WINEVENT_SKIPOWNTHREAD = 0x0001;
const uint EVENT_SYSTEM_DESKTOPSWITCH = 0x0020;
I'm calling this static class from Main() like this:
WindowEventHook.StartListeningForDesktopSwitch();
How did you use the outside variable?
Try storing the callback in a static variable to keep it from being GCed. Like this:
public static class WindowEventHook
{
private static readonly WinEventDelegate callback = EventCallback;
public static void StartListeningForDesktopSwitch()
{
SetWinEventHook(EVENT_SYSTEM_DESKTOPSWITCH, EVENT_SYSTEM_DESKTOPSWITCH,
IntPtr.Zero, callback, 0, 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNTHREAD);
}
...
}
Currently I'm using the following code to get active window title-
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
private static extern int GetWindowText(IntPtr hWnd, StringBuilder text, int count);
It's working great.
Now I would like to capture global active window change event in my C# app. I don't know how it can be done.
Any help?
Here is a complete code snippet taken from here-
using System;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
namespace ActiveWindowChangeEvent
{
public partial class Form1 : Form
{
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
WinEventDelegate dele = null;
[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_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);
private string GetActiveWindowTitle()
{
const int nChars = 256;
IntPtr handle = IntPtr.Zero;
StringBuilder Buff = new StringBuilder(nChars);
handle = GetForegroundWindow();
if (GetWindowText(handle, Buff, nChars) > 0)
{
return Buff.ToString();
}
return null;
}
public void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
Log.Text += GetActiveWindowTitle() + "\r\n"; //Log= RichTextBox
}
public Form1()
{
InitializeComponent();
dele = new WinEventDelegate(WinEventProc);
IntPtr m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, dele, 0, 0, WINEVENT_OUTOFCONTEXT);
}
}
}
Hope, this will help.
What i mean is for example i'm now watching the visual studio window then i click on the bottom on the Chrome icon and now i'm in the Chrome window .
I want it to detect that the window i'm in now is changed . Not if i clicked on the chrome icon or on the visual studio icon but to detect somehow that i changed the window i'm watching/active/using now .
For example this code :
In the top of my Form1 i added :
public delegate void ActiveWindowChangedHandler(object sender, String windowHeader, IntPtr hwnd);
public event ActiveWindowChangedHandler ActiveWindowChanged;
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[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);
const uint WINEVENT_OUTOFCONTEXT = 0;
const uint EVENT_SYSTEM_FOREGROUND = 3;
[DllImport("user32.dll")]
static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax,
IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc,
uint idProcess, uint idThread, uint dwFlags);
IntPtr m_hhook;
private WinEventDelegate _winEventProc;
Then in the constructor :
_winEventProc = new WinEventDelegate(WinEventProc);
m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND,
EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, _winEventProc,
0, 0, WINEVENT_OUTOFCONTEXT);
Then added events and functions :
void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd,
int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (eventType == EVENT_SYSTEM_FOREGROUND)
{
if (ActiveWindowChanged != null)
ActiveWindowChanged(this,GetActiveWindowTitle(hwnd),hwnd);
}
}
private string GetActiveWindowTitle(IntPtr hwnd)
{
StringBuilder Buff = new StringBuilder(500);
GetWindowText(hwnd, Buff, Buff.Capacity);
return Buff.ToString();
}
~Form1()
{
UnhookWinEvent(m_hhook);
}
But i'm not sure how to get it work and how to use it and if it's the right code at all .
Could you show me please an example of a code how to do it ?
Yes, you can use SetWinEventHook function.
hEvent = SetWinEventHook(EVENT_SYSTEM_FOREGROUND ,
EVENT_SYSTEM_FOREGROUND , NULL,
WinEventProcCallback, 0, 0,
WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
.......
VOID CALLBACK WinEventProcCallback ( HWINEVENTHOOK hWinEventHook, DWORD dwEvent, HWND hwnd, LONG idObject, LONG idChild, DWORD dwEventThread, DWORD dwmsEventTime)
{
/* your code here */
}
Quoted DReJ from here.
public class ActiveWindow
{
public delegate void ActiveWindowChangedHandler(object sender, String windowHeader,IntPtr hwnd);
public event ActiveWindowChangedHandler ActiveWindowChanged;
[DllImport("user32.dll")]
static extern IntPtr GetForegroundWindow();
[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);
const uint WINEVENT_OUTOFCONTEXT = 0;
const uint EVENT_SYSTEM_FOREGROUND = 3;
[DllImport("user32.dll")]
static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax,
IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc,
uint idProcess, uint idThread, uint dwFlags);
IntPtr m_hhook;
private WinEventDelegate _winEventProc;
public ActiveWindow()
{
_winEventProc = new WinEventDelegate(WinEventProc);
m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND,
EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, _winEventProc,
0, 0, WINEVENT_OUTOFCONTEXT);
}
void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd,
int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (eventType == EVENT_SYSTEM_FOREGROUND)
{
if (ActiveWindowChanged != null)
ActiveWindowChanged(this,GetActiveWindowTitle(hwnd),hwnd);
}
}
private string GetActiveWindowTitle(IntPtr hwnd)
{
StringBuilder Buff = new StringBuilder(500);
GetWindowText(hwnd, Buff, Buff.Capacity);
return Buff.ToString();
}
~ActiveWindow()
{
UnhookWinEvent(m_hhook);
}
}
when i switch between the active windows i get the callback
but when i maximize a minimized window i don't get a call back,
i find a work throw to solve this problem, but i am seeking for better solution
any help will be appreciated.
when i switch between the active windows i get the callback but when i maximize a minimized window i don't get a call back
Yes, you need to use either the EVENT_SYSTEM_MINIMIZESTART or EVENT_SYSTEM_MINIMIZEEND event constant to receive notification of window objects being minimized.
Use the eventMin and eventMax parameters of the SetWinEventHook function to indicate that you're interested in receiving notifications for one of these events and EVENT_SYSTEM_FOREGROUND.
I made a very small application that captures the screen inside games using SlimDX.
(I press left click to capture)
The capture works (atleast when I click on the form itself) but as soon as I click on firefox or any other application, I get this exception :
A callback was made on a garbage collected delegate of type 'CaptureScreen!CaptureScreen.Form1+WinEventDelegate::Invoke'. This may cause application crashes, corruption and data loss. When passing delegates to unmanaged code, they must be kept alive by the managed application until it is guaranteed that they will never be called.
at this line in my program.cs:
Application.Run(new Form1());
My Form1.cs (the designer itself has no controls)
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Forms;
using Microsoft.DirectX.Direct3D;
namespace CaptureScreen
{
public partial class Form1 : Form
{
private const uint WINEVENT_OUTOFCONTEXT = 0;
private const uint EVENT_SYSTEM_FOREGROUND = 3;
private const int WH_MOUSE_LL = 14;
private const int WM_LBUTTONDOWN = 513;
delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
IntPtr m_hhook;
[DllImport("user32.dll")]
static extern bool UnhookWinEvent(IntPtr hWinEventHook);
[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 lpString, int nMaxCount);
public Form1()
{
m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, WinEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);
hookProc = new HookProc(LowLevelMouseProc);
hook = SetWindowsHookEx(WH_MOUSE_LL, hookProc, GetModuleHandle(null), 0);
InitializeComponent();
}
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
UnhookWinEvent(m_hhook);
UnhookWindowsHookEx(hook);
}
void WinEventProc(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime)
{
if (eventType == EVENT_SYSTEM_FOREGROUND)
{
StringBuilder sb = new StringBuilder(500);
GetWindowText(hwnd, sb, sb.Capacity);
}
}
[DllImport("kernel32.dll")]
static extern IntPtr GetModuleHandle(string moduleName);
[DllImport("user32.dll")]
static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hMod, uint dwThreadId);
[DllImport("user32.dll")]
public static extern int UnhookWindowsHookEx(IntPtr hhook);
[DllImport("user32.dll")]
static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, uint wParam, IntPtr lParam);
delegate IntPtr HookProc(int nCode, uint wParam, IntPtr lParam);
[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
public static extern IntPtr GetForegroundWindow();
private HookProc hookProc;
private IntPtr hook;
IntPtr LowLevelMouseProc(int nCode, uint wParam, IntPtr lParam)
{
if (nCode >= 0 && (IntPtr)wParam == (IntPtr)WM_LBUTTONDOWN)
{
CaptureScreen();
}
return CallNextHookEx(IntPtr.Zero, nCode, wParam, lParam);
}
private void CaptureScreen()
{
StreamReader reader = new StreamReader(Path.GetFullPath("../../Counter.txt"));
string currentpic = reader.ReadLine();
if (string.IsNullOrEmpty(currentpic))
currentpic = "0";
reader.Close();
Bitmap bitmap = Direct3DCapture.CaptureWindow(GetForegroundWindow());
bitmap.Save(Path.GetFullPath("../../ScreenCapture/Test" + currentpic + ".gif"), ImageFormat.Gif);
StreamWriter writer = new StreamWriter(Path.GetFullPath("../../Counter.txt"));
writer.Write((int.Parse(currentpic)) + 1);
writer.Close();
}
public readonly uint DWM_EC_DISABLECOMPOSITION = 0;
public readonly uint DWM_EC_ENABLECOMPOSITION = 1;
[DllImport("dwmapi.dll", EntryPoint = "DwmEnableComposition")]
protected static extern uint Win32DwmEnableComposition(uint uCompositionAction);
}
}
the class that captures the screen can be found here:
http://spazzarama.wordpress.com/2009/02/07/screencapture-with-direct3d/
Any idea on how I can fix this?
Your problem is that you are just passing WinEventProc to SetWinEventHook, which will implicitly create a delegate that is eligible to be GCed once the current method exits (if not sooner!) You are seeing the consequences of that fact.
You will need to create a new member of Form1 of type WinEventDelegate, and use that as the parameter:
private WinEventDelegate winEventProc;
and then make use of it in your call to SetWinEventHook:
this.winEventProc = new WinEventDelegate(WinEventProc);
m_hhook = SetWinEventHook(EVENT_SYSTEM_FOREGROUND, EVENT_SYSTEM_FOREGROUND, IntPtr.Zero, this.winEventProc, 0, 0, WINEVENT_OUTOFCONTEXT);
That should ensure that your delegate stays alive as long as you need.
I had this problem also and have a similar solution to #dlev already in place but it does not work. I found if you mark the member static, it prevents it from being collected.
private static WinEventDelegate winEventProc;
There is a MSDN link which may help you to solve your problem.
Let The CLR Find Bugs For You With Managed Debugging Assistants