Программирование для Windows NT© Александр Фролов, Григорий ФроловТом 27, часть 2, М.: Диалог-МИФИ, 1996, 272 стр. Приложение SRVCTRLВ этом разделе мы приведем исходные тексты простейшего сервиса Simple и приложения SRVCTRL, с помощью которого можно установить данный сервис, запустить, остановить или удалить его, а также определить текущую конфигурацию. Главное меню приложения SRVCTRL и временное меню Service показано на рис. 5.4. Рис. 5.4. Главное меню приложения SRVCTRL С помощью строки Install вы можете установить сервис. Не забудьте перед этим записать загрузочный файл сервиса в каталог c:\ntbk2\src\service\small\debug, так как приложение SRVCTRL может установить сервис только из этого каталога. После установки имя сервиса появится в списке сервисов, который можно просмотреть при помощи приложения Services из папки Control Panel (рис. 5.5). Рис. 5.5. В списке сервисов появился новый сервис Sample of simple service Если теперь из меню Service нашего приложения выбрать строку Get configuration, на экране появится диалоговая панель, в которой будут отображены некоторые поля структуры конфигрурации сервиса (рис. 5.6). Рис. 5.6. Просмотр конфигурации сервиса Исходный текст сервисаИсходный текст сервиса представлен в листинге 5.1. Так как ранее мы уже подробно описывали структуру этого сервиса, то мы оставим вам этот листинг и листинг приложения SRVCTRL на самостоятельное изучение. Листинг 5.1. Файл service/small/small.c // ================================================== // Сервис "Sample of simple service" // Шаблон простейшего сервиса Windows NT // // (С) Фролов А.В., 1996 // Email: frolov@glas.apc.org // ================================================== #define STRICT #include <windows.h> #include <windowsx.h> #include <stdio.h> #include <conio.h> #include <stdlib.h> #include "small.h" // ----------------------------------------------------- // Глобальные переменные // ----------------------------------------------------- // Код ошибки DWORD dwErrCode; // Текущее состояние сервиса SERVICE_STATUS ss; // Идентификатор сервиса SERVICE_STATUS_HANDLE ssHandle; // ----------------------------------------------------- // Функция main // Точка входа процесса // ----------------------------------------------------- void main(int agrc, char *argv[]) { // Таблица точек входа SERVICE_TABLE_ENTRY DispatcherTable[] = { { // Имя сервиса MYServiceName, // Функция main сервиса (LPSERVICE_MAIN_FUNCTION)ServiceMain }, { NULL, NULL } }; printf("Sample of simple service\n" "(C) A. Frolov, 1996, Email: frolov@glas.apc.org\n"); // Запуск диспетчера if(!StartServiceCtrlDispatcher(DispatcherTable)) { fprintf(stdout, "StartServiceCtrlDispatcher: Error %ld\n", GetLastError()); getch(); return; } } // ----------------------------------------------------- // Функция ServiceMain // Точка входа сервиса // ----------------------------------------------------- void WINAPI ServiceMain(DWORD argc, LPSTR *argv) { // Регистрируем управляющую функцию сервиса ssHandle = RegisterServiceCtrlHandler(MYServiceName, ServiceControl); if(!ssHandle) return; // Устанавливаем состояние сервиса // Сервис работает как отдельный процесс ss.dwServiceType = SERVICE_WIN32_OWN_PROCESS; // Код ошибки при инициализации и завершения сервиса // не используется ss.dwServiceSpecificExitCode = 0; // Начинаем запуск сервиса. // Прежде всего устанавливаем состояние ожидания // запуска сервиса ReportStatus(SERVICE_START_PENDING, NO_ERROR, 4000); // Вызываем функцию, которая выполняет все // необходимые инициализирующие действия // ServiceStart(argc, argv); // После завершения инициализации устанавливаем // состояние работающего сервиса ReportStatus(SERVICE_RUNNING, NOERROR, 0); return; } // ----------------------------------------------------- // Функция ServiceControl // Точка входа функции обработки команд // ----------------------------------------------------- void WINAPI ServiceControl(DWORD dwControlCode) { // Анализируем код команды и выполняем эту команду switch(dwControlCode) { // Команда остановки сервиса case SERVICE_CONTROL_STOP: { // Устанавливаем состояние ожидания остановки ss.dwCurrentState = SERVICE_STOP_PENDING; ReportStatus(ss.dwCurrentState, NOERROR, 0); // Выполняем остановку сервиса, вызывая функцию, // которая выполняет все необходимые для этого действия // ServiceStop(); // Отмечаем состояние как остановленный сервис ReportStatus(SERVICE_STOPPED, NOERROR, 0); break; } // Определение текущего состояния сервиса case SERVICE_CONTROL_INTERROGATE: { // Возвращаем текущее состояние сервиса ReportStatus(ss.dwCurrentState, NOERROR, 0); break; } // В ответ на другие команды просто возвращаем // текущее состояние сервиса default: { ReportStatus(ss.dwCurrentState, NOERROR, 0); break; } } } // ----------------------------------------------------- // Функция ReportStatus // Посылка состояния сервиса системе управления сервисами // ----------------------------------------------------- void ReportStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint) { // Счетчик шагов длительных операций static DWORD dwCheckPoint = 1; // Если сервис не находится в процессе запуска, // его можно остановить if(dwCurrentState == SERVICE_START_PENDING) ss.dwControlsAccepted = 0; else ss.dwControlsAccepted = SERVICE_ACCEPT_STOP; // Сохраняем состояние, переданное через // параметры функции ss.dwCurrentState = dwCurrentState; ss.dwWin32ExitCode = dwWin32ExitCode; ss.dwWaitHint = dwWaitHint; // Если сервис не работает и не остановлен, // увеличиваем значение счетчика шагов // длительных операций if((dwCurrentState == SERVICE_RUNNING) || (dwCurrentState == SERVICE_STOPPED)) ss.dwCheckPoint = 0; else ss.dwCheckPoint = dwCheckPoint++; // Вызываем функцию установки состояния SetServiceStatus(ssHandle, &ss); } В файле small.h (листинг 5.2) определено имя сервиса MYServiceName и прототипы функций. Листинг 5.2. Файл service/small/small.h #define MYServiceName "Sample of simple service" void WINAPI ServiceMain(DWORD dwArgc, LPSTR *lpszArv); void WINAPI ServiceControl(DWORD dwControlCode); void ReportStatus(DWORD dwCurrentState, DWORD dwWin32ExitCode, DWORD dwWaitHint); Исходные тексты приложения SRVCTRLГлавный файл исходных текстов приложения SRVCTRL, предназначенного для управления сервисом Sample of simple service приведен в листинге 5.3. Листинг 5.3. Файл service/srvctrl.c // ================================================== // Приложение SRVCTRL // Работа с сервисом "Sample of simple service" // // (С) Фролов А.В., 1996 // Email: frolov@glas.apc.org // ================================================== #define STRICT #include <windows.h> #include <windowsx.h> #include "resource.h" #include "afxres.h" #include "srvctrl.h" HINSTANCE hInst; char szAppName[] = "ServiceCtlApp"; char szAppTitle[] = "Simple Service Control"; // Состояние сервиса SERVICE_STATUS ss; // ----------------------------------------------------- // Функция WinMain // ----------------------------------------------------- int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { WNDCLASSEX wc; HWND hWnd; MSG msg; // Сохраняем идентификатор приложения hInst = hInstance; // Преверяем, не было ли это приложение запущено ранее hWnd = FindWindow(szAppName, NULL); if(hWnd) { // Если было, выдвигаем окно приложения на // передний план if(IsIconic(hWnd)) ShowWindow(hWnd, SW_RESTORE); SetForegroundWindow(hWnd); return FALSE; } // Регистрируем класс окна memset(&wc, 0, sizeof(wc)); wc.cbSize = sizeof(WNDCLASSEX); wc.hIconSm = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICONSM), IMAGE_ICON, 16, 16, 0); wc.style = 0; wc.lpfnWndProc = (WNDPROC)WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInst; wc.hIcon = LoadImage(hInst, MAKEINTRESOURCE(IDI_APPICON), IMAGE_ICON, 32, 32, 0); wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hbrBackground = (HBRUSH) (COLOR_WINDOW + 1); wc.lpszMenuName = MAKEINTRESOURCE(IDR_APPMENU); wc.lpszClassName = szAppName; if(!RegisterClassEx(&wc)) if(!RegisterClass((LPWNDCLASS)&wc.style)) return FALSE; // Создаем главное окно приложения hWnd = CreateWindow(szAppName, szAppTitle, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL); if(!hWnd) return(FALSE); // Отображаем окно и запускаем цикл // обработки сообщений ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); while(GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } // ----------------------------------------------------- // Функция WndProc // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { HANDLE_MSG(hWnd, WM_COMMAND, WndProc_OnCommand); HANDLE_MSG(hWnd, WM_DESTROY, WndProc_OnDestroy); default: return(DefWindowProc(hWnd, msg, wParam, lParam)); } } // ----------------------------------------------------- // Функция WndProc_OnDestroy // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnDestroy(HWND hWnd) { PostQuitMessage(0); return 0L; } // ----------------------------------------------------- // Функция WndProc_OnCommand // ----------------------------------------------------- #pragma warning(disable: 4098) void WndProc_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify) { // Идентификатор сервиса SC_HANDLE schService; // Идентификатор системы управления сервисами SC_HANDLE schSCManager; LPQUERY_SERVICE_CONFIG lpBuf; DWORD dwBytesNeeded; char szBuf[1024]; switch (id) { case ID_FILE_EXIT: { // Завершаем работу приложения PostQuitMessage(0); return 0L; break; } // Установка сервиса в систему case ID_SERVICE_INSTALL: { // Открываем систему управления сервисами schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if(!schSCManager) break; // Создаем сервис с именем MYServiceName schService = CreateService( schSCManager, MYServiceName, MYServiceName, SERVICE_ALL_ACCESS, SERVICE_WIN32_OWN_PROCESS, SERVICE_DEMAND_START, SERVICE_ERROR_NORMAL, "c:\\ntbk2\\src\\service\\small\\debug\\small.exe", NULL, NULL, "", NULL, NULL); // Закрываем идентификатор системы управления // сервисами CloseServiceHandle(schSCManager); break; } // Удаление сервиса из системы case ID_SERVICE_REMOVE: { // Открываем систему управления сервисами schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if(!schSCManager) break; // Открываем сервис с именем MYServiceName schService = OpenService( schSCManager, MYServiceName, SERVICE_ALL_ACCESS); if(!schService) break; // Останавливаем сервис ControlService(schService, SERVICE_CONTROL_STOP, &ss); // Вызываем функцию удаления сервиса из системы DeleteService(schService); // Закрываем идентификатор системы управления // сервисами CloseServiceHandle(schSCManager); break; } case ID_SERVICE_START: { // Открываем систему управления сервисами schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if(!schSCManager) break; // Открываем сервис с именем MYServiceName schService = OpenService( schSCManager, MYServiceName, SERVICE_ALL_ACCESS); if(!schService) break; // Запускаем сервис StartService(schService, 0, NULL); // Закрываем идентификатор системы управления // сервисами CloseServiceHandle(schSCManager); break; } case ID_SERVICE_STOP: { // Открываем систему управления сервисами schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if(!schSCManager) break; // Открываем сервис с именем MYServiceName schService = OpenService( schSCManager, MYServiceName, SERVICE_ALL_ACCESS); // Останавливаем сервис ControlService(schService, SERVICE_CONTROL_STOP, &ss); // Закрываем идентификатор системы управления // сервисами CloseServiceHandle(schSCManager); break; } case ID_SERVICE_GETCONFIGURATION: { // Открываем систему управления сервисами schSCManager = OpenSCManager(NULL, NULL, SC_MANAGER_ALL_ACCESS); if(!schSCManager) break; // Открываем сервис с именем MYServiceName schService = OpenService( schSCManager, MYServiceName, SERVICE_ALL_ACCESS); if(!schService) break; // Получаем буфер для сохранения конфигурации lpBuf = (LPQUERY_SERVICE_CONFIG)malloc(4096); if(lpBuf != NULL) { // Сохраняем конфигурацию в буфере QueryServiceConfig(schService, lpBuf, 4096, &dwBytesNeeded); // Отображаем некоторые поля конфигурации wsprintf(szBuf, "Binary path: %s\n" "Start Name: %s\n" "Display Name: %s\n", lpBuf->lpBinaryPathName, lpBuf->lpServiceStartName, lpBuf->lpDisplayName); MessageBox(hWnd, szBuf, szAppTitle, MB_OK | MB_ICONINFORMATION); // Освобождаем буфер free(lpBuf); } // Закрываем идентификатор системы управления // сервисами CloseServiceHandle(schSCManager); break; } case ID_HELP_ABOUT: { MessageBox(hWnd, "Simple Service Control\n" "(C) Alexandr Frolov, 1996\n" "Email: frolov@glas.apc.org", szAppTitle, MB_OK | MB_ICONINFORMATION); return 0L; break; } default: break; } return FORWARD_WM_COMMAND(hWnd, id, hwndCtl, codeNotify, DefWindowProc); } В файле srvctrl.h (листинг 5.4) определено имя сервиса и прототипы функций. Листинг 5.4. Файл service/srvctrl.h // Имя сервиса #define MYServiceName "Sample of simple service" // ----------------------------------------------------- // Описание функций // ----------------------------------------------------- LRESULT WINAPI WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); void WndProc_OnCommand(HWND hWnd, int id, HWND hwndCtl, UINT codeNotify); void WndProc_OnDestroy(HWND hWnd); LRESULT WINAPI DlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam); BOOL DlgProc_OnInitDialog(HWND hdlg, HWND hwndFocus, LPARAM lParam); void DlgProc_OnCommand(HWND hdlg, int id, HWND hwndCtl, UINT codeNotify); Файл resource.h (листинг 5.5) содержит описания констант, которые используются в файле определения ресурсов приложения. Листинг 5.5. Файл service/resource.h //{{NO_DEPENDENCIES}} // Microsoft Developer Studio generated include file. // Used by srvctrl.rc // #define IDR_MENU1 101 #define IDR_APPMENU 101 #define IDI_APPICON 102 #define IDI_APPICONSM 103 #define ID_FILE_EXIT 40001 #define ID_HELP_ABOUT 40002 #define ID_SERVICE_INSTALL 40010 #define ID_SERVICE_REMOVE 40011 #define ID_SERVICE_START 40012 #define ID_SERVICE_STOP 40013 #define ID_SERVICE_GETCONFIGURATION 40014 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NEXT_RESOURCE_VALUE 105 #define _APS_NEXT_COMMAND_VALUE 40015 #define _APS_NEXT_CONTROL_VALUE 1006 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif Файл определения ресурсов приложения приведен в листинге 5.6. Листинг 5.6. Файл service/srvctrl.rc //Microsoft Developer Studio generated resource script. // #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include "afxres.h" ////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS ////////////////////////////////////////////////////////////// // Russian resources #if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_RUS) #ifdef _WIN32 LANGUAGE LANG_RUSSIAN, SUBLANG_DEFAULT #pragma code_page(1251) #endif //_WIN32 ////////////////////////////////////////////////////////////// // // Menu // IDR_APPMENU MENU DISCARDABLE BEGIN POPUP "&File" BEGIN MENUITEM "E&xit", ID_FILE_EXIT END POPUP "&Service" BEGIN MENUITEM "&Install", ID_SERVICE_INSTALL MENUITEM "&Remove", ID_SERVICE_REMOVE MENUITEM SEPARATOR MENUITEM "&Start", ID_SERVICE_START MENUITEM "Sto&p", ID_SERVICE_STOP MENUITEM SEPARATOR MENUITEM "&Get configuration", ID_SERVICE_GETCONFIGURATION END POPUP "&Help" BEGIN MENUITEM "&About...", ID_HELP_ABOUT END END #ifdef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include ""afxres.h""\r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END #endif // APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Icon // // Icon with lowest ID value placed first to ensure // application icon // remains consistent on all systems. IDI_APPICON ICON DISCARDABLE "srvctrl.ico" IDI_APPICONSM ICON DISCARDABLE "srvctrsm.ico" #endif // Russian resources ////////////////////////////////////////////////////////////// #ifndef APSTUDIO_INVOKED ////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED |