Best practice in native code is to call SetServiceStatus with a non-zero exit code to indicate 1) it’s stopped and 2) something went wrong.
In managed code, you could achieve the same effect by obtaining the SCM handle through the ServiceBase.ServiceHandle Property and P/Invoke-ing the Win32 API.
I don’t see why the SCM would treat this any differently than setting the
ServiceBase.ExitCode property non-zero and then calling
ServiceBase.Stop, actually. P/Invoke is a bit more direct perhaps, if the service is in panic mode.
As noted in the comments (also see https://serverfault.com/questions/72318/set-up-recovery-actions-to-take-place-when-a-service-fails) if a process calls
SetServiceStatus(SERVICE_STOPPED) with a non-zero exit code, the Recovery Actions for the serice will only be done if the option “Enable Actions For Stops With Errors” (
sc.exe failureflag) is ticked. -> System Event ID 7024
If a service process exits (
Env.Exit()) or crashs without consulting the SCM, then the Recovery Actions will always be run. -> System Event ID 7031