Crashed services of a business software are inconvenient. If they occur more than once, it becomes annoying. If there is also a software supplier who is unwilling or unable to solve the problem, it gets complicated. A quick and pragmatic solution was needed shortly before the Easter holidays. If only there was not another obstacle in the way: It is all about Windows services.
No progress with on-board resources
Usually, modern operating systems provide an administrator with the necessary tools to control services. In Windows, it is the graphical snap-in services.msc in the Microsoft Management Console1, which has barely changed since NT4 Option Pack. You can set the start modes and up to three error handling modes:
- Restart a service
- Execute a programme
- Restart the computer
Unfortunately, these actions can only be applied to three error responses. After the fourth error, the service remains dead for at least one day. Someone in Redmond thought in the late 90s of the last millennium that the error counter only had to be set by full day. The graphical service control of Windows is therefore unsuitable. The interface, unchanged for decades, is a statement manifested in code that will not change in the future.
What about the PowerShell?2 Missing functions in the GUI normally can be found there. But again, an admin looks into the abyss of a technological pile of broken pieces that has been accumulated and left untouched for decades.
The instance responsible for services in Windows is the Service Control Manager, SCM in short.3 Since NT4 times, there has been no way around this by design. In the .NET Framework there are methods and properties for starting or stopping services. The direct way to the unmanaged4 SCM remains conceptually denied. The lack of functions corresponds to that of the graphical user interface.
An indirect, rather “dirty way” is described in the MSDN article “Writing Windows Services in PowerShell”.5 A C-program generated from a PowerShell script is registered and executed as a service by the runtime. The disadvantages of this basic solution speak for themselves: Where normally one EXE file is sufficient, one has to deal with several scripts and one executable. The script execution, which by default is switched off for good reasons, must be activated system-wide. The overhead of the .NET script interpreter6 is dragged alongside. The usual service accounts with low privileges do not work. Full administration rights are necessary. This is, what security and programming madness looks like, no one wants to see in a production environment. The author of the MSDN article is well aware of this:
A service script written in Windows PowerShell will be good for prototyping a concept and for tasks with low performance costs (…) But for any high performance task, a rewrite in C++ or C# is recommended.
There is no way forward with Windows on-board utilities. A system without the possibility of controlling services is by definition not an operating system and has no role at all in any productive environment. However, this discussion has to be addressed elsewhere.
A service is needed
Back to the initial problem: How can troublesome, erratically crashing services be controlled and automatically restarted? With another service that queries the status via SCM and restarts them if necessary. All done noiselessly and unagitatedly. Which services are to be monitored is specified in a text file that is to be editable by the administrator.
For such tasks I have a secret weapon. Machine-oriented (it can even be programmed inline in assembler), “rocksolid” proven over many decades, with the full range of all language features of modern high-level languages and yet easy to handle. C-compatible libraries can be directly integrated and bindings exist for pretty much anything and everything. Of course, it’s all free and open source.
The solution: Servicewatcher
I created the servicewatcher in about two hours using the programming language Freebasic7 and compiled it in the fbc integrated gcc.8 The 64 bit Windows EXE is only 56 KB in size. Native9 without any dependencies only 524 KB of RAM are needed. The CPU load is not measurable.
In doing so, I have not reinvented the wheel. A ready-made example in C for Windows services exists in the MSDN.10 In the German Freebasic portal someone has already made efforts to port it.11 However, the implementation unfortunately did not run in 64 bit and lacked some required functions. But it was a good start and served as a basis. I was able to derive the missing pieces of the puzzle from the MSDN.
Any better code editor with a freely configurable build system is suitable as an IDE. Under Linux I use Geany 12. GTK surfaces I create visually with GNOME Glade.13 Because I have to talk to the Windows SCM for the servicewatcher and don’t need a UI for the console application14, I left my usual environment and programmed everything in a Windows VM with FBEdit15 written itself in Freebasic.
Those who have already used Basic dialects will immediately find their way around in Freebasic. It fulfils all the requirements that are expected of object-oriented16 languages. If you really want to push to the extremes, you can abuse everyting and create “Quick and Dirty” imperative17 progams like in GWBASIC.18
Perhaps it is due to my past that I first came into contact with BASIC19 on a Commodore C64 in the 80s. On my first Schneider Euro-PC I used Turbo-Basic and its successor Power-Basic.20 Visual-Basic followed in the 90s, much later VB.NET and Xojo. To this day, BASIC in its modern, high-level form is somehow closer to me than C or Python.
Small challenges and tools like servicewatcher are therefore welcome and quickly “written down” simply doing their job without fuss.
Sub ServiceStopRaw()
Dim ServiceStat As SERVICE_STATUS
Dim hServiceControlManager As HANDLE
Dim hService As HANDLE
hServiceControlManager = OpenSCManager(pg->wsComputerName, BYVAL NULL, SC_MANAGER_CREATE_SERVICE)
If hServiceControlManager Then
hService = OpenService(hServiceControlManager, pg->wsServiceName, SERVICE_ALL_ACCESS)
If hService Then ControlService(hService, SERVICE_CONTROL_STOP, @ServiceStat)
CloseServiceHandle(hServiceControlManager)
EndIf
End Sub
Of course the original cause of the crashing services of the enterprise resource planning solution has not been solved. But the solution programmed in Freebasic relieves the customer’s pains.
As expected and appropriate, he has received the GPL source code together with a short documentation as a Git repo.
Have a good day,
Yours, Tomas Jakobs
Update 07.08.2024:
You’ll find the repo of this project on codeberg.org:
https://codeberg.org/tomas-jakobs/servicewatcher
https://de.wikipedia.org/wiki/Microsoft_Management_Console ↩︎
https://learn.microsoft.com/en-us/archive/msdn-magazine/2016/may/windows-powershell-writing-windows-services-in-powershell ↩︎
https://learn.microsoft.com/de-de/windows/win32/services/the-complete-service-sample ↩︎
https://freebasic-portal.de/code-beispiele/system/windows-service-beispiel-306.html ↩︎
https://en.wikipedia.org/wiki/Object-oriented_programming ↩︎