I am shelling out to do some work and one of the requirements is to kill the process if it is hung.
My first thought was Process.Responding, however, I am not sure what it really means.
Is it the same thing as when Win7 adds a (Not Responding) to the window title of an application? On my machine, this happens even when MS Word tries to open a file from a really slow remote share.
What are the conditions for having a Process.Responding be false?
Under the hood, when you check the Process.Responding property, the Windows function SendMessageTimeout is called.
This function basically sends a message to the main window of another process and checks whether the window is accepting the message within a timeout of 5000 ms (Therefore checking this property on a console application has no effect).
If you want to use a custom timeout you can just as well call the SendMessageTimeout function yourself:
[DllImport("user32.dll", CharSet=CharSet.Auto)]
public static extern IntPtr SendMessageTimeout(
HandleRef hWnd,
int msg,
IntPtr wParam,
IntPtr lParam,
int flags,
int timeout,
out IntPtr pdwResult);
const int SMTO_ABORTIFHUNG = 2;
public bool RespondingWithinMs(Process process, int timeoutMs)
{
IntPtr ptr2;
return SendMessageTimeout(
new HandleRef(process, process.MainWindowHandle),
0,
IntPtr.Zero,
IntPtr.Zero,
SMTO_ABORTIFHUNG,
timeoutMs,
out ptr2) != IntPtr.Zero;
}
Responding means the application window is responding to the user. the process should have a MainWindowHandle from msdn:
true if the user interface of the associated process is responding to the system; otherwise, false.
If the process does not have a MainWindowHandle, this property returns true.
You can modify the timeout used by application.Responding check this.
From http://msdn.microsoft.com/en-us/library/system.diagnostics.process.responding.aspx
If a process has a user interface, the Responding property contacts the user interface to determine whether the process is responding to user input. If the interface does not respond immediately, the Responding property returns false. Use this property to determine whether the interface of the associated process has stopped responding.
Related
Both the callback and the AccessibleObjectFromEvent call appear to be working as intended, and I'm not using any locking mechanisms, but if AccessibleObjectFromEvent is present in the callback the winforms form will occasionally lock up.
I noticed when it freezes it has this odd property where right-clicking on its taskbar icon unfreezes it.
Pausing with the debugger just takes you to Application.Run(new Form1()), and nothing appears blocked on the .Net side - the debugger's window updates can even trigger some Active Accessibility events and then let you step through those events in the callback - working - all while the form remains frozen!
I note that AccessibleObjectFromEvent works by sending a WM_GETOBJECT message, but the .Net side is never stuck at the AccessibleObjectFromEvent call, and calling AccessibleObjectFromEvent from inside a SetWinEventHook callback is AFAIK a normal way to do Active Accessibility.
I've not noticed any correlation with Active Accessibility events when it freezes, but I don't really have enough information to rule that out. I also tried it compiled x86 (instead of Any), and that made no difference.
I boiled it down to its most minimal form:
using Accessibility;
using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Windows.Forms;
namespace WindowsFormsApp1 {
static class Program {
// PInvoke declarations, see http://www.pinvoke.net/default.aspx/user32.setwineventhook
[DllImport("user32.dll")]
static extern IntPtr SetWinEventHook(uint eventMin, uint eventMax, IntPtr hmodWinEventProc, WinEventDelegate lpfnWinEventProc, uint idProcess, uint idThread, WinEventFlag dwFlags);
public delegate void WinEventDelegate(IntPtr hWinEventHook, uint eventType, IntPtr hwnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime);
[DllImport("oleacc.dll")]
public static extern uint AccessibleObjectFromEvent(IntPtr hwnd, uint dwObjectID, uint dwChildID, out IAccessible ppacc, [MarshalAs(UnmanagedType.Struct)] out object pvarChild);
[Flags]
public enum WinEventFlag : uint {
/// <summary>Events are ASYNC</summary>
Outofcontext = 0x0000,
/// <summary>Don't call back for events on installer's thread</summary>
Skipownthread = 0x0001,
/// <summary>Don't call back for events on installer's process</summary>
Skipownprocess = 0x0002,
/// <summary>Events are SYNC, this causes your dll to be injected into every process</summary>
Incontext = 0x0004
}
static IntPtr hookHandle = IntPtr.Zero;
static GCHandle garbageCollectorHandle;
static void AccessibilityEventHandler(IntPtr hWinEventHook, uint eventType, IntPtr hWnd, int idObject, int idChild, uint dwEventThread, uint dwmsEventTime) {
try {
object objChild = null;
IAccessible accWindow = null;
AccessibleObjectFromEvent(hWnd, (uint)idObject, (uint)idChild, out accWindow, out objChild);
Debug.WriteLine("Hook was invoked");
} catch (Exception ex) {
Debug.WriteLine("Exception " + ex);
}
}
[STAThread]
static void Main() {
WinEventDelegate callback = new WinEventDelegate(AccessibilityEventHandler);
// Use the member garbageCollectorHandle to keep the delegate object in memory. Might not be needed, and can't properly pin it because it's not a primitive type.
garbageCollectorHandle = GCHandle.Alloc(callback);
SetWinEventHook(
0x7546, // eventMin (0x7546 = PropertyChanged_IsOffscreen)
0x7546, // eventMax
IntPtr.Zero,
callback,
0,
0,
WinEventFlag.Outofcontext
);
// Two hooks are not necessary to cause a freeze, but with two it can happen much faster - sometimes within minutes
SetWinEventHook(
0x0001, // eventMin (0x0001 = System_Sound)
0x0001, // eventMax
IntPtr.Zero,
callback,
0,
0,
WinEventFlag.Outofcontext
);
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Form1());
}
}
}
With such a minimal app it's harder to notice the message pump freeze, but you won't be able to drag the window or bring it to the foreground once it's locked. The bug is intermittent, often it happens within 5 minutes, sometimes it takes so long I give up, if you leave it overnight and the form is still responsive in the morning then it might depend on machine/OS (tried on Win 10.0.17174 and 10.0.17763, .net 4.5.2 and 4.6.1).
I'm 99% sure the call to AccessibleObjectFromEvent is required for the app to freeze, but with intermittent freezes there's no way to absolutely know for sure.
Reentrancy
Following Jimi's suggestions in the comments, I ran the minimal app with the flags
WinEventFlag.Outofcontext | WinEventFlag.Skipownthread | WinEventFlag.Skipownprocess
It took a while to freeze, but has still frozen. The Debug.WriteLine() calls indicate it's still responding to Active Accessibility events normally - i.e. no recursive busy-loop is happening through that callback (at least not now that I'm looking), but the form is frozen. It's using 0% CPU in the task manager.
The freeze is slightly different now, in that the task manager doesn't list it as "not responding" and I can bring it to the foreground by left-clicking the taskbar icon - normally the taskbar can't even bring it to the foreground. However the form still can't be moved or resized, and you can't bring the form to the foreground by clicking on it.
I'd also added some Debug.WriteLine in front of the AccessibleObjectFromEvent call, and tracked reentrancy depth, I can see that it is occasionally reentrant, but usually to a depth of only one before unwinding, and no deeper than 13 before fully unwinding. This appears to be caused by there being many events already in the message queue, rather than the hook handler recursively causing events it must then handle. The UI is currently frozen and the hook handler is 0 deep (i.e. not currently reentrant).
When I click ctrl+c in a console window, event is raised and application can execute some piece of code which should be executed before exiting.
However, when I click [X] close button, event is raised too, but in a short time, my app I forcibly closed - when 'end execution' event is still in progress.
Is it possible to set longer timeout before windows will forcibly close app?
EDIT:
Im setting:
[DllImport("Kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ControlEventHandler e, bool add);
public delegate void ControlEventHandler(ConsoleEvent consoleEvent);
When I click ctrl+c on console window, Im receiving: CTRL_C, and I can process code in mu event handler.
When I click [x] close button, I'm receiveing CTRL_CLOSE flag, I can process handler too, but only for 1-2secs. Then console windwo disappers...
Is it possible to set longer timeout before windows will forcibly close app?
Unfortunately, no. Starting with Windows Vista, you are allowed only 10 seconds or so grace period after receiving the CTRL_CLOSE_EVENT before the console window automatically closes. If you haven't exited the handler by the time 10 seconds has elapsed, then your process is unceremoniously terminated. (Obviously, if you return from the handler sooner, you will not get the full 10 seconds.)
As far as I can tell, this is part of a larger design strategy to ensure that applications cannot override the will of the user. Older versions of the SDK documentation spoke of a dialog box that would appear if the process didn't respond within a certain time-out period, but all mention of that is gone from the current version of the SDK documentation. The dialog box went MIA in Vista, it doesn't exist anymore. This is probably connected to the fact that applications can no longer block system shutdown either.
This doesn't affect your pressing Ctrl+C because that raises a different signal—CTRL_C_EVENT. Same thing with Ctrl+Break, which raises CTRL_BREAK_EVENT. But as far as I can tell from the documentation, CTRL_CLOSE_EVENT, CTRL_LOGOFF_EVENT, and CTRL_SHUTDOWN_EVENT are simply notifications, giving you a chance to clean up before you are terminated. There is no way to block termination or extend the grace period.
There is only one workaround that I can think of, and that is to disable the close button on the console window itself. I see that you're using C#, so a bit of P/Invoke will be required:
[DllImport("kernel32.dll")]
static extern IntPtr GetConsoleWindow();
[DllImport("user32.dll")]
static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);
[DllImport("user32.dll")]
static extern bool DeleteMenu(IntPtr hMenu, uint uPosition, uint uFlags);
private const uint MF_BYCOMMAND = 0x0;
private const uint SC_CLOSE = 0xF060;
void DisableConsoleCloseButton()
{
IntPtr hWnd = GetConsoleWindow();
if (hWnd != IntPtr.Zero)
{
IntPtr hMenu = GetSystemMenu(hWnd, false);
if (hMenu != IntPtr.Zero)
{
DeleteMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);
}
}
}
If you go this route, you will obviously need to provide the user with some way of closing the console window from within your application. In Win32-land, that functionality would call the FreeConsole function to close it, but I'll bet that's wrapped up in one of the .NET classes. Been too long for me to remember what it's called.
I'm using C# and I've got the program successfully recording the journal messages using SetWindowsHookEx with WH_JOURNALRECORD.
My problem comes when it's time to stop. The docs show that if the user pressed CTRL-ESC or CTRL-ALT-DELETE a WM_CANCELJOURNAL message will be posted that I can watch to know when to stop. My application gets unhooked but I never seem to get a WM_CANCELJOURNAL.
I have two hooks setup. One hook to do the Journal Record and one to check for the cancel message:
IntPtr hinstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0]);
JournalRecordProcedure = JournalRecordProc;
journalHook = SetWindowsHookEx(WH_JOURNALRECORD, JournalRecordProcedure, hinstance, 0);
GetMessageProcedure = GetMessageProc;
messageHook = SetWindowsHookEx(WH_GETMESSAGE, GetMessageProcedure, hinstance, 0);
------
public static int JournalRecordProc(int nCode, IntPtr wParam, IntPtr lParam)
{
if (nCode < 0) return CallNextHookEx(journalHook, nCode, wParam, lParam);
EventMsgStruct msg = (EventMsgStruct) Marshal.PtrToStructure(lParam, typeof (EventMsgStruct));
script.Add(msg); //just a quick way to record for now
return CallNextHookEx(journalHook, nCode, wParam, lParam);
}
public static int GetMessageProc(int code, IntPtr wParam, IntPtr lParam)
{
//it comes here but how do I test if it's WM_CANCELJOURNAL ??
//code always seems to be equal to zero.. I must be missing something
return CallNextHookEx(journalHook, code, wParam, lParam);
}
I suppose you're referring to this section in the documentation:
This role as a signal to stop journal recording means that a CTRL+BREAK key combination cannot itself be recorded. Since the CTRL+C key combination has no such role as a journaling signal, it can be recorded. There are two other key combinations that cannot be recorded: CTRL+ESC and CTRL+ALT+DEL. Those two key combinations cause the system to stop all journaling activities (record or playback), remove all journaling hooks, and post a WM_CANCELJOURNAL message to the journaling application.
The problem is that the WM_CANCELJOURNAL message is not sent to the callback function you installed with SetWindowsHookEx. But unlike other WM_* messages, it is also not meant to be processed by a window procedure (WndProc in WinForms) because it is posted to the message queue of the thread and is not associated with any particular window.
Rather, the documentation advises that one must process it within an application's main loop or using a WH_GETMESSAGE hook:
This message does not return a value. It is meant to be processed from within an application's main loop or a GetMessage hook procedure, not from a window procedure.
[ . . . ]
The WM_CANCELJOURNAL message has a NULL window handle, therefore it cannot be dispatched to a window procedure. There are two ways for an application to see a WM_CANCELJOURNAL message: If the application is running in its own main loop, it must catch the message between its call to GetMessage or PeekMessage and its call to DispatchMessage. If the application is not running in its own main loop, it must set a GetMsgProc hook procedure (through a call to SetWindowsHookEx specifying the WH_GETMESSAGE hook type) that watches for the message.
In managed WinForms code, you obviously don't have any access to or control over the application's main loop. I'm not sure if adding a message filter to your application will let you handle this message or not: I haven't tried it. If it will, that's probably the route you want to take, considering the alternative, which is to install a second hook, WH_GETMESSAGE, and then in that hook procedure, listen for the WM_CANCELJOURNAL message.
Update:
In the GetMessageProc callback function, the code parameter just tells you whether the hook procedure should process the message. Virtually all of the time, it's going to be 0, which is equivalent to the symbolic constant HC_ACTION. If the code parameter is less than 0, the hook procedure should simply call the CallNextHookEx function without performing any further processing. That's basically the exact same thing you did for the JournalRecordProc callback function.
The window message is going to be found in a MSG structure, a pointer to which is passed to the callback function as the lParam parameter. But that's Win32 stuff. Don't mess with raw pointers in .NET, let the P/Invoke marshaler handle all of that dirty stuff for you. The native MSG structure is equivalent to the managed System.Windows.Forms.Message structure (the same thing used by the WndProc method), so if you declare your GetMessageProc callback function like this, things will be much simpler:
public delegate int GetMessageProc(int code, IntPtr wParam, ref Message lParam);
Then, the windows message is found as the Msg member of the Message structure. That's the value you want to compare against WM_CANCELJOURNAL:
public static int GetMessageProc(int code, IntPtr wParam, ref Message lParam)
{
if (code >= 0)
{
if (lParam.Msg == WM_CANCELJOURNAL)
{
// do something
}
}
return CallNextHookEx(messageHook, code, wParam, ref lParam);
}
Note that in order for the above call to CallNextHookEx to work, you'll also have to provide an overloaded definition of the CallNextHookEx function that matches the signature of your GetMessageProc callback function:
[DllImport("user32.dll")]
public static extern int CallNextHookEx(IntPtr hHook, int nCode,
IntPtr wParam, ref Message lParam);
In order to get the application name of the foreground Window (or the name of application file) I want to use GetActiveWindow with GetWindowModuleFileName.
I found a similar question relating to GetWindowText here
That implementation of GetWindowText works fine, but GetWindowModuleFileName only returns a value for visual studio (when I click inside the devenv) for all other applications it stays blank.
Any hint how I can find out what goes wrong? Might this have to do with permission/security of my application querying the applicationfilename of another process?
EDIT: http://support.microsoft.com/?id=228469 looks like this doesn't work under Win >=XP
Any alternatives how to get the application file name?
In order to get the application name of the foreground Window (or the name of application file) I want to use GetActiveWindow with GetWindowModuleFileName.
... querying the applicationfilename of another process ...
In my opinion your problem with use of GetActiveWindow() function. It is used for gathering information from the calling thread/process only. If calling thread is inactive GetActiveWindow return 0;
From MSDN:
GetActiveWindow Retrieves the window handle to the active window attached to the calling thread's message queue.
Try to use GetForegroundWindow() function instead of GetActiveWindow()
By chance do you have UAC turned off?
Starting with Vista, if your code touches an HWND in another process, your process needs to be run at the same privilege level.
In other words, if the window is hosted in a process running as administrator, your app must also run as administrator.
I found a workaround using this:
[DllImport("user32.dll", SetLastError = true)]
static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);
IntPtr handle = IntPtr.Zero;
handle = GetForegroundWindow();
uint processId;
if (GetWindowThreadProcessId(handle, out processId) > 0)
{
Console.WriteLine(Process.GetProcessById((int)processId).MainModule.FileName);
}
I am trying to bring a window foreground. I am using this code. But its not working. Could someone please help?
ShowWindowAsync(wnd.hWnd, SW_SHOW);
SetForegroundWindow(wnd.hWnd);
// Code from Karl E. Peterson, www.mvps.org/vb/sample.htm
// Converted to Delphi by Ray Lischner
// Published in The Delphi Magazine 55, page 16
// Converted to C# by Kevin Gale
IntPtr foregroundWindow = GetForegroundWindow();
IntPtr Dummy = IntPtr.Zero;
uint foregroundThreadId = GetWindowThreadProcessId(foregroundWindow, Dummy);
uint thisThreadId = GetWindowThreadProcessId(wnd.hWnd, Dummy);
if (AttachThreadInput(thisThreadId, foregroundThreadId, true))
{
BringWindowToTop(wnd.hWnd); // IE 5.5 related hack
SetForegroundWindow(wnd.hWnd);
AttachThreadInput(thisThreadId, foregroundThreadId, false);
}
if (GetForegroundWindow() != wnd.hWnd)
{
// Code by Daniel P. Stasinski
// Converted to C# by Kevin Gale
IntPtr Timeout = IntPtr.Zero;
SystemParametersInfo(SPI_GETFOREGROUNDLOCKTIMEOUT, 0, Timeout, 0);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, Dummy, SPIF_SENDCHANGE);
BringWindowToTop(wnd.hWnd); // IE 5.5 related hack
SetForegroundWindow(wnd.hWnd);
SystemParametersInfo(SPI_SETFOREGROUNDLOCKTIMEOUT, 0, Timeout, SPIF_SENDCHANGE);
}
Code Explained
Making a window the foreground window
requires more than just calling the
SetForegroundWindow API. You must
first determine the foreground thread
and attach it to your window, using
AttachThreadInput, then call
SetForegroundWindow. That way they can
share input states.
First I call GetForegroundWindow to
get the handle of the current
foreground window. Then a few calls to
GetWindowThreadProcessId retrieve the
threads associated with the current
foreground window and the window I
want to bring to the foreground. If
these threads are the same a simple
call to SetForegroundWindow is all
that is necessary. Otherwise, the
foreground thread is attached to the
window that I am bringing to the front
and detached from what was the current
foreground window. The
AttachThreadInput API handles this.
Content Taken from here
Thanks.
I've used this method before:
[DllImport("user32.dll")]
static extern bool SetForegroundWindow(IntPtr hWnd);
Process[] processes = Process.GetProcessesByName("processname");
SetForegroundWindow(processes[0].MainWindowHandle);
More information: http://pinvoke.net/default.aspx/user32.SetForegroundWindow
This code restores and set focus to a window:
[DllImport("User32.dll")]
static extern int SetForegroundWindow(IntPtr hWnd);
[DllImport("user32.dll")]
internal static extern bool SendMessage(IntPtr hWnd, Int32 msg, Int32 wParam, Int32 lParam);
static Int32 WM_SYSCOMMAND = 0x0112;
static Int32 SC_RESTORE = 0xF120;
And use it like this:
var proc = Process.GetProcessesByName("YourProgram").FirstOrDefault();
if (proc != null)
{
var pointer = proc.MainWindowHandle;
SetForegroundWindow(pointer);
SendMessage(pointer, WM_SYSCOMMAND, SC_RESTORE, 0);
}
In order for SetForegroundWindow to work consistently, you have to meet a few criteria. The first is your process that would run the command must be in the foreground. Only foreground process can make another process foreground. In order to make your process foreground first, you have to bring the main window to the front, if it is not. You minimise it first and then SetForegroundWindow it, to make it foreground. Now find the target process and bring it to the front
The steps are
Minimise the current window
SetForegroundWindow it
Find the target process
SetForegroundWindow it
I've got an example, though it's a slightly different use case.
You should use SetForegroundWindow. Also it may be interesting for you C# Force Form Focus
I'll be brief: Form.BringToFront()
As of Windows 7 these features dont behave quite so well. If there is an application such as Excel in front of the application you want to bring to the front then Windows 7 blocks this and flashes the window. You can set a registry timeout setting ForegroundLockTimeout=0 in HKEY_CURRENT_USER\Control Panel\Desktop but these is known as stealing focus. To set the behaviour of how XP "should" behave and will behave in Windows 7 by default you can create/set the value to 0x00030D40 (200000ms).
I'd like to know what is the preferred solution for trusted Windows applications. eg. If I trust application B to take focus when I double click something in Application A, and some other app is obscuring the window of Application B.