Читаем без скачивания Системное программирование в среде Windows - Джонсон Харт
Шрифт:
Интервал:
Закладка:
Создание фоновых задач
Программа 6.3 реализует процессор задач, в котором пользователю предлагается ввести одну из трех возможных команд для их дальнейшего выполнения программой. В этой программе используется набор функций управления задачами, представленный программами 6.4, 6.5 и 6.6.
Программа 6.3. JobShell: создание, вывод списка и прекращение выполнения фоновых задач/* Глава 6. */
/* JobShell.с – команды управления задачами:
jobbg — Выполнить задачу в фоновом режиме.
jobs — Вывести список всех фоновых задач.
kill — Прекратить выполнение указанной задачи из семейства задач.
Существует опция, позволяющая генерировать управляющие сигналы консоли. */
#include "EvryThng.h"
#include "JobMgt.h"
int _tmain(int argc, LPTSTR argv[]) {
BOOL Exit = FALSE;
TCHAR Command[MAX_COMMAND_LINE + 10], *pc;
DWORD i, LocArgc; /* Локальный параметр argc. */
TCHAR argstr[MAX_ARG][MAX_COMMAND_LINE];
LPTSTR pArgs[MAX_ARG];
for (i = 0; i < MAX_ARG; i++) pArgs[i] = argstr[i];
/* Вывести подсказку пользователю, считать команду и выполнить ее. */
_tprintf(_Т("Управление задачами Windowsn"));
while (!Exit) {
_tprintf(_T("%s"), _T("JM$"));
_fgetts(Command, MAX_COMMAND_LINE, stdin);
pc = strchr(Command, 'n');
*pc = ' ';
/* Выполнить синтаксический разбор входных данных с целью получения командной строки для новой задачи. */
GetArgs(Command, &LocArgc, pArgs); /* См. Приложение А. */
CharLower(argstr[0]);
if(_tcscmp(argstr[0], _T("jobbg")) == 0) {
Jobbg(LocArgc, pArgs, Command);
} else if(_tcscmp(argstr[0], _T("jobs")) == 0) {
Jobs(LocArgc, pArgs, Command);
} else if(_tcscmp(argstr[0], _T("kill")) == 0) {
Kill(LocArgc, pArgs, Command);
} else if(_tcscmp(argstr[0], _T("quit")) == 0) {
Exit = TRUE;
} else _tprintf(_T("Такой команды не существует. Повторите вводn"));
}
return 0;
}
/* jobbg [параметры] командная строка [Параметры являются взаимоисключающими]
–с: Предоставить консоль новому процессу.
-d: Отсоединить новый процесс без предоставления ему консоли.
Если параметры не заданы, процесс разделяет консоль с jobbg. */
int Jobbg(int argc, LPTSTR argv[], LPTSTR Command) {
DWORD fCreate;
LONG JobNo;
BOOL Flags[2];
STARTUPINFO Startup;
PROCESS_INFORMATION ProcessInfo;
LPTSTR targv = SkipArg(Command);
GetStartupInfo(&StartUp);
Options(argc, argv, _T("cd"), &Flags[0], &Flags[1], NULL);
/* Пропустить также поле параметра, если он присутствует. */
if (argv[1][0] == '-') targv = SkipArg(targv);
fCreate = Flags[0] ? CREATE_NEW_CONSOLE : Flags [1] ? DETACHED_PROCESS : 0;
/* Создать приостановленную задачу/поток. Возобновить выполнение после ввода номера задачи. */
CreateProcess(NULL, targv, NULL, NULL, TRUE, fCreate | CREATE_SUSPENDED | CREATE_NEW_PROCESS_GROUP, NULL, NULL, &StartUp, &ProcessInfo);
/* Создать номер задачи и ввести ID и дескриптор процесса в "базу данных" задачи. */
JobNo = GetJobNumber(&ProcessInfo, targv); /* См. "JobMgt.h" */
if (JobNo >= 0) ResumeThread(ProcessInfo.hThread);
else {
TerminateProcess(ProcessInfo.hProcess, 3);
CloseHandle(ProcessInfo.hProcess);
ReportError(_T("Ошибка: Не хватает места в списке задач."), 0, FALSE);
return 5;
}
CloseHandle(ProcessInfo.hThread);
CloseHandle(ProcessInfo.hProcess);
_tprintf(_T(" [%d] %dn"), JobNo, ProcessInfo.dwProcessId);
return 0;
}
/* jobs: вывод списка всех выполняющихся и остановленных задач. */
int Jobs(int argc, LPTSTR argv[], LPTSTR Command) {
if (!DisplayJobs ()) return 1; /*См. описание функций управления задачами*/
return 0;
}
/* kill [параметры] Номер задачи (JobNumber)
–b: Генерировать Ctrl-Break.
–с: Генерировать Ctrl-C.
В противном случае прекратить выполнение процесса. */
int Kill(int argc, LPTSTR argv[], LPTSTR Command) {
DWORD ProcessId, JobNumber, iJobNo;
HANDLE hProcess;
BOOL CntrlC, CntrlB, Killed;
iJobNo = Options(argc, argv, _T("bc"), &CntrlB, &CntrlC, NULL);
/* Найти ID процесса, связанного с данной задачей. */
JobNumber = _ttoi(argv [iJobNo]);
ProcessId = FindProcessId(JobNumber); /* См. описание функций управления задачами. */
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, ProcessId);
if (hProcess == NULL) { /* ID процесса может не использоваться. */
ReportError(_T("Выполнение процесса уже прекращено.n"), 0, FALSE);
return 2;
}
if (CntrlB) GenerateConsoleCtrlEvent(CTRL_BREAK_EVENT, ProcessId);
else if (CntrlC) GenerateConsoleCtrlEvent(CTRL_C_EVENT, ProcessId);
else TerminateProcess(hProcess, JM_EXIT_CODE);
WaitForSingleObject(hProcess, 5000);
CloseHandle(hProcess);
_tprintf(T("Задача [%d] прекращена или приостановлена n"), JobNumber);
return 0;
}
Обратите внимание на то, как команда jobbg создает процесс в приостановленном состоянии, а затем вызывает функцию управления задачами Get JobNumber (программа 6.4) для получения номера задачи, а также регистрации задачи и процесса, который с ней связан. Если в силу каких-либо причин задача не может быть зарегистрирована, выполнение данного процесса немедленно прекращается. Обычно' генерируется корректный номер задачи, после чего выполнение основного потока возобновляется, и он может продолжать выполнение.
Получение номера задачи
Следующие три программы представляют три отдельные функции управления задачами. Все эти функции включены в единый файл JobMgt.c, содержащий все исходные тексты.
Первая из них, программа 6.4, представляет функцию Get JobNumber. Обратите внимание на использование блокирования файлов, а также обработчиков завершения, осуществляющих разблокирование файлов. Эта методика обеспечивает защиту от исключений и непреднамеренного обхода вызова функции разблокирования файлов. Переходы такого рода могут быть случайно вставлены в процессе сопровождения кода, даже если исходная программа корректна. Обратите также внимание на блокирование попыток записи за пределами конца файла в тех случаях, когда файл должен быть расширен за счет добавления новой записи.
Программа 6.4. JobMgt: создание информации о новой задаче/* Вспомогательная функция управления задачами. */
#include "EvryThng.h"
#include "JobMgt.h" /* Листинг приведен в приложении А. */
void GetJobMgtFileName (LPTSTR);
LONG GetJobNumber(PROCESS_INFORMATION *pProcessInfo, LPCTSTR Command)
/* Создать номер задачи для нового процесса и ввести информацию о новом процессе в базу данных задачи. */
{
HANDLE hJobData, hProcess;
JM_JOB JobRecord;
DWORD JobNumber = 0, nXfer, ExitCode, FsLow, FsHigh;
TCHAR JobMgtFileName[MAX_PATH];
OVERLAPPED RegionStart;
if (!GetJobMgtFileName(JobMgtFileName)) return –1;
/* Предоставление результата в виде строки "tmpUserName.JobMgt" */
hJobData = CreateFile(JobMgtFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (hJobData == INVALID_HANDLE_VALUE) return –1;
/* Блокировать весь файл плюс одну возможную запись для получения исключительного доступа. */
RegionStart.Offset = 0;
RegionStart.OffsetHigh = 0;
RegionStart.hEvent = (HANDLE)0;
FsLow = GetFileSize(hJobData, &FsHigh);
LockFileEx(hJobData, LOCKFILE_EXCLUSIVE_LOCK, 0, FsLow + SJM_JOB, 0, &RegionStart);
__try {
/* Чтение записи для нахождения пустого сегмента. */
while(ReadFile(hJobData, &JobRecord, SJM_JOB, &nXfer, NULL) && (nXfer > 0)) {
if (JobRecord.ProcessId == 0) break;
hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, JobRecord.ProcessId);
if (hProcess == NULL) break;
if (GetExitCodeProcess(hProcess, &ExitCode) && (ExitCode != STILL_ACTIVE)) break;
JobNumber++;
}
/* Либо найден пустой сегмент, либо мы находимся в конце файла и должны создать новый сегмент. */
if (nXfer != 0) /* Не конец файла. Резервировать. */
SetFilePointer(hJobData, –(LONG)SJM_JOB, NULL, FILE_CURRENT);
JobRecord.ProcessId = pProcessInfo->dwProcessId;
_tcsnccpy(JobRecord.CommandLine, Command, MAX_PATH);
WriteFile(hJobData, &JobRecord, SJM_JOB, &nXfer, NULL);
} /* Конец try-блока. */
__finally {
UnlockFileEx(hJobData, 0, FsLow + SJM_JOB, 0, &RegionStart);
CloseHandle(hJobData);
}
return JobNumber + 1;
}
Вывод списка фоновых задач
Программа 6.5 реализует функцию управления задачами DisplayJobs.
Программа 6.5. JobMgt: отображение списка активных задачBOOL DisplayJobs(void)
/* Просмотреть файл базы данных, сообщить статус задачи. */
{
HANDLE hJobData, hProcess;
JM_JOB JobRecord;
DWORD JobNumber = 0, nXfer, ExitCode, FsLow, FsHigh;