;********************************************************************************
;Titre      : AbleTaskKill.pb
;Auteur     : Pierre Bellisle - Modifié par CAGE - Recompilé avec PB 5.70 LTS
;Date       : 2019/03/10
;Version PB : PureBasic 5.70 LTS (Windows - x86)
;Version PB : PureBasic 5.70 LTS (Windows - x64)
;Compiler Options
;Compiler Options : Executable format: Console
;Compiler Options : Create threadsafe executable
;Compiler Options : Enable moderm theme support (for Windows X and above)
;Compiler Options : Request Administrator mode for Windows Vista and above
;Compile/Run      : Create temporary executable in the source directory
;
;Libairies: aucunes
;
; La Procedure.l RunAsSystemAdmin(sExeName.s) n'est pas utilisé dans cet exemple
;
; La Procedure serviceThread(idThread.l) est modifiée pour mes actions a réaliser
;
;********************************************************************************

; Source : http://pages.videotron.com/lyra/PowerBASIC/?FrameCode=ServiceGUI

; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
; * A Windows service that can launch a GUI application in System Administrator mode.                       *
; *                                                                                                         *
; * Compiler used: PureBasic 5.42 32/64 - Tested under Windows 7 and Windows 10                             *
; *                                                                                                         *
; * The exe produced is a service named "Able.exe" that react to those parameters:                          *
; *  /Install   ; To install the service so Windows is aware of it.                                         *
; *  /Uninstall ; To uninstall the service. It must have been stopped before.                               *
; *  /Status    ; Call Service Manager to get the current service state.                                    *
; *  /Help      ; Show these command line switch                                                            *
; *  /?         ; Show these command line switch                                                            *
; *                                                                                                         *
; * To install, start a command window as Administrator and type "Able /Install".                           *
; * You can Start/Stop/Pause/Continue the service via the Service Manager,                                  *
; * to start it click Start button, choose Execute and type "Services.msc".                                 *
; * You can also act at the command prompt:                                                                 *
; *  "Net start Able"                                                                                       *
; *  "Net stop Able"                                                                                        *
; *  "Net pause Able"                                                                                       *
; *  "Net continue Able"                                                                                    *
; *                                                                                                         *
; * If you set #Debug to TRUE in code you will need a DebugView utility                                     *
; * running in "Global capture" mode started as Administrator.                                              *
; * This is great to see what the service is doing and for debugging.                                       *
; * You can download one from Microsoft or CobaltFusion:                                                    *
; *  https://docs.microsoft.com/en-us/sysinternals/downloads/debugview                                      *
; *  https://github.com/CobaltFusion/DebugViewPP/releases                                                   *
; * See in code for #DelayedStart, #RunAsSystemAdmin, and #AutoStop options.                                *
; *                                                                                                         *
; * Once started, with #RunAsSystemAdmin = #True,                                                           *
; * the service, bypassing UAC, will start RegEdit.exe in the System Administrator mode,                    *
; * this is more powerfull than the Administrator mode, be carefull of what you do.                         *
; * Of course, you may alter the code to start some other applications.                                     *
; *                                                                                                         *
; * In Regedit, as System Admin, you will see subkeys like                                                  *
; * HKLM\SECURITY\Sam\* (Cache-Policy-Recovery-RXACT-SAM), you won't see this in normal Administrator mode. *
; *                                                                                                         *
; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *

; Normal folder for Windows service is System32, I do not care about this in the current demo.
; In memory problem, try to start Task Manager / Process (Check All users)  /  Right click "Able" and stop the process.

EnableExplicit
;#PB_Compiler_Processor        = #PB_Processor_x64
#PB_Compiler_ExecutableFormat = #PB_Compiler_Console
;#PB_Compiler_Filename        = "AbleTaskKill.exe"

;************************************************************************************************************************************************
#Version          = "2018-09-05 17:00:00" ; Recompilé avec PB 5.70 LTS le 10/02/2019                                                            *
#AppName          = "AbleTaskKill" ;Dictionary: able - �^bl, adjective having enough strength, power or means (To do a thing).                  *
#Debug            = #False ;#False #True  - See service called procedure and progress in a DebugView utility, must be in CAPTURE GLOBAL mode.   *
#DelayedStart     = #False ;#False #True  - To boot faster Windows will start the service after boot up is completely finished.                 *
;#RunAsSystemAdmin = #True  ;#False #True  - Start the GUI application RegEdit.exe with the powerfull SystemAdmin privilege.                     *
#AutoStop         = #False ;#False #True  - Service stop by itself after RunAsSystemAdmin                                                       *
;************************************************************************************************************************************************

Declare   WinMain()
Declare   serviceInstall()
Declare   serviceUninstall()
Declare.l serviceQueryServiceStatus(sServer.s, sService.s)
Declare.s serviceStringType(dwServiceType.l)
Declare.s serviceStringStatus(dwServiceStatus.l)
Declare.s serviceStringControlIsAccepted(dwControlAccepted.l)
Declare.s serviceStringControl(dwServiceControl.l)
Declare   serviceMain(dwArgs.l, lpszArgv.i)
Declare   serviceThreadCreate()
Declare   serviceHandler(ControlValue.l)
Declare   serviceThread(idThread.l)
Declare   serviceSetServiceStatus(CurrentStatus.l, ExitCode.l, SpecificExitCode.l, Checkpoint.l, WaitHint.l)
Declare   servicePause()
Declare   serviceResume()
Declare   serviceStop()
Declare   serviceStopRaw()
Declare   serviceTerminate(ErrorCode.l)
Declare.l RunAsSystemAdmin(sExeName.s)
Declare   StdOut(Text.s)

