Отримати поточний стан служби в структурі SERVICE_STATUS дозволяє наступна служба:
BOOL QueryServiceStatus (
SC_HANDLE hService,
LPSERVICE_STATUS lpServiceStatus)
Резюме: робота служб і керування ними
На мал.1 показаний диспетчер керування службами (SCM) і його взаємодія з службами й програмою керування. Служба реєструється в SCM і через SCM передаються всі команди для неї.
Мал.1. Керування службами NT через SCM
Ідея розв’язку задачі
Створити процес, котрим можна керувати: встановити, запустити (після чого з’являється попередження про завершення сеансу), призупинити, відновити його роботу, зупинити, видалити.
Опис функцій, які використовувалися для програмної реалізації служби
Main
Функція main – тіло програми, де виконуються, описані вище, функції.
main(int argc, char* argv[]);
Параметри:
argc – [число] кількість параметрів командного рядка.
argv - [текст] самі параметри командного рядка.
MySystemReboot
Для завершення роботи Windows можна скористатися функцією ExitWindowsEx. При цьому файлові буфери будуть скинуті на диск, а система наведена в стан, коли комп'ютер можна безпечно виключити.
В Windows NT/2000/XP: для завершення роботи системи необхідно одержати привілеї SE_SHUTDOWN_NAME, як і реалізовано у самій функції.
S_Headler
Функція керування службою.
void WINAPI S_Handler (DWORD Code1);
Параметри:
Code1 - значення команди (старт, стоп, пауза)
3.4 ServiceMain
Функція, в якій реєструється процес, встановлюється таймер на завершення сеансу (5 сек) і відповідні попередження користувача про ПЕРЕЗАГРУЗКУ (REBOOT).
ErrorP
Функція ErrorP вивід помилки і вихід із програми.
void ErrorP (LPTSTR Source);
Параметри:
Source – текст попередження про помилку.
Висновки
У роботі була створена служба Win32, яка перевантажує комп’ютер не відразу, а через деякий час (а саме через 5 секунд), щоб користувач міг завершити почату роботу. Була вивчена структура служб, їх встановлення та керування ними.
Література
1. Конспект лекцій.
2. Майкл Дж. Янг Visual C++ 6 (том 1,2).
3. MSDN Library, February 2005.
Програмна реалізація
#define _WIN32_WINNT 0x0400
#include <windows.h>
#include <stdio.h>
#include <string.h>
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
LPSTR MyServiceName = "DClock";
LPSTR MyDisplayName = "Desktop Clock";
void ErrorP(LPTSTR Source) // Повідомляємо про помилку і виходимо
{
LPVOID MsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR) &MsgBuf, 0, NULL);
printf("%s: %s", Source, MsgBuf);
exit(1);
}
SERVICE_STATUS_HANDLE s_s_handle;
SERVICE_STATUS s_status;
void WINAPI S_Handler(DWORD Code1) // функція керування службою
{
switch (Code1)
{
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
s_status.dwCurrentState = SERVICE_STOPPED;
break;
case SERVICE_CONTROL_PAUSE:
s_status.dwCurrentState = SERVICE_PAUSED;
break;
case SERVICE_CONTROL_CONTINUE:
s_status.dwCurrentState = SERVICE_RUNNING;
break;
}
SetServiceStatus(s_s_handle, &s_status); //рефреш властивостей функції
}
BOOL MySystemReboot() // ф-ція перезагрузки
{
//HANDLE hToken;
//TOKEN_PRIVILEGES tkp;
// отримуємо маркер поточного процесу
if (!OpenProcessToken(GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
return(FALSE);
// отримуємо LUID для привілеїв завершення роботи
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME,
&tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1; // встановлений один привілей
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
// отримуємо привілеї завершення роботи для даного процесу
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0,
(PTOKEN_PRIVILEGES)NULL, 0);
if (GetLastError()!= ERROR_SUCCESS)
return FALSE;
// перегружаємо систему і змушуємо всі інші програми закритися
if (!ExitWindowsEx(EWX_REBOOT | EWX_FORCE, 0))
return FALSE;
return TRUE;
}
void WINAPI ServiceMain(DWORD dwArgc, LPSTR *psArgv)
{
HANDLE h_Timer;
SYSTEMTIME st_Time;
FILETIME f_Time;
s_s_handle = RegisterServiceCtrlHandler(MyServiceName, S_Handler); // Запускаємо службу
s_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
s_status.dwCurrentState = SERVICE_RUNNING;
s_status.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE;
s_status.dwWin32ExitCode = NOERROR;
s_status.dwCheckPoint = 0;
s_status.dwWaitHint = 0;
SetServiceStatus(s_s_handle, &s_status);
MessageBox(0, "System will be restarted in 5 seconds!",
"Shutdown", MB_ICONINFORMATION);
h_Timer = CreateWaitableTimer(NULL, FALSE, NULL); // встановлюємо таймер
if(!h_Timer)
ErrorP("CreateWaitableTimer");
GetSystemTime(&st_Time);
st_Time.wSecond += 5;
SystemTimeToFileTime(&st_Time, &f_Time);
if(!SetWaitableTimer(h_Timer, (LARGE_INTEGER*)&f_Time, 1000, NULL,
NULL, FALSE)
)
ErrorP("SetWaitableTimer");
WaitForSingleObject(h_Timer, INFINITE);
MySystemReboot(); // перегружаємося =)
}
void main(int argc, char* argv[])
{
if(argc > 1)
{
SC_HANDLE h_SC;
h_SC = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); //менеджер керування службою
if(!h_SC)
ErrorP("OpenSCManager");
if(!stricmp(argv[1], "create"))
{
CHAR lpPath[MAX_PATH]; // Створюємо службу
GetModuleFileName(NULL, lpPath, MAX_PATH);
h_SC = CreateService(h_SC, MyServiceName,
MyDisplayName, 0, SERVICE_WIN32_SHARE_PROCESS |
SERVICE_INTERACTIVE_PROCESS, SERVICE_AUTO_START,
SERVICE_ERROR_IGNORE, lpPath, NULL, NULL, NULL,
NULL, NULL);
if(!h_SC)
ErrorP("CreateService");
}
else if(!stricmp(argv[1], "start"))
{
h_SC = OpenService(h_SC, MyServiceName, SERVICE_START); // Запускаємо службу
if(!h_SC)
ErrorP("OpenService");
if(!StartService(h_SC, 0, NULL))
ErrorP("StartService");
}
else if(!stricmp(argv[1], "pause"))
{
h_SC = OpenService(h_SC, MyServiceName, // призупиняємо службу
SERVICE_PAUSE_CONTINUE);
if(!h_SC)
ErrorP("OpenService");
if(!ControlService(h_SC, SERVICE_CONTROL_PAUSE, &s_status))
ErrorP("ControlService");
}
else if(!stricmp(argv[1], "continue"))
{
h_SC = OpenService(h_SC, MyServiceName, // продовжуємо призупинену службу
SERVICE_PAUSE_CONTINUE);
if(!h_SC)
ErrorP("OpenService");
if(!ControlService(h_SC, SERVICE_CONTROL_CONTINUE,
&s_status)
)
ErrorP("ControlService");
}
else if(!stricmp(argv[1], "stop"))
{
h_SC = OpenService(h_SC, MyServiceName, SERVICE_STOP); // зупиняємо службу
if(!h_SC)
ErrorP("OpenService");
if(!ControlService(h_SC, SERVICE_CONTROL_STOP, &s_status))
ErrorP("ControlService");
}
else if(!stricmp(argv[1], "delete"))
{
h_SC = OpenService(h_SC, MyServiceName, DELETE); // видаляємо службу
if(!h_SC)
ErrorP("OpenService");
if(!DeleteService(h_SC))
ErrorP("DeleteService");
}
else
printf("Invalid command\n");
}
else
{
SERVICE_TABLE_ENTRY s_Table[] = { // Запускаємо на виконання
{ MyServiceName, ServiceMain },
{ NULL, NULL }
};
StartServiceCtrlDispatcher(s_Table);
}
}