У меня точно такая же проблема, как и эта, и я думаю, что у меня есть работоспособное решение. Изначально я искал вариант, подобный тому, что упоминал «tylerl». Однако в моем случае вместо использования SetWindowsHookEx я попытался использовать аналогичную функцию RegisterShellHookWindows.
К сожалению, мне удалось уведомить только о создании / уничтожении подмножества окон. Единственные окна, для которых он предоставил уведомления, это те, которые отображаются на панели задач.
Поскольку мне не хотелось взламывать другие процессы или писать собственный код, который потребуется для SetWindowHookEx, я попытался покопаться в API автоматизации .NET, представленных в .NET 4.0, и я думаю, что это ответ на ваш вопрос. проблема (по крайней мере, в том, что касается определения, когда окна открыты / закрыты).
Вот фрагмент кода для использования этого API для обнаружения открытия / закрытия окон:
using System.Windows.Automation;
private void StartMonitoringForWindowEvents()
{
Task.Factory.StartNew(() =>
{
AutomationEventHandler windowOpenedHandler = new AutomationEventHandler(OnWindowOpened);
System.Windows.Automation.Automation.AddAutomationEventHandler(
WindowPattern.WindowOpenedEvent, AutomationElement.RootElement,
TreeScope.Descendants, windowOpenedHandler);
});
}
private void OnWindowOpened(object source, AutomationEventArgs eventArgs)
{
try
{
AutomationElement sourceElement = (AutomationElement)source;
string message = string.Format(
"Automation.WindowOpened PID: {0}, Handle: {1}, Name:{2}",
sourceElement.Current.ProcessId,
sourceElement.Current.NativeWindowHandle,
sourceElement.Current.Name);
Debug.WriteLine(message);
// for each created window, register to watch for it being closed
RegisterForClosedWindowEvent(sourceElement);
}
catch
{
}
}
private void RegisterForClosedWindowEvent(AutomationElement element)
{
try
{
string elementName = element.Current.Name;
int processId = element.Current.ProcessId;
int nativeHandle = element.Current.NativeWindowHandle;
AutomationEventHandler windowClosedHandler = new AutomationEventHandler(
(ignoreSource, ignoreArgs) => OnWindowClosed(nativeHandle, processId, elementName));
System.Windows.Automation.Automation.AddAutomationEventHandler(
WindowPattern.WindowClosedEvent, element,
TreeScope.Element, windowClosedHandler);
}
catch
{
}
}
private void OnWindowClosed(int nativeHandle, int processId, string elementName)
{
string message = string.Format(
"Automation.WindowClosed PID: {0}, Handle: {1}, Name:{2}",
processId,
nativeHandle,
elementName);
Debug.WriteLine(message);
}
Вам нужно будет добавить ссылку на сборки UIAutomationClient и UIAutomationClientTypes.
Вот ссылка на документацию MSDN (вы, вероятно, особенно захотите взглянуть на информацию о событиях):
http://msdn.microsoft.com/en-us/library/ms747327.aspx
Важные замечания по реализации:
1.) Обратите внимание, что в примере я использовал фабрику задач, чтобы зарегистрироваться для получения событий автоматизации. Особенно важно избегать использования потока пользовательского интерфейса при регистрации событий автоматизации или общении с API автоматизации. Это может (и обычно быстро) приводит к тупику. Поэтому я использую фабрику задач, чтобы обеспечить регистрацию через пул потоков.
Это также означает, что события будут приниматься в пуле потоков ... Так что, если вам необходимо выполнить какие-либо обновления пользовательского интерфейса, вам придется направить их в поток пользовательского интерфейса.
2.) Вы также заметите, что я собираю любую необходимую информацию об элементе, который может быть закрыт, во время регистрации (используя закрытие). Это связано с тем, что после закрытия элемента у нас больше не будет доступа к этой информации - поскольку элемент был уничтожен.
Phil