#SERVICES_ACTIVE_DATABASE              = "ServicesActive"
#MAX_SERVICE_NAME_LEN                  = 128
#SERVICE_ADAPTER                       = $4
#SERVICE_RECOGNIZER_DRIVER             = $8
#SERVICE_USER_OWN_PROCESS              = $50
#SERVICE_USER_SHARE_PROCESS            = $60
#SERVICE_INTERACTIVE_PROCESS           = $100
#SERVICE_CONTROL_PARAMCHANGE           = $06
#SERVICE_CONTROL_NETBINDADD            = $07
#SERVICE_CONTROL_NETBINDREMOVE         = $08
#SERVICE_CONTROL_NETBINDENABLE         = $09
#SERVICE_CONTROL_NETBINDDISABLE        = $0A
#SERVICE_CONTROL_DEVICEEVENT           = $0B
#SERVICE_CONTROL_HARDWAREPROFILECHANGE = $0C
#SERVICE_CONTROL_POWEREVENT            = $0D
#SERVICE_CONTROL_SESSIONCHANGE         = $0E
#SERVICE_CONTROL_PRESHUTDOWN           = $0F
#SERVICE_CONTROL_TIMECHANGE            = $10
#SERVICE_CONTROL_TRIGGEREVENT          = $20
#SERVICE_CONTROL_USERMODEREBOOT        = $40

#TokenPrimary                          = 1
#NameSamCompatible                     = 2

Structure SERVICE_DELAYED_AUTO_START_INFO
  fDelayedAutostart.l  ;Delayed autostart flag
EndStructure

