Page 1 of 2

Windows service

Posted: Sat Oct 27, 2018 8:51 pm
by Pierre Bellisle
A Windows service template that can also launch a GUI application in System Administrator mode.

Have fun...

Re: Windows service

Posted: Sun Oct 28, 2018 1:05 am
by Dude
Thanks for this... will try it later. :)

Re: Windows service

Posted: Wed Oct 31, 2018 9:06 am
by Rings
@Pierre Bellisle: nice website with Powerbasic codes.
to preserve this purebasic code, i
extracted it from the web page:

Code: Select all

; * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
 ; * 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        = "Able.exe"

;************************************************************************************************************************************************
#Version          = "2018-09-02 18:11:00" ;                                                                                                     *
#AppName          = "Able" ;Dictionary: able - �^bl, adjective having enough strength, power or means (To do a thing).                          *
#Debug            = #True  ;#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 + " start a GUI as SYSTEM Administrator." ;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 serviceThread(idThread.l)
 ;Here goes the service's job...

 If #RunAsSystemAdmin
   If #Debug : OutputDebugString_("-") : EndIf

   If #Debug
     ;Show UserNameEx and UserName
     Protected zUserName.s{#UNLEN}
     Protected UserNameLen.l = #UNLEN
     GetUserNameEx_(#NameSamCompatible, @zUserName, @UserNameLen)
     OutputDebugString_("UserNameEx: " + zUserName)
     GetUserName_(@zUserName, @UserNameLen)
     OutputDebugString_("UserName: " + zUserName + " <<<<<<<<<<<<<<<<<<< Will be 'System' (System Administrator).")
   EndIf

   ;Call RegEdit.exe
   Protected WindowsDirectory.s{#MAX_PATH}
   Protected FullNameExe.s{#MAX_PATH}
   GetWindowsDirectory_(@WindowsDirectory, #MAX_PATH)
   FullNameExe = WindowsDirectory + "\RegEdit.exe"
   If #Debug
     OutputDebugString_("serviceThread(RunAsSystemAdmin(" + FullNameExe + ")")
     OutputDebugString_("In RegEdit you can see HKLM\SECURITY\* (Cache-Policy-Recovery-RXACT-SAM")
     OutputDebugString_(" you can't in normal Administrator mode.")
   EndIf
   RunAsSystemAdmin(FullNameExe) ;Usually "C:\Windows\RegEdit.exe")
   Sleep_(2000)
 EndIf

 If #AutoStop
   ServiceStopRaw() ;Used when a service want To End by itself after his job is done
 Else
   Repeat
     If #Debug : OutputDebugString_("serviceThread() running " + FormatDate("%hh:%ii:%ss", Date())) : EndIf
     Sleep_(2000)
   ForEver
 EndIf

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.42 LTS (Windows - x64)
; ExecutableFormat = Console
; CursorPosition = 614
; FirstLine = 591
; Folding = ----
; EnableAsm
; EnableUnicode
; EnableThread
; EnableXP
; EnableAdmin
; UseIcon = 274.ico
; Executable = Able.exe
; DisableDebugger
; CompileSourceDirectory
; Compiler = PureBasic 5.42 LTS (Windows - x64)
; IncludeVersionInfo
; VersionField0 = 1.0.0.0
; VersionField1 = 1.0.0.0
; VersionField2 = Bellisle
; VersionField3 = Able
; VersionField4 = 1.0.0.0
; VersionField5 = 1.0.0.0
; VersionField6 = Service to start a GUI as SYSTEM Administrator
; VersionField7 = Able
; VersionField8 = Able
; VersionField9 = 2018
; VersionField10 = 2018

Re: Windows service

Posted: Wed Oct 31, 2018 9:24 am
by Dude
Rings wrote:to preserve this purebasic code, i
extracted it from the web page
I already saved it offline, too. ;)

Re: Windows service

Posted: Fri May 24, 2019 8:37 am
by boyoss
@Pierre Bellisle Thanks for the excellent code
@Rings Thanks for bringing it here

It will be usefull to have a possibilty to send paramters to the program launched that way

Re: Windows service

Posted: Sat May 25, 2019 12:15 pm
by Russell18
thanks for giving the info!

Re: Windows service

Posted: Wed May 29, 2019 4:50 pm
by boyoss
I've found how to send paramters

Code: Select all

									path.s = GetPathPart(Mid(sExeName, 1, FindString(sExeName + " ", " ")))
									file.s = Mid(sExeName, Len(path) + 1)
									ProcedureReturn(CreateProcessAsUser_(hUserTokenDup,       ;Client's access token
									                                     #Null,								;File to execute
									                                     @file,								;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
									                                     @path,								;BYVAL %NULL, _ 'Name of current directory
									                                     @StartupInf,					;Pointer to STARTUPINFO structure
									                                     @ProcessInfo))				;Receives information about new process
But now i have another problem, i want to launch the exe with NORMAL rights, not System. For example, i want to open registry and see it normaly, without the "HKEY_LOCAL_MACHINE\SECURITY\*"

I understantd that the rights has to be changed, but how?

thanks

Re: Windows service

Posted: Sat Jun 27, 2020 5:57 am
by Pierre Bellisle
It's been a year, still, if you never found a solution...
Just change...
If LCase(*BStrData\s) = "winlogon.exe"
to
If LCase(*BStrData\s) = "explorer.exe"

Re: Windows service

Posted: Sat Jun 27, 2020 6:16 am
by Pierre Bellisle
I recently worked with tatanas to insert some SERVICE_CONTROL_SESSIONCHANGE notifications.
While at it, I did many minors corrections...
The service can now write to a log file in addition to the OutputDebugString() command.

More, the service will now start an application launcher, so experimentation will be much easier.
This application launcher is named SysAdminExec.exe, it will tell you if you are Administrator, or System-Administrator, a corresponding icon will be chosen. From an .ini file, SysAdminExec will memorize the screen position and size, and the last 150 chosen executables. Thanks to a mutex, only one instance is allowed. An attempt to restart will bring the original to fore. SysAdminExec accepts <Drag and Drop> messages from applications with a lower elevation. You can also specify a list of executables via the command line.

I used compiler v. 5.72.
APIs lover should like.

Able service
Able service Able (Alternate address)

Re: Windows service

Posted: Tue Dec 01, 2020 10:12 pm
by RichAlgeni
Elegant code is a joy to read! Nicely done!!!

Re: Windows service

Posted: Thu Dec 03, 2020 7:47 pm
by Kwai chang caine
Waooouh !!! impressive all this collection of powerfulls codes :shock:
It's a pity it's PowerBasic codes :wink:
Mainly the USB codes ....they missing to PB
Download (rename to .zip)
I have download
http://codesite.atwebpages.com/PureBASIC/Able.zip.txt
and rename to ZIP but impossible to uncompress :cry: W10/X64

Re: Windows service

Posted: Wed Dec 16, 2020 1:03 am
by Pierre Bellisle
Rich,

I'm glad you liked. :-)

-

Hey Kwai!

Yep, it is mostly PowerBASIC code. Conversion to Pure should be not too hard to do in many cases I guess...

I took a look at Able.zip.txt and had the same problem you do.

Since this web server refuse zip files, I renamed it to .txt as a bypass.
After upload, checking the .txt file give an incorect bytes count.
Mabe an automated Windows to Unix CRLF/LF conversion, I'm not sure...
I did re-upload with a .zzz extention, then rename it with a .txt extention on the server.
The bytes count is now correct and my download/unzip try worked fine.

Give it a try...

Able service
Able service (Alternate address)

Re: Windows service

Posted: Sun Dec 20, 2020 4:55 pm
by Caronte3D
What are the possible uses for this code?

Re: Windows service

Posted: Mon Dec 21, 2020 4:32 am
by Pierre Bellisle
Satisfy curiosity is the main purpose... :-)
The three significant aspects of this code are, how to code a service, get System account rights, and start a GUI from a service.

As is, it give super power to an application of your choice.
For example, if you want to access the registry HKEY_LOCAL_MACHINE\SAM via RegEdit.exe you'll need System rights.

You may do a web search with Windows "system account", many interesting things can be found, like
System Account in Windows or Identity and access protection - System account
etc...

Re: Windows service

Posted: Mon Dec 21, 2020 12:05 pm
by Caronte3D
Pierre Bellisle wrote:Satisfy curiosity is the main purpose... :-)...
Thanks! :D