На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
[!] Как относитесь к модерированию на этом форуме? Выскажите свое мнение здесь
  
> Работа с процессами
    Как узнать запущен ли процесс с определенным именем?
    Как убить процесс с определенным именем?
      Для NT:
      EnumProcesses
      OpenProcess
      EnumProcessModules ( модуль процесса - первый в списке)

      Убить процесс:
      TerminateProcess
        Snapshot
        Threads

        Добавлено
        Хорошая статья была в инете, сейчас оно не доступна.

        Функция TerminateProcess
        Для принудительного и безоговорочного завершения процессов в Win32 служит функция TerminateProcess:

        BOOL TerminateProcess(
        IN HANDLE hProcess, // описатель процесса
        IN DWORD dwExitCode // код завершения процесса
        );



        Сейчас я обязан сделать

        ПРЕДУПРЕЖДЕНИЕ
        Функцию TerminateProcess следует использовать только в исключительных случаях, когда исчерпаны все другие способы воздействия на процесс, поскольку она не позволяет потокам процесса выполнить очистку или сохранить данные, а также не оповещает загруженные DLL о завершении процесса.

        Я уверен, что это предупреждение вас не остановит. Для того, чтобы воспользоваться функцией TerminateProcess, необходимо получить описатель (handle) процесса. Зная идентификатор процесса, это несложно сделать c помощью функции OpenProcess, в итоге функция принудительного завершения процесса может выглядеть, например, так:

        BOOL KillProcess(
        IN DWORD dwProcessId
        )
        {
        // получаем описатель процесса
        HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId);
        if (hProcess == NULL)
        return FALSE;

        DWORD dwError = ERROR_SUCCESS;

        // пытаемся завершить процесс
        if (!TerminateProcess(hProcess, (DWORD)-1))
        dwError = GetLastError();

        // закрываем описатель процесса
        CloseHandle(hProcess);

        SetLastError(dwError);
        return dwError == ERROR_SUCCESS;
        }



        Завершение процессов и система безопасности Windows NT
        Если вы воспользуетесь функцией KillProcess, то обнаружите, что хотя она замечательно работает в Windows 95/98, в Windows NT она не может завершить некоторые процессы, потому что OpenProcess возвращает ошибку ERROR_ACCESS_DENIED. В частности, это происходит при попытке остановить процессы системных служб и некоторых DCOM-серверов. Как вы наверное уже догадываетесь, все дело в правах доступа.

        Как и любой объект ядра, объект процесса защищен дескриптором безопасности, который проверяется всякий раз, когда открывается описатель процесса. Для разных видов процессов дескриптор безопасности инициализируется по-разному. Вы можете воспользоваться тестовой программой Process Viewer для изучения дескрипторов безопасности процессов. Так, вы можете обнаружить, что дескриптор безопасности для интерактивных процессов выглядит так:

        Интерактивный пользователь Полный доступ
        СИСТЕМА Полный доступ

        в то время как для системных служб, работающих в контексте системной логон-сессии, дескриптор безопасности выглядит иначе:

        Администраторы Чтение
        СИСТЕМА Полный доступ

        Теперь ясно, почему OpenProcess не позволяет открыть описатель процесса системной службы с правами PROCESS_TERMINATE, даже если вызывающий пользователь является администратором компьютера. Это может показаться странным, почему администратор компьютера не может остановить любой процесс в системе. На самом деле, такая возможность у него есть, и заключается она в наличии привилегии SE_DEBUG_NAME.

        Все привилегии, которые имеет пользователь, могут находиться в двух состояниях: выключенном, неактивном, и включенном, активном состоянии. Администраторы компьютера имеют привилегию SE_DEBUG_NAME, но по умолчанию она находится в выключенном состоянии и не оказывает влияния на работу OpenProcess. Когда эта привилегия включена, вызывающий поток может открывать описатели процессов с любыми правами доступа, вне зависимости от того, какой дескриптор безопасности назначен объекту процесса. Это как раз то, что нам нужно.

        Ниже приведен модифицированный код функции KillProcess, которая при необходимости включает привилегию SE_DEBUG_NAME, чтобы получить описатель процесса.

        BOOL KillProcess(
        IN DWORD dwProcessId
        )
        {
        HANDLE hProcess;
        DWORD dwError;

        // сначала попробуем получить описатель процесса без
        // использования дополнительных привилегий
        hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId);
        if (hProcess == NULL)
        {
        if (GetLastError() != ERROR_ACCESS_DENIED)
        return FALSE;

        OSVERSIONINFO osvi;

        // определяем версию операционной системы
        osvi.dwOSVersionInfoSize = sizeof(osvi);
        GetVersionEx(&osvi);

        // мы больше ничего не можем сделать, если это не Windows NT
        if (osvi.dwPlatformId != VER_PLATFORM_WIN32_NT)
        return SetLastError(ERROR_ACCESS_DENIED), FALSE;

        // включим привилегию SE_DEBUG_NAME и попробуем еще раз

        TOKEN_PRIVILEGES Priv, PrivOld;
        DWORD cbPriv = sizeof(PrivOld);
        HANDLE hToken;

        // получаем токен текущего потока
        if (!OpenThreadToken(GetCurrentThread(),
        TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES,
        FALSE, &hToken))
        {
        if (GetLastError() != ERROR_NO_TOKEN)
        return FALSE;

        // используем токен процесса, если потоку не назначено
        // никакого токена
        if (!OpenProcessToken(GetCurrentProcess(),
        TOKEN_QUERY|TOKEN_ADJUST_PRIVILEGES,
        &hToken))
        return FALSE;
        }

        _ASSERTE(ANYSIZE_ARRAY > 0);

        Priv.PrivilegeCount = 1;
        Priv.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
        LookupPrivilegeValue(NULL, SE_DEBUG_NAME, &Priv.Privileges[0].Luid);

        // попробуем включить привилегию
        if (!AdjustTokenPrivileges(hToken, FALSE, &Priv, sizeof(Priv),
        &PrivOld, &cbPriv))
        {
        dwError = GetLastError();
        CloseHandle(hToken);
        return SetLastError(dwError), FALSE;
        }

        if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
        {
        // привилегия SE_DEBUG_NAME отсутствует в токене
        // вызывающего
        CloseHandle(hToken);
        return SetLastError(ERROR_ACCESS_DENIED), FALSE;
        }

        // попробуем открыть описатель процесса еще раз
        hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, dwProcessId);
        dwError = GetLastError();

        // восстанавливаем исходное состояние привилегии
        AdjustTokenPrivileges(hToken, FALSE, &PrivOld, sizeof(PrivOld),
        NULL, NULL);
        CloseHandle(hToken);

        if (hProcess == NULL)
        return SetLastError(FALSE), NULL;
        }

        // пытаемся завершить процесс
        if (!TerminateProcess(hProcess, (UINT)-1))
        {
        dwError = GetLastError();
        CloseHandle(hProcess);
        return SetLastError(dwError), FALSE;
        }

        CloseHandle(hProcess);

        // успешное завершение
        return TRUE;
        }



        Модифицированный вариант функции KillProcess сначала пытается открыть описатель процесса без использования привилегии SE_DEBUG_NAME. Если это у него не получается, он включает привилегию, получает описатель процесса и затем восстанавливает привилегию в исходное состояние.

        Теперь с помощью функции KillProcess можно убить практически любой процесс в системе, если, конечно, вы являетесь администратором компьютера. Чтобы проверить новый вариант функции в деле, вы можете воспользоваться тестовым приложением Process Viewer, которое теперь умеет завершать процессы. Например, можно остановить процесс Winlogon и через несколько секунд увидеть синий экран с надписью STOP 0xC000021A (не забудьте сохранить все открытые документы, прежде чем это сделать).

        Завершение дерева процессов
        Некоторое время назад мой коллега по работе попросил меня написать утилиту, аналогичную утилите kill в Unix. Он занимался переносом каких-то скриптов из Unix на Windows NT и ему нужна была такая утилита. Использовать утилиту, поставляемую в составе Windows NT Resource Kit, он не мог по лицензионным (а может, и религиозным) соображениям.

        Я быстро оформил функцию KillProcess в виде небольшого консольного приложения и отправил ему. Коллега был приятно удивлен скорости, с которой я выполнил его просьбу, однако он отметил, что моя версия kill работает не так, как аналог из Unix. В Unix, kill завершает не только сам процесс, но и все дочерние процессы, которые были запущены из него прямо или косвенно (так называемое дерево процессов). Моя же версия работала как kill -9, когда завершается только указанный процесс. Таким образом, встала задача повторить поведение Unix kill на Windows NT.

        Чтобы завершить все процессы в дереве, необходимо каким-либо образом отследить отношения родитель - потомок между процессами. В Windows 9x/Me и Windows 2000/XP это позволяют сделать функции перечисления процессов из ToolHelp32 API, в частности поле th32ParentProcessID структуры PROCESSENTRY32 содержит идентификатор родительского процесса. В Windows NT 4.0 эту информацию можно получить с помощью официально недокументированной функции ZwQuerySystemInformation. (Подробнее о функциях перечисления процессов рассказано в статье Как получить список запущенных процессов?)

        Ниже приведен код функции KillProcessEx, которая позволяет завершать как отдельный процесс, так и целое дерево процессов. Если вы читали статью про перечисление процессов, то найдете много знакомых кусков кода в этой функции.

        // вспомогательная функция, которая рекурсивно обходит дерево процессов
        // в Windows NT и завершает все процессы в дереве
        static
        BOOL KillProcessTreeNtHelper(
        IN PSYSTEM_PROCESSES pInfo,
        IN DWORD dwProcessId
        )
        {
        _ASSERTE(pInfo != NULL);

        PSYSTEM_PROCESSES p = pInfo;

        // сначала завершаем все дочерние процессы
        for (;;)
        {
        if (p->InheritedFromProcessId == dwProcessId)
        KillProcessTreeNtHelper(pInfo, p->ProcessId);

        if (p->NextEntryDelta == 0)
        break;

        // находим адрес следующей структуры
        p = (PSYSTEM_PROCESSES)(((LPBYTE)p) + p->NextEntryDelta);
        }

        // завершаем исходный процесс
        if (!KillProcess(dwProcessId))
        return GetLastError();

        return ERROR_SUCCESS;
        }

        // вспомогательная функция, которая рекурсивно обходит дерево процессов
        // в Windows 9x и завершает все процессы в дереве
        static
        BOOL KillProcessTreeWinHelper(
        IN DWORD dwProcessId
        )
        {
        HINSTANCE hKernel;
        HANDLE (WINAPI * _CreateToolhelp32Snapshot)(DWORD, DWORD);
        BOOL (WINAPI * _Process32First)(HANDLE, PROCESSENTRY32 *);
        BOOL (WINAPI * _Process32Next)(HANDLE, PROCESSENTRY32 *);

        // получаем описатель KERNEL32.DLL
        hKernel = GetModuleHandle(_T("kernel32.dll"));
        _ASSERTE(hKernel != NULL);

        // находим необходимые функции KERNEL32.DLL
        *(FARPROC *)&_CreateToolhelp32Snapshot =
        GetProcAddress(hKernel, "CreateToolhelp32Snapshot");
        *(FARPROC *)&_Process32First =
        GetProcAddress(hKernel, "Process32First");
        *(FARPROC *)&_Process32Next =
        GetProcAddress(hKernel, "Process32Next");

        if (_CreateToolhelp32Snapshot == NULL ||
        _Process32First == NULL ||
        _Process32Next == NULL)
        return ERROR_PROC_NOT_FOUND;

        HANDLE hSnapshot;
        PROCESSENTRY32 Entry;

        // создаем моментальный снимок процессов
        hSnapshot = _CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
        if (hSnapshot == INVALID_HANDLE_VALUE)
        return GetLastError();

        Entry.dwSize = sizeof(Entry);
        if (!_Process32First(hSnapshot, &Entry))
        {
        DWORD dwError = GetLastError();
        CloseHandle(hSnapshot);
        return dwError;
        }

        // завершаем сначала дочерние процессы
        do
        {
        if (Entry.th32ParentProcessID == dwProcessId)
        KillProcessTreeWinHelper(Entry.th32ProcessID);

        Entry.dwSize = sizeof(Entry);
        }
        while (_Process32Next(hSnapshot, &Entry));

        CloseHandle(hSnapshot);

        // завершаем исходный процесс
        if (!KillProcess(dwProcessId))
        return GetLastError();

        return ERROR_SUCCESS;
        }

        BOOL KillProcessEx(
        IN DWORD dwProcessId, // идентификатор процесса
        IN BOOL bTree // признак завершения всего дерева
        )
        {
        if (!bTree)
        return KillProcess(dwProcessId);

        OSVERSIONINFO osvi;
        DWORD dwError;

        // определяем версию операционной системы
        osvi.dwOSVersionInfoSize = sizeof(osvi);
        GetVersionEx(&osvi);

        if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
        {
        HINSTANCE hNtDll;
        NTSTATUS (WINAPI * _ZwQuerySystemInformation)(UINT, PVOID, ULONG, PULONG);

        // получаем описатель NTDLL.DLL
        hNtDll = GetModuleHandle(_T("ntdll.dll"));
        _ASSERTE(hNtDll != NULL);

        // находим адрес ZwQuerySystemInformation
        *(FARPROC *)&_ZwQuerySystemInformation =
        GetProcAddress(hNtDll, "ZwQuerySystemInformation");
        if (_ZwQuerySystemInformation == NULL)
        return SetLastError(ERROR_PROC_NOT_FOUND), NULL;

        // получаем описатель кучи процесса по умолчанию
        HANDLE hHeap = GetProcessHeap();

        NTSTATUS Status;
        ULONG cbBuffer = 0x8000;
        PVOID pBuffer = NULL;

        // трудно заранее определить, какой размер выходного
        // буфера будет достаточным, поэтому мы начинам с буфера
        // размером 32K и увеличиваем его по необходимости
        do
        {
        pBuffer = HeapAlloc(hHeap, 0, cbBuffer);
        if (pBuffer == NULL)
        return SetLastError(ERROR_NOT_ENOUGH_MEMORY), FALSE;

        Status = _ZwQuerySystemInformation(
        SystemProcessesAndThreadsInformation,
        pBuffer, cbBuffer, NULL);

        if (Status == STATUS_INFO_LENGTH_MISMATCH)
        {
        HeapFree(hHeap, 0, pBuffer);
        cbBuffer *= 2;
        }
        else if (!NT_SUCCESS(Status))
        {
        HeapFree(hHeap, 0, pBuffer);
        return SetLastError(Status), NULL;
        }
        }
        while (Status == STATUS_INFO_LENGTH_MISMATCH);

        // вызываем вспомогательную функцию
        dwError = KillProcessTreeNtHelper((PSYSTEM_PROCESSES)pBuffer,
        dwProcessId);

        HeapFree(hHeap, 0, pBuffer);
        }
        else
        {
        // вызываем вспомогательную функцию
        dwError = KillProcessTreeWinHelper(dwProcessId);
        }

        SetLastError(dwError);
        return dwError == ERROR_SUCCESS;
        }



        Используя функцию KillProcessEx, я смог написать утилиту PKILL, которая более точно соответствует своему Unix-аналогу. Эта утилита прилагается в качестве тестового приложения к данной статье.

        Завершение 16-битных задач в Windows NT
        Рассмотрение вопроса о завершении процессов не будет полным, если мы не затронем тему 16-битных задач на Windows NT. Для завершения 16-битных задач VDMDBG.DLL предоставляет функцию VDMTerminateTaskWOW:

        BOOL VDMTerminateTaskWOW(
        IN DWORD dwProcessId, // идентификатор виртуальной DOS-машины
        IN WORD hTask16 // идентификатор 16-битной задачи
        );



        Параметры этой функции говорят сами за себя. Тестовое приложение Process Viewer использует ее для завершения 16-битных задач на Windows NT.

        Заключение
        Таким образом, теперь вы во всеоружии готовы завершить любой процесс. Но, как и всякое оружие, используйте возможность принудительного завершения процессов с осторожностью.
          M
          Используем тег [code] для добавление текста программы в пост! Устное предупреждение! Читаем правила!
            Цитата
            Как узнать запущен ли процесс с определенным именем?
            Как убить процесс с определенным именем?

            В FAQ по билдеру это есть!
            http:///sources.ru/builder/faq/058.html - сначала узнай pid процесса
            http:///sources.ru/builder/faq/063.html - а потом уже убей его!
            Думаю разберешься :) "Определенное имя" - это обычно название exe-файла.
              Спасиюо всем
                P.S материал из ФАКа не работает - точнее нахождение PID'a по имени.
                Работает через раз...
                  Попробуй это :D
                  ExpandedWrap disabled
                    HANDLE PHandle;
                    PROCESSENTRY32 ProcEntry;
                    BOOL Result;
                     
                     PHandle = CreateToolhelp32Snapshot (TH32CS_SNAPPROCESS,0);
                     ProcEntry.dwSize = sizeof(PROCESSENTRY32);
                     if (!Process32First(PHandle, &ProcEntry))
                      {
                      if (ExtractFileName(ProcEntry.szExeFile) == "PROG_NAME.exe") Result = true;
                      }
                     while(Process32Next(PHandle, &ProcEntry))
                      if (ExtractFileName(ProcEntry.szExeFile) == "PROG_NAME.exe") Result = true;
                     // анализ результата
                     if (Result == true) MessageBox(0, "У вас запущен PROG_NAME.", "Внимание", MB_OK);
                     Application->Terminate();
                    попробую - tnx.
                    0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
                    0 пользователей:


                    Рейтинг@Mail.ru
                    [ Script execution time: 0,0460 ]   [ 16 queries used ]   [ Generated: 22.07.24, 18:00 GMT ]