Structure GlobalType ;All usefull variables in one place
  zComputerName.s{#MAX_COMPUTERNAME_LENGTH + 1}
  zServiceName.s{#MAX_SERVICE_NAME_LEN}
  zServiceDisplayName.s{#MAX_SERVICE_NAME_LEN}
  zExeName.s{#MAX_PATH}
  hServiceStatus.i
  hInstance.i
  hEvent.i
  hThread.i
  CurrentServiceStatus.l
  ServiceIsRunning.l
  ServiceIsPaused.l
EndStructure

Import "Kernel32.lib"
  WTSGetActiveConsoleSessionId()
  ProcessIdToSessionId(ProcessEntry_th32ProcessID.l, ProcessSessionId.l)
EndImport

Global *pg.GlobalType ;A single global pointer to a structure of many needed variables

WinMain() ;Starting point
;_____________________________________________________________________________

Procedure WinMain()
 Protected g.GlobalType

 If #Debug : OutputDebugString_("WinMain") : EndIf

 g\zServiceName        = #AppName ;Set the service name and display name
 g\zServiceDisplayName = #AppName + " service" ;Viewed in service manager, 256 char
 g\hInstance           = GetModuleHandle_(0)

 Protected ComputerNameLen.l = SizeOf(g\zComputerName)
 GetComputerName_(@g\zComputerName, @ComputerNameLen) ;Use "" for default local service

 *pg = @g ;Set the GlobalType variable pointer

 Protected sCmdLine.s
 sCmdLine.s = LCase(ProgramParameter(0))
 If FindString(sCmdLine, "/uninstall")
   serviceUninstall()
 ElseIf FindString(sCmdLine, "/install")
   serviceInstall()
 ElseIf FindString(sCmdLine, "/status")
   serviceQueryServiceStatus(*pg\zComputerName, *pg\zServiceName)
 ElseIf FindString(sCmdLine, "/help") | FindString(sCmdLine, "/?")
   StdOut(#AppName)
   StdOut(" /install")
   StdOut(" /uninstall")
   StdOut(" /status")
   StdOut(" /help")
   StdOut(" /?")
   StdOut(" Net start    " + #AppName)
   StdOut(" Net stop     " + #AppName)
   StdOut(" Net pause    " + #AppName)
   StdOut(" Net continue " + #AppName)
   StdOut(" Services.msc (For Service Manager)")
 ElseIf Len(sCmdLine) ;Unknown command
   StdOut(" Unknown command! (Try /? for help.)")
 Else ;Command line is empty,
   Dim ServiceTable.SERVICE_TABLE_ENTRY(1) ;Last entry must be blank
   ServiceTable(0)\lpServiceName = @g\zServiceName
   ServiceTable(0)\lpServiceProc = @ServiceMain()

   If #Debug : OutputDebugString_("WinMain:StartServiceCtrlDispatcher") : EndIf
   ;StartServiceCtrlDispatcher_() connects the main thread of a service process to the service control manager,
   ;which causes the thread to be the service control dispatcher thread for the calling process.
   ;When Services.msc starts a service, it waits up to 30 sec for the service process to call StartServiceCtrlDispatcher,
   ;and does not return until all running services in the process have entered the SERVICE_STOPPED
   If StartServiceCtrlDispatcher_(@ServiceTable(0))
     ;Service was started pointing to ServiceMain() and following code will be executed after SERVICE_STOPPED
     If #Debug : OutputDebugString_("WinMain:StartServiceCtrlDispatcher done : Stopped_OK") : EndIf
   Else
     Protected LastError.l
     LastError = GetLastError_() ;Might br ERROR_FAILED_SERVICE_CONTROLLER_CONNECT - ERROR_INVALID_DATA - ERROR_SERVICE_ALREADY_RUNNING
     If #Debug : OutputDebugString_("WinMain:StartServiceCtrlDispatcher not done : Error " + Str(LastError)) : EndIf
     ExitProcess_(LastError)
   EndIf

 EndIf
EndProcedure
;_____________________________________________________________________________

Procedure.s serviceStringControlIsAccepted(dwControlAccepted.l)
  Protected sCtrlAccept.s

  If dwControlAccepted & #SERVICE_ACCEPT_STOP           : sCtrlAccept + "SERVICE_ACCEPT_STOP, "           : EndIf ;0x001
  If dwControlAccepted & #SERVICE_ACCEPT_PAUSE_CONTINUE : sCtrlAccept + "SERVICE_ACCEPT_PAUSE_CONTINUE, " : EndIf ;0x002
  If dwControlAccepted & #SERVICE_ACCEPT_SHUTDOWN       : sCtrlAccept + "SERVICE_ACCEPT_SHUTDOWN, "       : EndIf ;0x004
  If dwControlAccepted & #SERVICE_ACCEPT_PARAMCHANGE    : sCtrlAccept + "SERVICE_ACCEPT_PARAMCHANGE, "    : EndIf ;0x008
  If dwControlAccepted & #SERVICE_ACCEPT_NETBINDCHANGE  : sCtrlAccept + "SERVICE_ACCEPT_NETBINDCHANGE, "  : EndIf ;0x010
  If dwControlAccepted & #SERVICE_ACCEPT_PRESHUTDOWN    : sCtrlAccept + "SERVICE_ACCEPT_PRESHUTDOWN, "    : EndIf ;0x100
  If dwControlAccepted = 0                              : sCtrlAccept = "Service not started, "           : EndIf ;0x000
  If Len(sCtrlAccept)  = 0                              : sCtrlAccept = "SERVICE_ACCEPT_UNKNOWN--"        : EndIf ;
  ProcedureReturn(Left(sCtrlAccept, Len(sCtrlAccept) - 2) + " (0x" + Hex(dwControlAccepted) + ")")

EndProcedure

Procedure.s serviceStringType(dwServiceType.l)
  Protected sServiceType.s

  If dwServiceType & #SERVICE_KERNEL_DRIVER       : sServiceType + "SERVICE_KERNEL_DRIVER, "       : EndIf ;0x001
  If dwServiceType & #SERVICE_FILE_SYSTEM_DRIVER  : sServiceType + "SERVICE_FILE_SYSTEM_DRIVER, "  : EndIf ;0x002
  If dwServiceType & #SERVICE_ADAPTER             : sServiceType + "SERVICE_ADAPTER, "             : EndIf ;0x004
  If dwServiceType & #SERVICE_RECOGNIZER_DRIVER   : sServiceType + "SERVICE_RECOGNIZER_DRIVER, "   : EndIf ;0x008
  If dwServiceType & #SERVICE_WIN32_OWN_PROCESS   : sServiceType + "SERVICE_WIN32_OWN_PROCESS, "   : EndIf ;0x010
  If dwServiceType & #SERVICE_WIN32_SHARE_PROCESS : sServiceType + "SERVICE_WIN32_SHARE_PROCESS, " : EndIf ;0x020
  If dwServiceType & #SERVICE_USER_OWN_PROCESS    : sServiceType + "SERVICE_USER_OWN_PROCESS, "    : EndIf ;0x050
  If dwServiceType & #SERVICE_USER_SHARE_PROCESS  : sServiceType + "SERVICE_USER_SHARE_PROCESS, "  : EndIf ;0x060
  If dwServiceType & #SERVICE_INTERACTIVE_PROCESS : sServiceType + "SERVICE_INTERACTIVE_PROCESS, " : EndIf ;0x100
  If Len(sServiceType) = 0                        : sServiceType = "SERVICE_TYPE_UNKNOWN--"        : EndIf ;
  ProcedureReturn(Left(sServiceType, Len(sServiceType) - 2) + " (0x" + Hex(dwServiceType) + ")")

EndProcedure
;_____________________________________________________________________________

Procedure.s serviceStringStatus(dwServiceStatus.l)
  Protected sServiceStatus.s
  
  Select dwServiceStatus
    Case #SERVICE_STOPPED          : sServiceStatus = "SERVICE_STOPPED"          ;1 The service is not running.
    Case #SERVICE_START_PENDING    : sServiceStatus = "SERVICE_START_PENDING"    ;2 The service is starting.
    Case #SERVICE_STOP_PENDING     : sServiceStatus = "SERVICE_STOP_PENDING"     ;3 The service is stopping.
    Case #SERVICE_RUNNING          : sServiceStatus = "SERVICE_RUNNING"          ;4 The service is running.
    Case #SERVICE_CONTINUE_PENDING : sServiceStatus = "SERVICE_CONTINUE_PENDING" ;5 The service continue is pending.
    Case #SERVICE_PAUSE_PENDING    : sServiceStatus = "SERVICE_PAUSE_PENDING"    ;6 The service pause is pending.
    Case #SERVICE_PAUSED           : sServiceStatus = "SERVICE_PAUSED"           ;7 The service is paused.
    Default                        : sServiceStatus = "SERVICE_STATUS_UNKNOWN"   ;
  EndSelect
  ProcedureReturn(sServiceStatus + " (" + Str(dwServiceStatus) + ")")
  
EndProcedure
;_____________________________________________________________________________

Procedure.s serviceStringControl(dwServiceControl.l)
  Protected sServiceControl.s
  
  Select dwServiceControl
    Case #SERVICE_CONTROL_STOP                  : sServiceControl = "SERVICE_CONTROL_STOP"                  ;0x01 Notifies a service that it should stop.
    Case #SERVICE_CONTROL_PAUSE                 : sServiceControl = "SERVICE_CONTROL_PAUSE"                 ;0x02 Notifies a service that it should pause.
    Case #SERVICE_CONTROL_CONTINUE              : sServiceControl = "SERVICE_CONTROL_CONTINUE"              ;0x03 Notifies a paused service that it should resume.
    Case #SERVICE_CONTROL_INTERROGATE           : sServiceControl = "SERVICE_CONTROL_INTERROGATE"           ;0x04 Notifies a service to report its current status
    Case #SERVICE_CONTROL_SHUTDOWN              : sServiceControl = "SERVICE_CONTROL_SHUTDOWN"              ;0x05 Notifies a service to report its current status
    Case #SERVICE_CONTROL_PARAMCHANGE           : sServiceControl = "SERVICE_CONTROL_PARAMCHANGE"           ;0x06 Notifies a service that service-specific startup parameters have changed.
    Case #SERVICE_CONTROL_NETBINDADD            : sServiceControl = "SERVICE_CONTROL_NETBINDADD"            ;0x07 Notifies a network service that there is a new component for binding.
    Case #SERVICE_CONTROL_NETBINDREMOVE         : sServiceControl = "SERVICE_CONTROL_NETBINDREMOVE"         ;0x08 Notifies a network service that a component for binding has been removed.
    Case #SERVICE_CONTROL_NETBINDENABLE         : sServiceControl = "SERVICE_CONTROL_NETBINDENABLE"         ;0x09 Notifies a network service that a disabled binding has been enabled.
    Case #SERVICE_CONTROL_NETBINDDISABLE        : sServiceControl = "SERVICE_CONTROL_NETBINDDISABLE"        ;0x0A Notifies a network service that one of its bindings has been disabled.
    Case #SERVICE_CONTROL_DEVICEEVENT           : sServiceControl = "SERVICE_CONTROL_DEVICEEVENT"           ;0x0B Notifies a service of device events.
    Case #SERVICE_CONTROL_HARDWAREPROFILECHANGE : sServiceControl = "SERVICE_CONTROL_HARDWAREPROFILECHANGE" ;0x0C Notifies a service that the computer's hardware profile has changed.
    Case #SERVICE_CONTROL_POWEREVENT            : sServiceControl = "SERVICE_CONTROL_POWEREVENT"            ;0x0D Notifies a service of system power events.
    Case #SERVICE_CONTROL_SESSIONCHANGE         : sServiceControl = "SERVICE_CONTROL_SESSIONCHANGE"         ;0x0E Notifies a service of session change events.
    Case #SERVICE_CONTROL_PRESHUTDOWN           : sServiceControl = "SERVICE_CONTROL_PRESHUTDOWN"           ;0x0F Notifies a service that the system will be shutting down.
    Case #SERVICE_CONTROL_TIMECHANGE            : sServiceControl = "SERVICE_CONTROL_TIMECHANGE"            ;0x10 Notifies a service that the system time has changed.
    Case #SERVICE_CONTROL_TRIGGEREVENT          : sServiceControl = "SERVICE_CONTROL_TRIGGEREVENT"          ;0x20 Notifies a service registered for a service trigger event that the event has occurred.
    Case #SERVICE_CONTROL_USERMODEREBOOT        : sServiceControl = "SERVICE_CONTROL_USERMODEREBOOT"        ;0x40 Notifies a service that the user has initiated a reboot.
    Default                                     : sServiceControl = "SERVICE_CONTROL_UNKNOWN"               ;
  EndSelect
  ProcedureReturn(sServiceControl + " (0x" + Hex(dwServiceControl) + ")")
  
EndProcedure
;_____________________________________________________________________________

Procedure.l serviceQueryServiceStatus(sServer.s, sService.s)
  Protected ServiceStat.SERVICE_STATUS
  Protected hScManager.i
  Protected hService.i
  Protected LastError.l

  If #Debug : OutputDebugString_("serviceQueryServiceStatus") : EndIf
  hScManager = OpenSCManager_(@sServer, #SERVICES_ACTIVE_DATABASE, #SC_MANAGER_ENUMERATE_SERVICE)
  If hScManager
    hService  = OpenService_(hScManager, @sService, #SC_MANAGER_ENUMERATE_SERVICE)
    LastError = GetLastError_()
    If hService
      If QueryServiceStatus_(hService, @ServiceStat)
        StdOut(" Server:           " + sServer)
        StdOut(" Service:          " + sService)
        StdOut(" CurrentState:     " + serviceStringStatus(ServiceStat\dwCurrentState))
        StdOut(" ServiceType:      " + serviceStringType(ServiceStat\dwServiceType))
        StdOut(" ControlsAccepted: " + serviceStringControlIsAccepted(ServiceStat\dwControlsAccepted))
        StdOut(" Win32ExitCode:    " + Str(ServiceStat\dwWin32ExitCode))
        StdOut(" SpecificExitCode: " + Str(ServiceStat\dwServiceSpecificExitCode))
        StdOut(" CheckPoint:       " + Str(ServiceStat\dwCheckPoint))
        StdOut(" WaitHint:         " + Str(ServiceStat\dwWaitHint))
        ProcedureReturn(ServiceStat\dwCurrentState)
      EndIf
      CloseServiceHandle_(hService)
    Else
      Select LastError
        Case #ERROR_SERVICE_DOES_NOT_EXIST : StdOut(" Service " + sService + " : ERROR_SERVICE_DOES_NOT_EXIST")
        Case #ERROR_INVALID_NAME           : StdOut(" Service " + sService + " : ERROR_INVALID_NAME")
        Case #ERROR_ACCESS_DENIED          : StdOut(" Service " + sService + " : ERROR_ACCESS_DENIED")
        Case #ERROR_INVALID_HANDLE         : StdOut(" Service " + sService + " : ERROR_INVALID_HANDLE")
        Default                            : StdOut(" Service " + sService + " : ERROR 0x" + Hex(LastError))
      EndSelect
    EndIf
    CloseServiceHandle_(hScManager)
  Else
    StdOut(" serviceQueryServiceStatus:OpenSCManager failed")
  EndIf
  
EndProcedure
;_____________________________________________________________________________

Procedure.l RunAsSystemAdmin(sExeName.s)
;  ;Launches the given application with full system admin rights bypassing the UAC prompt
;  ;sExeName The name of the application to launch
; 
;  Protected  ProcessInfo.PROCESS_INFORMATION
;  Protected  ProcessEntry.PROCESSENTRY32
;  Protected  SecurityAttributes.SECURITY_ATTRIBUTES
;  Protected  StartupInf.STARTUPINFO
;  Static     zWinSta0.s{20}
;  Protected  hUserTokenDup.l
;  Protected  hProcess.l
;  Protected  hProcessSnapshot.l
;  Protected  hProcessToken.l
;  Protected  SessionId.l
;  Protected  ProcessSessionId.l
;  Protected  CreationFlags.l
;  Protected  RetValProc.l
;  Protected  *FunctionPointer
; 
;  If #Debug : OutputDebugString_("RunAsSystemAdmin") : EndIf
; 
;  ;Obtain the currently active session id; every logged on user in the system has a unique session id
;  SessionId = WTSGetActiveConsoleSessionId() ;Get the session-id of the console session: SessionId = 1 Console Active UserName or 0 Services Disconnected ""
;  If SessionId <> #INVALID_HANDLE_VALUE
;    ;Get the ProcessId of the WinLogon that have the same SessionId as the User's dwSessionId
;    ;Obtain the process id of the winlogon process that is running within the currently active session
;    If #Debug : OutputDebugString_("RunAsSystemAdmin:SessionId:OK") : EndIf
;    hProcessSnapshot = CreateToolhelp32Snapshot_(#TH32CS_SNAPPROCESS | #TH32CS_SNAPTHREAD, #Null)
;    If hProcessSnapshot <> #INVALID_HANDLE_VALUE
;      If #Debug : OutputDebugString_("RunAsSystemAdmin:hProcessSnapshot:OK") : EndIf
;      ProcessEntry\dwSize = SizeOf(PROCESSENTRY32)
;      RetValProc          = Process32First_(hProcessSnapshot, @ProcessEntry)
;      While RetValProc
;        Protected *BStr
;        Protected *BStrData.String
;        *BStr            = @ProcessEntry\szExeFile
;        *BStrData.String =  @*BStr
;        If LCase(*BStrData\s) = "winlogon.exe" ;May be more than one
;          If #Debug : OutputDebugString_("RunAsSystemAdmin:winlogon:OK") : EndIf
;          ProcessIdToSessionId(ProcessEntry\th32ProcessID, @ProcessSessionId) ;Process session id RunAsAdmin for winlogon, return nonzero on success
;          If ProcessSessionId = SessionId ;Need WinLogOn sessionId like in TaskManager
;            ;Obtain a handle to the winlogon process
;            If #Debug : OutputDebugString_("RunAsSystemAdmin:ProcessSessionId:OK") : EndIf
;            hProcess = OpenProcess_(#MAXIMUM_ALLOWED, #False, ProcessEntry\th32ProcessID)
;            If hProcess
;              If #Debug : OutputDebugString_("RunAsSystemAdmin:OpenProcess_:OK") : EndIf
;              If OpenProcessToken_(hProcess, #TOKEN_DUPLICATE, @hProcessToken)
;                If #Debug : OutputDebugString_("RunAsSystemAdmin:OpenProcessToken_:OK") : EndIf
;                ;Security attibute structure used in DuplicateTokenEx and CreateProcessAsUser
;                SecurityAttributes\nLength = SizeOf(SECURITY_ATTRIBUTES)
;                ;Copy the access token of the winlogon process, the newly created token will be a primary token
;                If DuplicateTokenEx_(hProcessToken, #MAXIMUM_ALLOWED, SecurityAttributes, #SecurityIdentification,
;                                     #TokenPrimary, @hUserTokenDup)
;                  If #Debug : OutputDebugString_("RunAsSystemAdmin:DuplicateTokenEx_:OK") : EndIf
;                  ;By default CreateProcessAsUser creates a process on a non-interactive window station, meaning
;                  ;the window station has a desktop that is invisible and the process is incapable of receiving
;                  ;user input. To remedy this we set the lpDesktop parameter to indicate we want to enable user
;                  ;interaction with the new process.
;                  zWinSta0             = "winsta0\default"
;                  StartupInf\lpDesktop = @zWinSta0 ;Interactive window station parameter; basically this indicates that the process created can display a GUI on the desktop
;                  StartupInf\cb        = SizeOf(STARTUPINFO)
; 
;                  ;Flags that specify the priority and creation method of the process
;                  CreationFlags = #NORMAL_PRIORITY_CLASS | #CREATE_NEW_CONSOLE
; 
;                  If #Debug : OutputDebugString_("RunAsSystemAdmin:sExeName: " + sExeName) : EndIf
;                  Protected zCurDir.s{#MAX_PATH}
;                  If FindString(sExeName, "\")
;                    zCurDir = Left(sExeName, Len(sExeName) - FindString(ReverseString(sExeName), "\"))
;                  Else
;                  EndIf
;                  If #Debug : OutputDebugString_("RunAsSystemAdmin:zCurDir: " + zCurDir) : EndIf
; 
;                  If #Debug : OutputDebugString_("RunAsSystemAdmin:CreateProcessAsUser") : EndIf
;                  ;Create a new process in the current user's logon session, Ok if return TRUE
;                  ;RegEdit/Admin      can't see HKEY_LOCAL_MACHINE\SECURITY\* (Cache-Policy-Recovery-RXACT-SAM)
;                  ;RegEdit/SystemAdmin can see HKEY_LOCAL_MACHINE\SECURITY\* (Cache-Policy-Recovery-RXACT-SAM)
;                  ProcedureReturn(CreateProcessAsUser_(hUserTokenDup,       ;Client's access token
;                                                       #Null,               ;File to execute
;                                                       @sExeName,           ;Command line
;                                                       @SecurityAttributes, ;Pointer to process SECURITY_ATTRIBUTES
;                                                       @SecurityAttributes, ;Pointer to thread SECURITY_ATTRIBUTES
;                                                       #False,              ;Handles are not inheritable
;                                                       CreationFlags,       ;Creation flags
;                                                       #Null,               ;Pointer to new environment block
;                                                       @zCurDir,            ;BYVAL %NULL, _ 'Name of current directory
;                                                       @StartupInf,         ;Pointer to STARTUPINFO structure
;                                                       @ProcessInfo))       ;Receives information about new process
;                  If #Debug : OutputDebugString_(" With SystemAdmin right you have access to HKLM\SECURITY\* subkey, aka (Cache-Policy-Recovery-RXACT-SAM)") : EndIf
;                  CloseHandle_(hUserTokenDup)
;                EndIf
;                CloseHandle_(hProcessToken)
;              EndIf
;              CloseHandle_(hProcess)
;            EndIf
;            Break ;Job done
;          EndIf
;        EndIf
;        RetValProc = Process32Next_(hProcessSnapshot, ProcessEntry)
;      Wend
;      CloseHandle_(hProcessSnapshot)
;    EndIf
;  EndIf

EndProcedure
;_____________________________________________________________________________

Procedure serviceInstall()
   Protected ServiceDelayed.SERVICE_DELAYED_AUTO_START_INFO
   Protected ServiceDesc.SERVICE_DESCRIPTION
   Protected sServiceDescription.s
   Protected hServiceControlManager.i
   Protected hService.i

 If #Debug : OutputDebugString_("serviceInstall()") : EndIf

 hServiceControlManager = OpenSCManager_(*pg\zComputerName, #Null, #SC_MANAGER_CREATE_SERVICE)
 If hServiceControlManager
   GetModuleFileName_(*pg\hInstance, @*pg\zExeName, #MAX_PATH) ;Get exe full name
   hService = CreateService_(hServiceControlManager, *pg\zServiceName, *pg\zServiceDisplayName,
                             #SERVICE_ALL_ACCESS, #SERVICE_WIN32_OWN_PROCESS | #SERVICE_INTERACTIVE_PROCESS,
                             #SERVICE_AUTO_START, #SERVICE_ERROR_NORMAL, *pg\zExeName, #Null, #Null,
                             #Null, #Null, #Null) ;SERVICE_DEMAND_START SERVICE_ERROR_IGNORE
   If hService
     ChangeServiceConfig_(hService, #SERVICE_NO_CHANGE, #SERVICE_AUTO_START,
                          #SERVICE_ERROR_NORMAL, #NUL, #NUL, 0, #NUL, #NUL, #NUL, #NUL)

     sServiceDescription       = #AppName + " Kill Compatibility Task." ;1024 bytes
     ServiceDesc\lpDescription = @sServiceDescription
     ChangeServiceConfig2_(hService, #SERVICE_CONFIG_DESCRIPTION, @ServiceDesc)

     If #DelayedStart ;Delayed service start, giving Windows time to breath on starting
       ServiceDelayed\fDelayedAutostart = #True
       ChangeServiceConfig2_(hService, #SERVICE_CONFIG_DELAYED_AUTO_START_INFO, @ServiceDelayed)
     EndIf

     StdOut("Service install successfull.")

     ProcedureReturn(#True)
     CloseServiceHandle_(hService)
   Else
     StdOut("Install - CreateService : Error.")
   EndIf
   CloseServiceHandle_(hServiceControlManager)
 Else
   StdOut("Install - OpenSCManager : Error.")
   StdOut("Need to be run as admin.")
 EndIf

EndProcedure
;_____________________________________________________________________________

Procedure serviceUnInstall()
 Protected hServiceControlManager.i
 Protected hService.i

 If #Debug : OutputDebugString_("serviceUnInstall()") : EndIf
 hServiceControlManager = OpenSCManager_(*pg\zComputerName, #Null, #SC_MANAGER_CREATE_SERVICE)
 If hServiceControlManager
   hService = OpenService_(hServiceControlManager, *pg\zServiceName, #SERVICE_ALL_ACCESS)
   If hService
     If DeleteService_(hService)
       StdOut("Uninstall successfull.")
       ProcedureReturn(#True)
     Else
       StdOut("Uninstall - DeleteService : Error.")
     EndIf
     CloseServiceHandle_(hService)
   Else
     StdOut("Uninstall - OpenService : Error.")
   EndIf
   CloseServiceHandle_(hServiceControlManager)
 Else
   StdOut("Uninstall - OpenSCManager : Error.")
 EndIf

EndProcedure
;_____________________________________________________________________________

Procedure serviceMain(dwArgs.l, lpszArgv.i)
 ;dwArgc   The number of arguments in the lpszArgv array.
 ;lpszArgv The null-terminated argument strings passed to the service by the call to the StartService function
 ;          that started the service. If there are no arguments, this parameter can be NULL.
 ;          Otherwise, the first argument (lpszArgv[0]) is the name of the service, followed
 ;          by any additional arguments (lpszArgv[1] through lpszArgv[dwArgc-1]).
 ;          If the user starts a manual service using the Services snap-in from the Control Panel,
 ;          the strings for the lpszArgv parameter come from the properties dialog box
 ;          for the service (from the Services snap-in, right-click the service entry, click Properties,
 ;          and enter the parameters in Start parameters.)

 ;All initialization tasks are done in ServiceMain() when the service is started.

 Protected SecurityAttribute.SECURITY_ATTRIBUTES
 Protected RetVal.l

 If #Debug : OutputDebugString_("serviceMain()") : EndIf
 *pg\hServiceStatus = RegisterServiceCtrlHandler_(*pg\zServiceName, @ServiceHandler())
 If *pg\hServiceStatus ;Did Not work
   ;Startup is pending
   If serviceSetServiceStatus(#SERVICE_START_PENDING, #NO_ERROR, 0, 1, 5000)
     ;Create the termination event
     *pg\hEvent = CreateEvent_(SecurityAttribute, #True, #False, "")
     If *pg\hEvent
       ;Service startup is still pending
       If serviceSetServiceStatus(#SERVICE_START_PENDING, #NO_ERROR, 0, 2, 1000)
         RetVal = serviceThreadCreate() ;Start the service
         If RetVal  ;Service did start
           If serviceSetServiceStatus(#SERVICE_RUNNING, #NO_ERROR, 0, 0, 0)  ;Service is now running
             ;Wait for the signal to end
             If #Debug : OutputDebugString_("serviceMain : Service thread started : SERVICE_RUNNING : WaitForSingleObject") : EndIf
             WaitForSingleObject_(*pg\hEvent, #INFINITE)
           EndIf
         EndIf
       EndIf
     EndIf
   EndIf
 EndIf
 ServiceTerminate(GetLastError_())

EndProcedure
;_____________________________________________________________________________

Procedure serviceThreadCreate()
 Protected SecurityAttribute.SECURITY_ATTRIBUTES
 Protected idThread.l

 If #Debug : OutputDebugString_("serviceThreadCreate()") : EndIf
 *pg\hThread = CreateThread_(SecurityAttribute, 0, @ServiceThread(), 0, 0, idThread)
 If *pg\hThread ;The thread start OK
   *pg\ServiceIsRunning = #True ;Set the global to running
   ProcedureReturn(#True)
 EndIf

EndProcedure
;_____________________________________________________________________________

Procedure serviceHandler(ControlValue.l)
 ;Service Control Handler - https://docs.microsoft.com/en-us/windows/desktop/services/service-control-handler-function
 ;Procedure will be called by Services.msc (Service Manager)

 If #Debug : OutputDebugString_("serviceHandler(Received request for " + serviceStringControl(ControlValue) + ")") : EndIf
 Select ControlValue

   Case #SERVICE_CONTROL_STOP,     ;0x1 Service should stop.
        #SERVICE_CONTROL_SHUTDOWN, ;0x5 To do on service shutdown
        #CTRL_SHUTDOWN_EVENT       ;0xA signal that the system sends when the system is shutting down.
     If *pg\ServiceIsPaused ;Resuming before ending
       serviceSetServiceStatus(#SERVICE_CONTINUE_PENDING, #NO_ERROR, 0, 1, 1000) ;Tell the SCM that we are resuming
       ServiceResume() ;Resume the service
       *pg\CurrentServiceStatus = #SERVICE_RUNNING ;Set the current state
     EndIf
     *pg\CurrentServiceStatus = #SERVICE_STOP_PENDING ;Set global status
     serviceSetServiceStatus(#SERVICE_STOP_PENDING, #NO_ERROR, 0, 1, 5000)
     ServiceStop()

   Case #SERVICE_CONTROL_PAUSE ;0x2 Service should pause.
     If (*pg\ServiceIsRunning <> #False) And (*pg\ServiceIsPaused = #False) ;Running And Not paused
       serviceSetServiceStatus(#SERVICE_PAUSE_PENDING, #NO_ERROR, 0, 1, 1000) ;Tell the SCM that we are pausing
       ServicePause() ;Pause it
       *pg\CurrentServiceStatus = #SERVICE_PAUSED ;Set the current state
     EndIf

   Case #SERVICE_CONTROL_CONTINUE ;0x3 Service should resume
     If (*pg\ServiceIsRunning <> #False) And (*pg\ServiceIsPaused <> #False) ;Running and paused
       serviceSetServiceStatus(#SERVICE_CONTINUE_PENDING, #NO_ERROR, 0, 1, 1000) ;Tell the SCM that we are resuming
       ServiceResume() ;Resume the service
       *pg\CurrentServiceStatus = #SERVICE_RUNNING ;Set the current state
     EndIf

   Case #SERVICE_CONTROL_INTERROGATE ;0x4 Service should report its current status to the control manager
     ;Simply return NO_ERROR; the SCM is aware of the current state of the service.
     ProcedureReturn(#NO_ERROR) ;A return value is needed else the command line "Net Start" will be erratic

 EndSelect

 serviceSetServiceStatus(*pg\CurrentServiceStatus, #NO_ERROR, 0, 0, 0)
 ProcedureReturn(#NO_ERROR)

EndProcedure
;_____________________________________________________________________________

Procedure.i getNamePID(ProcessName.s,*ptrPID)
  Protected HSnap.l,Prec.processentry32,result.l=#False
  HSnap=CreateToolhelp32Snapshot_(#TH32CS_SNAPPROCESS,0)
  If HSnap=#INVALID_HANDLE_VALUE
    ProcedureReturn result
  EndIf
  Prec\dwSize=SizeOf(prec)
  If Not Process32First_(HSnap,Prec)
    ProcedureReturn result
  EndIf
  Repeat
    If LCase(GetFilePart(PeekS(@Prec\szexefile)))=LCase(ProcessName)
      PokeL(*ptrPID,Prec\th32ProcessID)
      result=#True
      Break
    EndIf
  Until Not Process32Next_(HSnap,Prec)
  CloseHandle_(HSnap)
  ProcedureReturn result
EndProcedure
;_____________________________________________________________________________

Procedure netStop(sService.s)
  RunProgram("net.exe", "stop " + sService, #Null$, #PB_Program_Hide)
EndProcedure
;_____________________________________________________________________________

Procedure scConfig(sService.s, sStart.s)
  RunProgram("sc.exe" , "config " + sService + " start= " + sStart, #Null$, #PB_Program_Hide)
EndProcedure
;_____________________________________________________________________________

Procedure serviceThread(idThread.l) ; ; Here goes the service's job...
  ; Here goes the service's job...
  ; Modifier cette procédure pour effectuer les actions désirées
  
  Protected loop = 0, ptrPID = 0
  Protected sService.s, ExeName.s
  
  DataSection
    Demand:                         ; Début des services a positionner en Type de démarrage "Manuel"
      Data.s "QHActiveDefense"      ; 360 Total Security
      Data.s "BoxSyncUpdateService" ; Updater for Box Sync
      Data.s "DbxSvc"               ; Dropbox Service
      Data.s "Emby"                 ; The windows background Service For Emby Server.
      Data.s "MEmuSVC"              ; MEmu Service
      Data.s "SbieSvc"              ; SandBoxie Service
      Data.s "gupdate"              ; Service Google Update (gupdate)
      Data.s "gupdatem"             ; Service Google Update (gupdatem)
      Data.s "dbupdate"             ; Service Mise à jour Dropbox (dbupdate)
      Data.s "dbupdatem"            ; Service Mise à jour Dropbox (dbupdatem)
      Data.s "uSHAREitSvc"          ; SHAREit Hotspot Service
      Data.s "Spooler"              ; Spouleur d’impression
      Data.s "TomTomHOMEService"    ; TomTom Home Service For ejecting devices
      Data.s "tvmosaic_server"      ; C:\Program Files (x86)\DVBLogic\TVMosaic\tvmosaic_server.exe
      Data.s #Null$                 ; Fin des services Manuel
      
    Disabled:                       ; Début des services a positionner en Type de démarrage "Désactivé"
      Data.s "SysMain"              ; Gère et améliore les performances du système dans le temps.
      Data.s "WSearch"              ; Fournit des fonctionnalités d’indexation de contenu, ...
      Data.s #Null$                 ; Fin des services "Désactivé"
      
    TaskKill:                       ; Début des processus a tuer
      Data.s "CompatTelRunner.exe"  ; Microsoft Compatibility Telemetry
      Data.s #Null$                 ; Fin des processus a tuer
  EndDataSection
  
  ;;; *********************************************************
  ;;; ACTIONS A REALISER A CHAQUE DEMARRAGE DU SERVICE OU DU PC
  ;;; *********************************************************
  Restore Demand
  
  Repeat
    Read.s sService
    If sService <> #Null$
      Debug "service=" + sService
      scConfig(sService, "demand") : netStop(sService)
    EndIf
  Until sService = #Null$
  
  Restore Disabled
  
  Repeat
    Read.s sService
    If sService <> #Null$
      Debug "service=" + sService
      scConfig(sService, "disabled") : netStop(sService)
    EndIf
  Until sService = #Null$
  
  ;;; *******************************************************************
  ;;; MONITORING : ACTION A REALISER TANT QUE LE PC EST EN FONCTIONNEMENT
  ;;; *******************************************************************
  
  Repeat
    Restore TaskKill
    Repeat
      Read.s ExeName
      If ExeName <> #Null$
        ptrPID = 0
        If getNamePID(ExeName,@ptrPID)
          RunProgram("TaskKill.exe", "/f /im " + ExeName, #Null$, #PB_Program_Hide)
        EndIf
      EndIf
    Until ExeName = #Null$
    Delay(3000)
    loop+1
    ;;; ************************************
    ;;; ACTION A REALISER TOUTES LES MINUTES
    ;;; ************************************
    If loop = 20
      loop = 0
      RunProgram("TaskKill.exe", "/F /FI status eq Not RESPONDING", #Null$, #PB_Program_Hide)
    EndIf
  ForEver
EndProcedure
;_____________________________________________________________________________

Procedure serviceSetServiceStatus(CurrentStatus.l, ExitCode.l, ServiceSpecificExitCode.l, Checkpoint.l, WaitHint.l)
  
  If #Debug : OutputDebugString_("serviceSetServiceStatus(" + serviceStringStatus(CurrentStatus) + ")") : EndIf
  Protected ServiceStatus.SERVICE_STATUS
  
  ServiceStatus\dwServiceType  = #SERVICE_WIN32_OWN_PROCESS ;Setup the UDT.
  ServiceStatus\dwCurrentState = CurrentStatus
  
  If CurrentStatus = #SERVICE_START_PENDING
    ServiceStatus\dwControlsAccepted = 0
  Else
    ServiceStatus\dwControlsAccepted = #SERVICE_ACCEPT_STOP | #SERVICE_ACCEPT_PAUSE_CONTINUE | #SERVICE_ACCEPT_SHUTDOWN
  EndIf
  
  If ServiceSpecificExitCode = 0
    ServiceStatus\dwWin32ExitCode = ExitCode
  Else
    ServiceStatus\dwWin32ExitCode = #ERROR_SERVICE_SPECIFIC_ERROR
  EndIf
  
  ServiceStatus\dwServiceSpecificExitCode = ServiceSpecificExitCode
  ServiceStatus\dwCheckPoint              = Checkpoint
  ServiceStatus\dwWaitHint                = WaitHint
  
  If SetServiceStatus_(*pg\hServiceStatus, ServiceStatus)
    ProcedureReturn(#True)
  Else ;Something went wrong, so stop the service
    ServiceStop()
  EndIf
  
EndProcedure
;_____________________________________________________________________________

Procedure servicePause()
  
  If #Debug : OutputDebugString_("servicePause() " +  FormatDate("%hh:%ii:%ss", Date())) : EndIf
  *pg\ServiceIsPaused = #True ;Set the global indicating that we are paused
  SuspendThread_(*pg\hThread)
  
EndProcedure
;_____________________________________________________________________________

Procedure serviceResume()
  
  If #Debug : OutputDebugString_("serviceResume() " + FormatDate("%hh:%ii:%ss", Date())) : EndIf
  *pg\ServiceIsPaused = #False ;Set the global indicating that we are not paused
  ResumeThread_(*pg\hThread)
  
EndProcedure
;_____________________________________________________________________________

Procedure serviceStop()
  
  If #Debug : OutputDebugString_("Service thread stopping at " + FormatDate("%hh:%ii:%ss", Date())) : EndIf
  *pg\ServiceIsRunning = #False ;Set the global flag indicating that the service is not running
  SetEvent_(*pg\hEvent) ;Set the event so the service will stop
  
EndProcedure
;_____________________________________________________________________________

Procedure serviceStopRaw() ;Use when a service want to end by itself
  Protected ServiceStat.SERVICE_STATUS
  Protected hServiceControlManager.i
  Protected hService.i
  
  If #Debug : OutputDebugString_("serviceStopRaw()") : EndIf
    hServiceControlManager = OpenSCManager_(*pg\zComputerName, #Null, #SC_MANAGER_CREATE_SERVICE)
  If hServiceControlManager
    hService = OpenService_(hServiceControlManager, *pg\zServiceName, #SERVICE_ALL_ACCESS)
    If hService
      ControlService_(hService, #SERVICE_CONTROL_STOP, ServiceStat)
    EndIf
    CloseServiceHandle_(hServiceControlManager)
  EndIf
  
EndProcedure
;_____________________________________________________________________________

Procedure serviceTerminate(ErrorCode.l)
  
  If #Debug : OutputDebugString_("serviceTerminate()") : EndIf
  If *pg\hEvent
    CloseHandle_(*pg\hEvent)
  EndIf
  
  If *pg\hServiceStatus
    serviceSetServiceStatus(#SERVICE_STOPPED, ErrorCode, 0, 0, 0)
  EndIf
  
  If *pg\hThread
    CloseHandle_(*pg\hThread)
  EndIf
  
EndProcedure
;_____________________________________________________________________________

Procedure StdOut(Text.s)
  Protected CharDone.l
  Protected Reserved.l
  
  WriteConsole_(GetStdHandle_(#STD_OUTPUT_HANDLE), Text + #CRLF$, Len(Text) + 2, CharDone, Reserved)
  
EndProcedure
;_____________________________________________________________________________
;

; IDE Options = PureBasic 5.70 LTS (Windows - x64)
; ExecutableFormat = Console
; CursorPosition = 730
; FirstLine = 254
; Folding = QFo-D5
; Markers = 731
; EnableThread
; EnableXP
; Executable = AbleTaskKill.exe
; CompileSourceDirectory
; EnablePurifier