Is there Windows system event on active window changed? - c#

The desktop application I'm developing need to know what windows were active while the application was run. Currently it performs GetForegroundWindow() call (of user32.dll) every 250 msec. The approach is not very accurate.
Is there any Windows (WINAPI?) event which fires every time the active (focused) window changed? I'd like to subscribe with my callback function.
Thanks.

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 */
}

There's the WM_ACTIVATE message, which is sent to the activated and deactivated windows.

Related

is there easy way to check some process is activated?

I sent a WM_ACTIVE message using postmessage api to some programs.
When a program is deactivated, sending a message does not actually activate the window, but the program thinks it is active. ( It actually succeeded. )
However, I think it is very inefficient to send postmessage regularly.
If I want to check the WM_ACTIVE value of the program and it is inactivated, I try to send a WM_ACTIVE message again using the POSTMESSAGE API to confuse the program itself with being active, but I can't think of a way. Although there is an idea that hooking would be easy to use, C# did not support other types of global hooking except for the keyboard and mouse.
Can anyone come up with any other ideas? please help me.
To check if a process is focused you should use GetForegroundWindow to get the focused window handle and then use GetWindowThreadProcessId to get the process from that window handle:
[DllImport("user32.dll")]
private static extern IntPtr GetForegroundWindow();
[DllImport("user32.dll")]
static extern uint GetWindowThreadProcessId(int hWnd, out int ProcessId);
IntPtr focusedWindow = GetForegroundWindow(); //get the focused window
int focusedProcessID = 0;
GetWindowThreadProcessID(focusedWindow, out focusedProcessID); //get it's process id
Process focusedProcess = Process.GetProcessById(focusedProcessID);//get the focused process
Console.WriteLine("Current Focused Process:" + focusedProcess.ProcessName);

Global Hook for Window creation/maximise/minimise etc in C#

I want to monitor any window is created globally. The closest API I found is SetWinEventHook on EVENT_OBJECT_CREATE, however, it not only hooks window creation but also controls. I wonder if there is any way that does not cost too much, and can hook not only CBT windows.
I tried to use IsWindow() to detect if the callback hwnd is a window hwnd, but it seems always return true regardless if the hwnd is a window or control.
I would prefer using managed api rather than adding other dlls, although if it is absolute necessary it is still an open option.
Lastly, how can I hook the windows maximise, minimise and restore events? Tried EVENT_OBJECT_STATECHANGE but it seems not the correct one. Tried EVENT_SYSTEM_MOVESIZESTART and EVENT_SYSTEM_MOVESIZEEND but also not capturing the max/min/restore events.
Partial code can be seen as follows:
private List<IntPtr> _hooks;
private User32ex.WinEventDelegate _delegate;
private void StartService() {
_delegate = WinEventProc;
_hooks.Add(User32ex.SetWinEventHook(User32.WindowsEventHookType.EVENT_OBJECT_CREATE, User32.WindowsEventHookType.EVENT_OBJECT_DESTROY, IntPtr.Zero, _delegate, 0, 0, User32.WindowsEventHookFlags.WINEVENT_OUTOFCONTEXT));
// Other hooks
}
private void WinEventProc(IntPtr hWinEventHook, User32.WindowsEventHookType eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) {
if (hwnd == IntPtr.Zero || !User32.IsWindow(hwnd)) return;
switch (eventType) {
case User32.WindowsEventHookType.EVENT_OBJECT_CREATE:
Debug.Print($"Create: {hwnd}");
// Do something - here captures all objects not only windows but also controls etc
break;
case User32.WindowsEventHookType.EVENT_OBJECT_STATECHANGE:
Debug.Print($"State change: {hwnd}");
// Do something
break;
// Other cases
}
}
Many thanks in advance!
Welcome to the beautiful world of Windows API! The reason your hook hooks not only "windows", but also "controls" is that in Windows both "windows" and "controls" are just windows. There might be different kinds of windows they might look different and behave differently; controls are just windows with specific looks and specific behaviours.
Since all of them just windows, you can't just hook to "windows" not hooking to "controls" at the same time. However, when you already hooked to one, you may identify if the window you've hooked to is the kind of window you would like to hook. As suggested in the comments, you may use window styles:
// this is a pretty crude and basic way to sort out "controls"
BOOL isControl_KindOf = GetWindowLong(hwnd, GWL_STYLE) & WS_CHILD;

