Смекни!
smekni.com

Розробка служби Win32 (стр. 3 из 3)

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

2.2 Ідея розв’язку задачі

Створити процес, котрим можна керувати: встановити, запустити (після чого з’являється попередження про завершення сеансу), призупинити, відновити його роботу, зупинити, видалити.


3. Опис функцій, які використовувалися для програмної реалізації служби

3.1 Main

Функція main – тіло програми, де виконуються, описані вище, функції.

main( int argc, char* argv[] );

Параметри:

argc – [число] кількість параметрів командного рядка.

argv - [текст] самі параметри командного рядка.

3.2MySystemReboot

Для завершення роботи Windows можна скористатися функцією ExitWindowsEx. При цьому файлові буфери будуть скинуті на диск, а система наведена в стан, коли комп'ютер можна безпечно виключити.

В Windows NT/2000/XP: для завершення роботи системи необхідно одержати привілеї SE_SHUTDOWN_NAME, як і реалізовано у самій функції.

3.3 S_Headler

Функція керування службою.

void WINAPI S_Handler (DWORD Code1);

Параметри:

Code1 - значення команди (старт, стоп, пауза)


3.4 ServiceMain

Функція, в якій реєструється процес, встановлюється таймер на завершення сеансу (5 сек) і відповідні попередження користувача про ПЕРЕЗАГРУЗКУ (REBOOT).

3.5 ErrorP

Функція ErrorP вивід помилки і вихід із програми.

void ErrorP ( LPTSTR Source );

Параметри:

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&bsol;n");

}

else

{

SERVICE_TABLE_ENTRY s_Table[] = { // Запускаємо на виконання

{ MyServiceName, ServiceMain },

{ NULL, NULL }

};

StartServiceCtrlDispatcher(s_Table);

}

}