Мне было поручено пытаться отслеживать активность всех программ с графическим интерфейсом, порожденных начальной программой, и убивать их, когда они становятся неактивными в течение заданного периода времени. Это не может быть системной активностью, так как мы должны закрывать программы, даже если пользователь какое-то время просто использует другую программу (не порожденную исходной программой). Процессы запуска приложений — это программа .Net, и это не изменится. Я был в состоянии использовать SetWindowsHookEx
установить низкоуровневые крючки (WH_KEYBOARD_LL
а также WH_MOUSE_LL
) из .Net, но не был уверен, как связать сообщения с процессами, которые я начал (используя System.Diagnostics.Process
.) Я мог бы перехватывать события из приложения, выполняющего перехват, но он всегда заканчивался неудачей, когда я пытался перехватить определенный поток для порожденной программы.
Самым близким, что я смог прийти, было создание C ++ dll, который вызывает SetWindowsHookEx
от имени программы .Net. Используя этот метод, я могу подключиться к первому потоку порожденного notepad.exe, но функция обратного вызова никогда не вызывается. Если я укажу идентификатор потока, равный 0, обратный вызов вызывается, когда в исходном приложении есть активность, поэтому я знаю SetWindowsHookEx
работает в таком случае.
Я еще не полностью понимаю перехват, но из того, что я прочитал, это не сработает, потому что адрес функции обратного вызова dll будет бессмысленным для перехваченного процесса. Это проблема, с которой я сталкиваюсь? Если да, есть ли способ обойти это или кто-то может предложить альтернативу, чтобы определить, активно ли используется порожденный процесс?
.Чистый (C #) код:
private const int WH_MOUSE = 14;
[DllImport("TestHook.dll", EntryPoint="InitializeHook", SetLastError=true, CharSet=CharSet.Unicode, ExactSpelling=true, CallingConvention=CallingConvention.StdCall)]
private static extern bool InitializeHook(uint hookType, int threadID);
private void HookEm() {
//...
System.Diagnostics.Process testProcess = System.Diagnostics.Process.Start("notepad");
if(!InitializeHook(WH_MOUSE, testProcess.Threads[0].Id)) // The notepad.exe thread I know hooks
MessageBox.Show("Boom!");
//...
}
C ++ dll:
HHOOK hookValue;
bool InitializeHook(UINT hookID, int threadID) {
//... Determining which callback to use
hookValue = SetWindowsHookEx(hookID, (HOOKPROC)MouseHookCallback, _dllInstance, threadID);
//...
return hookValue != NULL
}
static LRESULT CALLBACK MouseHookCallback(int code, WPARAM wparam, LPARAM lparam) {
//From what I can see, this is never touched when hooking a thread on an outside process
}
Все это в настоящее время выполняется на 32-битном компьютере с Windows XP. Я понимаю, что отдельная 64-битная DLL будет необходима для работы с порожденными 64-битными приложениями.
Для тех, кто сталкивается с подобной проблемой, наше окончательное решение состояло в том, чтобы проверить окно переднего плана с интервалом, используя GetWindowThreadProcessId(GetForegroundWindow(), ref foregroundWindowProcessId)
и ведение карты наших порожденных идентификаторов процессов. Каждый раз, когда срабатывает этот таймер, мы устанавливаем логическое значение для проверки функции, которая вызывает CallNextHookEx
так как это должно быть эффективно / быстро, иначе окна отцепят нас. Затем мы используем вышеуказанное логическое значение, и система перехватывает SetWindowsHookEx
определить, активен ли пользователь в нашем приложении. Для этого решения не требовался C ++ dll.
Других решений пока нет …