Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Программирование для 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

[Назад] [Содеожание] [Дальше]