WH_KEYBOARD with Alt+Tab stop working

When I set up non-global WH_KEYBOARD hook, it works fine and all keys are catched. But if I press Alt+Tab and then return to the window, hook stops its working with no reason.
This is my hook proc where I'm trying to block all keyboard messages for proccess with id = root:
private static IntPtr HookCallback (int nCode, IntPtr wParam, IntPtr lParam)
{
var proc = 0u;
GetWindowThreadProcessId(GetForegroundWindow(), out proc);
if (proc != root) return CallNextHookEx(hooks[0], nCode, wParam, lParam);
return new IntPtr(1);
}
For developing I use 32bit Windows in VirtualBox.
UPDATE:
It also doesn't depend on blocking or passing parameters to the next hook: code variant that do nothing then calling next hook stops working after Alt+Tab too. Losing focus and activating of the window doesn't stop hook if it was done not with Alt+Tab.
After looking for some samples and analysing my code I've found how to solve this "bug". I should set last two parameters in SetWindowsHookEx function in such way:
SetWindowsHookEx(HookType.WH_KEYBOARD, HookCallback,
IntPtr.Zero, GetCurrentThreadId());
but not like this:
SetWindowsHookEx(HookType.WH_KEYBOARD, HookCallback,
GetModuleHandle(curModule.ModuleName), 0);

How to detect when the user switches to the Log On screen?

I need to know when the user switches to the logon screen (as triggered by ctrl-alt-del) in order to circumvent a pesky bug in WPF. I want to work around this bug by reinitializing my GUI after returning from the logon screen. Currently it works, but I have to trigger it manually.
I have found SystemEvents.SessionSwitch, but unfortunately this is only triggered when logging off.
How can I detect when the logon screen is displayed by forming ctrl-alt-del?
The tricky thing is that this is not a session change, but rather just a desktop change. In particular, Ctrl+Alt+Del switches to a secured desktop associated with Winlogon.
I don't think you're really supposed to detect this kind of thing (that is, after all, the whole point of having a "secure desktop"), but you could probably do it using an Active Accessibility hook. Call the SetWinEventHook function to install an event hook for the EVENT_SYSTEM_DESKTOPSWITCH event and see what notifications you receive.
To get it going, you'll need to do the following:
Ensure that you're pumping a message loop on your client thread in order to receive event notifications. This shouldn't be a problem for a standard WPF application.
Make sure that you specify the WINEVENT_OUTOFCONTEXT flag, considering that you're working from managed code. You don't want the system to attempt to inject your DLL that contains the callback function into every process. Instead, this will cause the callback function to be called asynchronously from a queue; much safer from the land of managed code.
A little bit of P/Invoke magic. To get you started…
const uint WINEVENT_OUTOFCONTEXT = 0x0;
const uint EVENT_SYSTEM_DESKTOPSWITCH = 0x0020;
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin,
uint eventMax,
IntPtr hmodWinEventProc,
WinEventDelegate lpfnWinEventProc,
uint idProcess,
uint idThread,
uint dwFlags);
delegate void WinEventDelegate(IntPtr hWinEventHook,
uint event,
IntPtr hwnd,
int idObject,
int idChild,
uint dwEventThread,
uint dwmsEventTime);
[DllImport("user32.dll")]
static extern bool UnhookWinEvent(IntPtr hWinEventHook);
The process which gets started to show the logon screen seems to be called LogonUI.exe.
Using the Windows Management Instrumentation (WMI) infrastructure you can listen for processes which start and shut down. You will need to reference the System.Management assembly.
var interval = new TimeSpan( 0, 0, 1 );
const string isWin32Process = "TargetInstance isa \"Win32_Process\"";
// Listen for started processes.
WqlEventQuery startQuery
= new WqlEventQuery( "__InstanceCreationEvent", interval, isWin32Process );
_startWatcher = new ManagementEventWatcher( startQuery );
_startWatcher.Start();
_startWatcher.EventArrived += OnStartEventArrived;
// Listen for closed processes.
WqlEventQuery stopQuery
= new WqlEventQuery( "__InstanceDeletionEvent", interval, isWin32Process );
_stopWatcher = new ManagementEventWatcher( stopQuery );
_stopWatcher.Start();
_stopWatcher.EventArrived += OnStopEventArrived;
Handling these events, you can get information about the started or closed process. This way you can verify when LogonUI.exe was shut down, and subsequently trigger the required actions.
void OnStopEventArrived( object sender, EventArrivedEventArgs e )
{
var o = (ManagementBaseObject)e.NewEvent[ "TargetInstance" ];
string name = (string)o[ "Name" ];
...
}

