RX, вероятно, самый простой выбор, особенно если вы уже используете его в своем приложении. Но если нет, то добавление может быть немного излишним.
Для приложений на основе пользовательского интерфейса (например, WPF) я использую следующий класс, который использует DispatcherTimer:
public class DebounceDispatcher
{
private DispatcherTimer timer;
private DateTime timerStarted { get; set; } = DateTime.UtcNow.AddYears(-1);
public void Debounce(int interval, Action<object> action,
object param = null,
DispatcherPriority priority = DispatcherPriority.ApplicationIdle,
Dispatcher disp = null)
{
// kill pending timer and pending ticks
timer?.Stop();
timer = null;
if (disp == null)
disp = Dispatcher.CurrentDispatcher;
// timer is recreated for each event and effectively
// resets the timeout. Action only fires after timeout has fully
// elapsed without other events firing in between
timer = new DispatcherTimer(TimeSpan.FromMilliseconds(interval), priority, (s, e) =>
{
if (timer == null)
return;
timer?.Stop();
timer = null;
action.Invoke(param);
}, disp);
timer.Start();
}
}
Для его использования:
private DebounceDispatcher debounceTimer = new DebounceDispatcher();
private void TextSearchText_KeyUp(object sender, KeyEventArgs e)
{
debounceTimer.Debounce(500, parm =>
{
Model.AppModel.Window.ShowStatus("Searching topics...");
Model.TopicsFilter = TextSearchText.Text;
Model.AppModel.Window.ShowStatus();
});
}
События клавиш теперь обрабатываются только после клавиатуры не используется в течение 200 мс - все предыдущие ожидающие события отбрасываются.
Существует также метод Throttle, который всегда запускает события после заданного интервала:
public void Throttle(int interval, Action<object> action,
object param = null,
DispatcherPriority priority = DispatcherPriority.ApplicationIdle,
Dispatcher disp = null)
{
// kill pending timer and pending ticks
timer?.Stop();
timer = null;
if (disp == null)
disp = Dispatcher.CurrentDispatcher;
var curTime = DateTime.UtcNow;
// if timeout is not up yet - adjust timeout to fire
// with potentially new Action parameters
if (curTime.Subtract(timerStarted).TotalMilliseconds < interval)
interval = (int) curTime.Subtract(timerStarted).TotalMilliseconds;
timer = new DispatcherTimer(TimeSpan.FromMilliseconds(interval), priority, (s, e) =>
{
if (timer == null)
return;
timer?.Stop();
timer = null;
action.Invoke(param);
}, disp);
timer.Start();
timerStarted = curTime;
}
Из этой статьи вы можете использовать WMI (пространство имен System.Management
) в своей службе для отслеживания событий запуска процесса.
void WaitForProcess()
{
ManagementEventWatcher startWatch = new ManagementEventWatcher(
new WqlEventQuery("SELECT * FROM Win32_ProcessStartTrace"));
startWatch.EventArrived
+= new EventArrivedEventHandler(startWatch_EventArrived);
startWatch.Start();
ManagementEventWatcher stopWatch = new ManagementEventWatcher(
new WqlEventQuery("SELECT * FROM Win32_ProcessStopTrace"));
stopWatch.EventArrived
+= new EventArrivedEventHandler(stopWatch_EventArrived);
stopWatch.Start();
}
static void stopWatch_EventArrived(object sender, EventArrivedEventArgs e) {
stopWatch.Stop();
Console.WriteLine("Process stopped: {0}"
, e.NewEvent.Properties["ProcessName"].Value);
}
static void startWatch_EventArrived(object sender, EventArrivedEventArgs e) {
startWatch.Stop();
Console.WriteLine("Process started: {0}"
, e.NewEvent.Properties["ProcessName"].Value);
}
}
WMI позволяет выполнять довольно сложные запросы; вы можете изменить запросы здесь, чтобы запускать обработчик событий только при запуске наблюдаемого приложения или по другим критериям. Вот краткое введение с точки зрения C #.
у вас есть 3 варианта:
Надежный / интрузивный, настроить ловушку в неуправляемом коде, которая обменивается данными с вашим приложением C # при каждом запуске приложения. Это сложно сделать правильно и требует загрузки дополнительной DLL с каждым процессом. (В качестве альтернативы вы можете настроить драйвер, который еще сложнее написать)
Менее надежный способ - регулярно перечислять все процессы (используя класс System.Diagnostics.Process ) (скажем, каждые 10-30 секунд), чтобы проверить, запущено ли приложение.
Также возможно отслеживать WMI-событие Win32_Process, InstanceCreationEvent из управляемого кода. Не уверен, насколько это надежно, но я подозреваю, что это было бы лучше, чем процессы опроса.
Отслеживайте, запущен ли процесс - хотя для этого должна быть запущена одна служба.
Если вы действительно не хотите использовать какие-либо ресурсы - напишите простую службу на чистом языке C. Сервисное приложение, написанное без MFC / ATL, может потреблять всего 300-400 КБ памяти и практически не требовать циклов ЦП. Когда запускается интересующий вас процесс, вы можете создавать свои службы C #.