Service

来源:互联网 发布:广东省干部网络培训 编辑:程序博客网 时间:2024/06/10 03:47
program SimpleServ;{$APPTYPE CONSOLE}uses Windows, WinSVC; //Не используем SysUtils и прочий хлам - размер екзешника у нас будет всего ~25 килобайт  Function IsWin2KorHigher: BOOL;  Var aVer: OSVERSIONINFO;  Begin    ZeroMemory(@aVer, SizeOf(aVer));    aVer.dwOSVersionInfoSize := SizeOf(aVer);    GetVersionEx(aVer);    Result := (aVer.dwPlatformId = VER_PLATFORM_WIN32_NT) And (aVer.dwMajorVersion >= 5);  End;Const  ServicesCount = 1;  ServiceName = 'SimpleServ';  DisplayName = 'Simple Service';  ServiceType = SERVICE_WIN32_OWN_PROCESS;  ServiceDescription = 'SimpleServ from Handlex ;-))) This simple program will help you to understand WinNT Services. Mail to alex_wh@mail.ru.';Var  ServThrHndl: THandle = 0;  StopEvent: THandle = 0;  aServHndl: DWord = 0;  aServStatus: SERVICE_STATUS;  Function IntToStr(Value: Integer): String; //Included because we don't use SysUtils  Var aSign: Bool;  Begin    Result := '';    aSign := Value >= 0;    If Not aSign Then Value := -Value;    Repeat      Result := Char(Value - (Value Div 10) * 10 + Byte('0')) + Result;      Value := Value Div 10;    Until Value = 0;    If Not aSign Then Result := '-' + Result;  End;  // Helper function for windows error strings  function SysErrorMessage(ErrorCode: Integer): string; //Included because we don't use SysUtils  var    Len: Integer;    Buffer: array[0..255] of Char;  begin    Len := FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM or      FORMAT_MESSAGE_ARGUMENT_ARRAY, nil, ErrorCode, 0, Buffer,      SizeOf(Buffer), nil);    while (Len > 0) and (Buffer[Len - 1] in [#0..#32]) do Dec(Len);    SetString(Result, Buffer, Len);    UniqueString(Result);    ANSItoOEM(PChar(Result), PChar(Result));    If Result <> '' Then      Result := '(' + IntToStr(ErrorCode) + ') ' + Result;  end;  Procedure ShowInfo;  Begin    WriteLn;    WriteLn('                    -=* SIMPLE TRAINING SERVICE BY HandleX *=-');  End;  Procedure ProcessStartupParams; //Реакция на install, uninstall    Function SetServiceDescription(aSHndl: THandle; aDesc: String): Bool; //Устанавливает "описание" для службы, Win2k и выше    Const SERVICE_CONFIG_DESCRIPTION: DWord = 1;    Var      DynChangeServiceConfig2: Function(        hService: SC_HANDLE;                    // handle to service        dwInfoLevel: DWORD;                     // information level        lpInfo: Pointer): Bool; StdCall;        // new data      aLibHndl: THandle;      TempP: PChar;    Begin      aLibHndl := GetModuleHandle(advapi32);      Result := aLibHndl <> 0; If Not Result Then Exit;      DynChangeServiceConfig2 := GetProcAddress(aLibHndl, 'ChangeServiceConfig2A');      Result := @DynChangeServiceConfig2 <> Nil; If Not Result Then Exit;      TempP := PChar(aDesc); //ChangeServiceConfig2 хочет вместо строки указатель на указатель ;-)      Result := DynChangeServiceConfig2(aSHndl, SERVICE_CONFIG_DESCRIPTION, @TempP);    End;  Type    TToDo = (tdError, tdInstall, tdUninstall);    TToDo_s = Set of TToDo;  Const    ParamStrings: Array[tdInstall..tdUninstall] of String = ('install', 'uninstall');  Function MapParam(aParam: String): TToDo; //Узнаёт из параметра о вашем желании ;-)  Var    J: TToDo;    TempStr: String;  Begin    Result := tdError;    TempStr := aParam;    If TempStr[1] In ['/', '-'] Then      TempStr := Copy(TempStr, 2, Length(TempStr) - 1);    UniqueString(TempStr);      CharLower(PChar(TempStr));    For J := Low(ParamStrings) to High(ParamStrings) Do      If ParamStrings[J] = TempStr Then      Begin        Result := J;        Exit;      End;  End;  Var    J: Integer;    scHndl, sHndl: THandle;    aStatus: TServiceStatus;    toDo: TTodo_s;  Begin    toDo := [];    For J := 1 to ParamCount Do    Begin      Include(ToDo, MapParam(ParamStr(J)));      If tdError in toDo Then      Begin        ExitCode := ERROR_INVALID_PARAMETER;        WriteLn('Unknown parameter - ' + ParamStr(J) + '. RTFM, please...');        Exit;      End;    End;    If [tdInstall, tdUninstall] <= toDo Then    Begin      ExitCode := ERROR_INVALID_PARAMETER;      WriteLn('Error: you can not install and uninstall service simultaniosly. Check params.');      Exit;    End;    If tdInstall in toDo Then //Устанавливаем сервис    Begin      Write('Connecting Service Control Manager...');      scHndl := OpenSCManager(Nil, Nil, SC_MANAGER_CREATE_SERVICE);      If scHndl = 0 Then      Begin        ExitCode := GetLastError;        WriteLn('Failed!'); WriteLn('Error: ', SysErrorMessage(ExitCode));        Exit;      End;      Try        WriteLn('Ok');        Write('Creating service database record...');        sHndl := CreateService(          SCHndl, ServiceName, DisplayName,          SERVICE_QUERY_CONFIG Or SERVICE_CHANGE_CONFIG, ServiceType, SERVICE_DEMAND_START,          SERVICE_ERROR_NORMAL, PChar(ParamStr(0)), Nil, Nil, Nil, Nil, Nil);        If sHndl = 0 Then        Begin          ExitCode := GetLastError;          WriteLn('Failed!'); WriteLn('Error: ', SysErrorMessage(ExitCode));          Exit;        End;        Try          WriteLn('Ok');          If ServiceDescription <> '' Then          Begin            Write('Setting service description...');            If Not SetServiceDescription(sHndl, ServiceDescription) Then            Begin              WriteLn('Failed!');              WriteLn('Warning: ', SysErrorMessage(GetLastError));              WriteLn('Warning: SetServiceDesc() failed, but service is installed!');            End;            WriteLn('Ok');          End;        Finally          CloseServiceHandle(sHndl);        End;      Finally        CloseServiceHandle(SCHndl);      End;      WriteLn('Service "', DisplayName, '" install success.');    End;    If tdUninstall in toDo Then //Удаляем сервис...    Begin      Write('Connecting Service Control Manager...');      scHndl := OpenSCManager(Nil, Nil, GENERIC_EXECUTE);      If scHndl = 0 Then      Begin        ExitCode := GetLastError;        WriteLn('Failed!');        WriteLn('Error: ', SysErrorMessage(ExitCode));        Exit;      End;      Try        WriteLn('Ok');        Write('Opening and Quering Service...');        sHndl := OpenService(SCHndl, ServiceName, STANDARD_RIGHTS_REQUIRED Or SERVICE_QUERY_STATUS Or SERVICE_STOP);        If sHndl = 0 Then        Begin          ExitCode := GetLastError;          WriteLn('Failed!'); WriteLn('Error: ', SysErrorMessage(ExitCode));          Exit;        End;        Try          If Not QueryServiceStatus(sHndl, aStatus) Then          Begin            ExitCode := GetLastError;            WriteLn('Failed!'); WriteLn('Error: ', SysErrorMessage(ExitCode));            Exit;          End;          WriteLn('Ok');          If aStatus.dwCurrentState <> SERVICE_STOPPED Then          Begin            Write('Service is running, wait until stopped...');            If Not ControlService(sHndl, SERVICE_CONTROL_STOP, aStatus) Then            Begin              ExitCode := GetLastError;              WriteLn('Failed!'); WriteLn('Error: ', SysErrorMessage(ExitCode));              Exit;            End;            While aStatus.dwCurrentState <> SERVICE_STOPPED Do            Begin              Sleep(250); Write('.');              If Not QueryServiceStatus(sHndl, aStatus) Then              Begin                ExitCode := GetLastError;                WriteLn('Failed!'); WriteLn('Error: ', SysErrorMessage(ExitCode));                Exit;              End;            End;            WriteLn('Stopped');          End;          Write('Deleting Service...');          If Not DeleteService(sHndl) Then          Begin            ExitCode := GetLastError;            WriteLn('Failed!'); WriteLn('Error: ', SysErrorMessage(ExitCode));            Exit;          End;          WriteLn('Ok');        Finally          CloseServiceHandle(sHndl);        End;      Finally        CloseServiceHandle(SCHndl);      End;      WriteLn('Service uninstall success.');    End;  End;    Function SetState(aState: DWORD): DWORD;    Begin      aServStatus.dwCurrentState := aState;      If aServHndl <> 0 Then        SetServiceStatus(aServHndl, aServStatus);      Result := aServStatus.dwCurrentState;    End;    Procedure ServiceHandler(fdwControl: DWORD); StdCall;    Begin      Case fdwControl Of        SERVICE_CONTROL_STOP: Begin           //Requests the service to stop.          SetState(SERVICE_STOP_PENDING);          SetEvent(StopEvent);          ResumeThread(ServThrHndl); //Если сервис был в паузе, то рабочий поток надо возобновить        End;        SERVICE_CONTROL_PAUSE: Begin          //Requests the service to pause.          SetState(SERVICE_PAUSE_PENDING);          SuspendThread(ServThrHndl); //Останавливаем рабочий поток сервиса          SetState(SERVICE_PAUSED);        End;        SERVICE_CONTROL_CONTINUE: Begin       //Requests the paused service to resume.          SetState(SERVICE_CONTINUE_PENDING);          ResumeThread(ServThrHndl); //Восстанавливаем рабочий поток сервиса          SetState(SERVICE_RUNNING);        End;        SERVICE_CONTROL_INTERROGATE: Begin    //Requests the service to update immediately its current status information to the service control manager.          SetState(aServStatus.dwCurrentState); //Говорим SCM о том, в каком состоянии находится наша служба        End;        128..255: Begin                       //The service defines the action associated with the control code.          SuspendThread(ServThrHndl);         //Протяжно пищим в спикер, потому что кто-то послал USER DEFINED CONTROL CODE ;-)          Windows.Beep(1000, 500);          ResumeThread(ServThrHndl);        End;      End;    End;  Procedure MainServiceProc(      // Каждая служба может иметь параметры своего запуска. У нас не используется ;-)    dwArgc: DWORD;                // number of arguments    lpszArgv: Pointer); StdCall   // array of arguments  Begin    aServHndl := RegisterServiceCtrlHandler(ServiceName, @ServiceHandler);    If aServHndl = 0 Then    Begin      ExitCode := GetLastError;      Exit; //Какая-то ошибка, срочно выходим, SCM будет ругаться, но сообщить мы ему ничего не можем...    End;    ZeroMemory(@aServStatus, SizeOf(aServStatus));    aServStatus.dwServiceType := ServiceType;    aServStatus.dwControlsAccepted := SERVICE_ACCEPT_STOP Or SERVICE_ACCEPT_PAUSE_CONTINUE;    //aServStatus.dwWaitHint := 500; //Здесь может быть подсказка для небыстрых служб о том, как долго она реагирует на команды    SetState(SERVICE_START_PENDING); //Извещаем SCM, что мы начали стартовать именно этот сервис...    // Тут идёт рутина инициализации...    If Not DuplicateHandle(GetCurrentProcess, GetCurrentThread, GetCurrentProcess, @ServThrHndl, 0, FALSE, DUPLICATE_SAME_ACCESS) Then    Begin //Нам нужен реальный дескриптор потока службы, делаем его...      aServStatus.dwWin32ExitCode := GetLastError;      SetState(SERVICE_STOPPED);      Exit;    End;    //Нам нужен unnamed event для реагирования на останов из ControlHandler...    StopEvent := CreateEvent(Nil, True, False, Nil);    If StopEvent = 0 Then //Какая-то ошибка - срочно останавливаемся и выходим...    Begin      aServStatus.dwWin32ExitCode := GetLastError;      SetState(SERVICE_STOPPED);      Exit;    End;    // Инит прошёл, пошла работа сервиса...    SetState(SERVICE_RUNNING); //Момент истины - извещаем SCM что мы работаем!!!    While WaitForSingleObject(StopEvent, 500) = WAIT_TIMEOUT Do      Windows.Beep(10000, 10); //Крутим цикл, бипаем по таймауту, иначе выходим...    // Выполняем остановку сервиса - вычищаемся и выходим...    CloseHandle(ServThrHndl);    ServThrHndl := 0;    CloseHandle(StopEvent);    StopEvent := 0;    SetState(SERVICE_STOPPED); // Работа сервиса закончена...  End;Var  ServTableEntryArray: Array[0..ServicesCount] Of TServiceTableEntryA;begin{$R *.res}  //Старт программы...  If ParamCount > 0 Then //От нас что-то хотят...  Begin    ShowInfo;    ProcessStartupParams; //Выясняем что и выходим...    Exit;  End;  //Готовимся к вызову StartServiceCtrlDispatcher  ZeroMemory(@ServTableEntryArray, SizeOf(ServTableEntryArray));  ServTableEntryArray[0].lpServiceName := ServiceName;  ServTableEntryArray[0].lpServiceProc := @MainServiceProc;  If Not StartServiceCtrlDispatcher(ServTableEntryArray[0]) Then  Begin    ExitCode := GetLastError;    ShowInfo; //Какой-то косяк, поэтому выводим в консоль сообщение и выходим...    WriteLn('Error: ', SysErrorMessage(ExitCode));    WriteLn('This program is Windows NT Service, so it CAN NOT be run from command prompt.');    WriteLn('You can install it with "/install" parameter.');    Exit;  End;  // Все сервисы завершили свою работу, выходим...end.
原创粉丝点击