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).
Related
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 found this script to change the System sound volume and it works. But what are these constant volume codes called and where can I find a full list of these codes that do more things.
[DllImport("user32.dll")]
static extern IntPtr SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam);
//Volume codes, or messages, or whatever they are called
const int VOLUME_MUTE = 0x80000;
const int VOLUME_DOWN = 0x90000;
const int VOLUME_UP = 0xA0000;
SendMessage(this.Handle, 0x319, IntPtr.Zero, (IntPtr)VOLUME_UP);
These are AppCommand messages.
0x319 is the Win32 Windows MSG for WM_APPCOMMAND, and the messages are more accurately APPCOMMAND_VOLUME_UP, etc...
AppCommand messages are messages sent to windows, which are handled at a global level and perform certain application functions. These tend to be linked to Keyboard hotkeys and mouse button functions.
Your app gets first crack at processing any such messages, and if you do not handle them then your apps parent does. If that doesn't handle them, then eventually it gets sent to a global message hook to process them. The key point here is that other windows can trap these messages, so it's not a guarantee that sending these messages will accomplish the task. Just like you might have seen where pressing the volume up or down on your keyboard might not always work when certain windows have focus.
You can find the details for all the messages in the Win32 API reference:
http://msdn.microsoft.com/en-us/library/windows/desktop/ms646275(v=vs.85).aspx
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.
I am working on VS 2010 Ultimate. I have created a simple console application about 25-30 rows. So I want in the Main() function simply to simulate pressing "ALT+TAB" in a while cycle. Hoh can I do that - I cant use SendKeys class cause it "Provides methods for sending keystrokes to an application." I want just when I start my console application to simulate 1000 times pressing "ALT+TAB" without attaching it to anny applications. Something like this:
using System;
using System.Windows.Forms;
namespace nagradite
{
class Program
{
static void Main()
{
int i = 1000;
while( i > 0 )
{
// PRESS "ALT+TAB"
i--;
}
}
}
}
what should I type instead of // PRESS "ALT+TAB"
Use "%{TAB}" for Alt+TAB
SendKeys.Send("%{TAB}");
SendKeys.Send("%{TAB} 1000"); //if you want to do same by 1000 times as you stated
Reference http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.send.aspx
I think you can use SendMessage for finding keys you can see http://www.blizzhackers.cc/viewtopic.php?t=396398, your parent window is null (desktop)
[DllImport("user32.dll", CharSet = CharSet.Auto)]
static extern IntPtr SendMessage(IntPtr hWnd, UInt32 Msg, IntPtr wParam, IntPtr lParam);
You can try a Win32 API called SendInput. It allows you to simulate keyboard/mouse input events and does not require a HWND target. However, I don't know if this will actually trigger system-wide keyboard shortcuts such as ALT+TAB.
MSDN - http://msdn.microsoft.com/en-us/library/ms646310(v=vs.85).aspx
PInvoke.Net - http://www.pinvoke.net/default.aspx/user32.sendinput
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.