HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services
Для створення служби слідує визвати функцію CreateService, вказавши дескриптор SC_HANDLE, отриманий при виклику OpenSCManager.
SC_HANDLE CreatrService (
SC_HANDLE hSCManager,
LPCTSTR lpServiceName,
LPCTSTR lpDisplayName,
DWORD dwDesiredAccess,
DWORD dwServiceType,
DWORD dwStartType,
DWORD dwErrorControl,
LPCTSTR lpBinaryPathName,
LPCTSTR lpLoadOrderGroup,
LPDWORD lpdwTagId,
LPCTSTR lpDependencies,
LPCTSTR lpServiceStartName,
LPCTSTR lpPassword);
Параметри
hSCManager – дескриптор SC_HANDLE, який отриманий від OpenSCManager.
lpServiceName – ім’я, по якому в подальшому ми будемо посилатися на службу, це одне із імен логічних служб, які вказані в таблиці при виклику StartServiceCtrlDispatcher. Замітимо, що для кожної логічної служби потрібний окремий виклик CreateService.
lpDisplayName – ім’я, під яким служба буде відображатися в реєстрі і в аплеті Служби, який відкривається на панелі керування в розділі Средства адміністрування. Це ім’я з’являється зразу після успішного виклику CreateService.
dwDesiredAccess може мати значення SERVICE_ALL_ACCESS або бути комбінацією GENERIC_READ, GENERIC_WRITE і GENERIC_EXECUTE. Більш детально це описано в вкладеній документації.
dwServiceType має значення, які представленні в таблиці 1.
dwStartType визначає порядок запуску служби. В наших прикладах використовується зачення SERVICE_DEMAND_START; інші значення (SERVICE_BOOT_START і SERVICE_SYSTEM_START) дозволяють запускати служби драйверів пристроїв в ході початкового запуску системи, а SERVICI_AUTO_START визначає, що служба повинна автоматично запускатися при запуску системи.
lpBinaryPathName вказує на виконуючий файл служби; розширення .exe вказувати не потрібно.
Інші параметри визначають ім’я облікового запису і пароль, групи для об’єднувальних служб і залежності в випадку декількох взаємозалежних служб.
Для отримання дескриптора іменованої служби використовується функція OpenService, для видалення служби із системного реєстру – DeleteService, а для закриття дескриптора – CloseServiceHandle.
Зразу після створення служба ще не працює. Запустіть функцію ServiceMain (), вказавши дескриптор, який отриманий від CreateService, і параметри командного рядка argc, argv, які вимагає „головна” функція служби (тобто функція, яка вказана в таблиці служб).
BOOL StartService (
SC_HANDLE hService,
DWORD argc,
LPTSTR argv [] )
Для керуючої дії над службою диспетчером SCMповинен бути викликаний обробник керування службою з відповідним кодом керування.
BOOL ControlService (
SC_HANDLE hService,
DWORD ControlCode,
LPSERVICE_STATUS pServStat)
Якщо доступ дозволений, ControlCode має одне із наступних значень:
SERVICE_CONTROL_STOP
SERVICE_CONTROL_PAUSE
SERVICE_CONTROL_CONTINUE
SERVICE_CONTROL_INTERROGATE
SERVICE_CONTROL_SHUTDOWN
або вказане користувачем значення в діапазоні 128-255. Це ті ж значення, які використовувалися для прапора fdwControl в функції ServerCtrlHandler.
pServStat вказує на структуру SERVICE_STATUS, яка отримує поточний стан. Ця ж структура, що використовується в функціїSetServiceStatus.
Отримати поточний стан служби в структурі SERVICE_STATUSдозволяє наступна служба:
BOOL QueryServiceStatus (
SC_HANDLE hService,
LPSERVICE_STATUS lpServiceStatus)
На мал.1 показаний диспетчер керування службами (SCM)і його взаємодія з службами й програмою керування. Служба реєструється в SCM і через SCM передаються всі команди для неї.
Мал.1. Керування службами NTчерезSCM
Створити процес, котрим можна керувати: встановити, запустити (після чого з’являється попередження про завершення сеансу), призупинити, відновити його роботу, зупинити, видалити.
Функція main – тіло програми, де виконуються, описані вище, функції.
main( int argc, char* argv[] );
Параметри:
argc – [число] кількість параметрів командного рядка.
argv - [текст] самі параметри командного рядка.
Для завершення роботи Windows можна скористатися функцією ExitWindowsEx. При цьому файлові буфери будуть скинуті на диск, а система наведена в стан, коли комп'ютер можна безпечно виключити.
В Windows NT/2000/XP: для завершення роботи системи необхідно одержати привілеї SE_SHUTDOWN_NAME, як і реалізовано у самій функції.
Функція керування службою.
Параметри:
Функція, в якій реєструється процес, встановлюється таймер на завершення сеансу (5 сек) і відповідні попередження користувача про ПЕРЕЗАГРУЗКУ (REBOOT).
Функція ErrorP вивід помилки і вихід із програми.
Параметри:
Source – текст попередження про помилку.
У роботі була створена служба Win32, яка перевантажує комп’ютер не відразу, а через деякий час (а саме через 5 секунд), щоб користувач міг завершити почату роботу. Була вивчена структура служб, їх встановлення та керування ними.
1. Конспект лекцій.
2. Майкл Дж. Янг VisualC++ 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);
}
}