where do I get arguments of win32 API function CallWindowProc()?

According to my research, when I run C# executable which opens WinForm, within .NET, they don't offer the function to access those WinForm object from separate c# process (separate file I mean) but win32 API does.
Then I came across 3 functions from API.
FindWindow();
GetWindowLong();
CallWindowProc()
I need to call it from top down to the bottom but then I got stuck by CallWIndowProc() because
I can't figure what I should pass for last 3 arguments.
private static extern UIntPtr CallWindowProc(IntPtr a, IntPtr b, uint c, IntPtr d, IntPtr e);
c, d and e
According to the doc, it should be some sort of "message" which is int. But where can I get such value???
http://msdn.microsoft.com/en-us/library/ms633571(v=vs.85).aspx
Code:
[DllImportAttribute("User32.dll")]
private static extern IntPtr FindWindow(String ClassName, String WindowName);
[DllImportAttribute("User32.dll")]
private static extern long GetWindowLong(IntPtr a, int b);
[DllImportAttribute("User32.dll")]
private static extern UIntPtr CallWindowProc(IntPtr a, IntPtr b, uint c, IntPtr d, IntPtr e);
[STAThread]
static void Main(string[] args)
{
IntPtr lResult;
uint lMsg = 0;
IntPtr HWND = FindWindow("WindowsFormsApplication1.Form1", "Form1");
int GWL_WNDPROC = -4;
long WNDPROC = GetWindowLong(HWND, GWL_WNDPROC);
lResult = CallWindowProc(WNDPROC, HWND, lMsg, 0, 0);
}
Clarification
OK.. I should have made it clear.. my goal is to run following chunk of code against the WebForm being executed. (I'ts WatiN)
var t = new Thread(() =>
{
Settings.AutoStartDialogWatcher = false;
var ie = new IE(form1.webBrowser1.ActiveXInstance);
ie.GoTo("http://www.google.com");
ie.TextField(Find.ByClass("lst")).TypeText("this is awesome!!");
ie.Button(Find.ByName("btnG")).Click();
});
t.SetApartmentState(ApartmentState.STA);
t.Start();
What message you are trying to send to callWinProc?
Arguments are
nProc is a value returned previously by SubClassWindow() (Source Window).
hWnd is the handle to the window that was subclassed (target window).
nMsg is the message (one of the WM_* values defined in WINDOWS.CH, basically kind of event or message like click is one message). For complete system messages see http://msdn.microsoft.com/en-us/library/ms644927(v=vs.85).aspx#system_defined
wParam depends on nMsg. For click, it takes left or right click
lParam depends on nMsg. for click it takes the location as lparam
you can see wparam and lparam defination for each message.
It looks like you're trying to call the window proc of a window from a different thread/process. I'm assuming this because you're using FindWindow, and I can't see where you created the window. If that is what you are doing, CallWindowProc won't work because you cannot call a window proc from a thread other than the one that created the window. What you need is SendMessage, which accepts the same last four parameters (HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) - to interpret them you need to know what message you're sending.
I recommand to use the parameter names from the native methode just for clearness. You can get those pinvoke signatures from pinvoke.net so you don't have to do it on your own all the time. These messages are definded in header files and documented in the msdn. Its quiete hard to use the right message in the correct manner if you're new to win32 and/or C#.
If you want to intercept the windows messages of your form you need a windows message hook, but this doesn't work in .Net. You can also read this article which cover this topic.
Maybe you should try to find a complete different solution for your problem. Other IPC methodes etc.
EDIT: The CLR type of your form (WindowsFormsApplication1.Form1) is not the class name you have to put in FindWindow, FindWindow is an unmanaged api and isn't aware of the CLR typesystem. Try out Spy++ to investigate some windows on your PC.

Categories

Resources