Skip to content

Commit

Permalink
Mirror Windows Service Host exitcode behaviour on ungraceful exit
Browse files Browse the repository at this point in the history
Add ability to stop service with exit code

This allows services to signal recovery states to the Windows SCM
without having to throw an unhandled exception which will spam
the event log.
  • Loading branch information
StijnHoopVDL authored and phatboyg committed Jan 5, 2018
1 parent 8cfd1f7 commit fec8cc2
Show file tree
Hide file tree
Showing 4 changed files with 28 additions and 19 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ packages
*.dotSettings

.fake
.idea


bin
obj
Expand Down
6 changes: 6 additions & 0 deletions src/Topshelf.sln.DotSettings
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Int64 x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/BLANK_LINES_AFTER_START_COMMENT/@EntryValue">0</s:Int64>
<s:String x:Key="/Default/CodeStyle/CodeFormatting/CSharpFormat/PLACE_ACCESSORHOLDER_ATTRIBUTE_ON_SAME_LINE_EX/@EntryValue">NEVER</s:String>
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/AddImportsToDeepestScope/@EntryValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/AllowAlias/@EntryValue">False</s:Boolean>
<s:Boolean x:Key="/Default/CodeStyle/CSharpUsing/CanUseGlobalAlias/@EntryValue">False</s:Boolean>
Expand All @@ -16,7 +17,12 @@ under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR &#xD
CONDITIONS OF ANY KIND, either express or implied. See the License for the &#xD;
specific language governing permissions and limitations under the License.</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=Interfaces/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="I" Suffix="" Style="AaBb"&gt;&lt;ExtraRule Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpRenamePlacementToArrangementMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAddAccessorOwnerDeclarationBracesMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002ECSharpPlaceAttributeOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateThisQualifierSettings/@EntryIndexedValue">True</s:Boolean>
<s:String x:Key="/Default/FilterSettingsManager/CoverageFilterXml/@EntryValue">&lt;data&gt;&lt;IncludeFilters /&gt;&lt;ExcludeFilters /&gt;&lt;/data&gt;</s:String>
Expand Down
1 change: 1 addition & 0 deletions src/Topshelf/Hosts/ConsoleRunHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ void StopService()
_settings.ExceptionCallback?.Invoke(ex);

_log.Error("The service did not shut down gracefully", ex);
_exitCode = TopshelfExitCode.StopServiceFailed;
}
finally
{
Expand Down
38 changes: 19 additions & 19 deletions src/Topshelf/Runtime/Windows/WindowsServiceHost.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ public TopshelfExitCode Run()

AppDomain.CurrentDomain.UnhandledException += CatchUnhandledException;

ExitCode = (int)TopshelfExitCode.Ok;
ExitCode = (int) TopshelfExitCode.Ok;

_log.Info("Starting as a Windows service");

Expand All @@ -70,22 +70,22 @@ public TopshelfExitCode Run()
_settings, Assembly.GetEntryAssembly().GetName());
_log.Fatal(message);

ExitCode = (int)TopshelfExitCode.ServiceNotInstalled;
ExitCode = (int) TopshelfExitCode.ServiceNotInstalled;
throw new TopshelfException(message);
}

_log.Debug("[Topshelf] Starting up as a windows service application");

Run(this);

return (TopshelfExitCode)Enum.ToObject(typeof(TopshelfExitCode), ExitCode);
return (TopshelfExitCode) Enum.ToObject(typeof(TopshelfExitCode), ExitCode);
}

void HostControl.RequestAdditionalTime(TimeSpan timeRemaining)
{
_log.DebugFormat("Requesting additional time: {0}", timeRemaining);

RequestAdditionalTime((int)timeRemaining.TotalMilliseconds);
RequestAdditionalTime((int) timeRemaining.TotalMilliseconds);
}

void HostControl.Restart()
Expand All @@ -95,13 +95,14 @@ void HostControl.Restart()
throw new NotImplementedException("This is not done yet, so I'm trying");
}

private void InternalStop(TopshelfExitCode? exitCode = null)
void InternalStop(TopshelfExitCode? exitCode = null)

{
if (CanStop)
{
_log.Debug("Stop requested by hosted service");
if (exitCode.HasValue)
ExitCode = (int)exitCode.Value;
ExitCode = (int) exitCode.Value;
Stop();
}
else
Expand Down Expand Up @@ -147,7 +148,7 @@ protected override void OnStart(string[] args)

_log.Fatal("The service did not start successfully", ex);

ExitCode = (int)TopshelfExitCode.StartServiceFailed;
ExitCode = (int) TopshelfExitCode.StartServiceFailed;
throw;
}
}
Expand All @@ -168,13 +169,13 @@ protected override void OnStop()
_settings.ExceptionCallback?.Invoke(ex);

_log.Fatal("The service did not shut down gracefully", ex);
ExitCode = (int)TopshelfExitCode.StopServiceFailed;
ExitCode = (int) TopshelfExitCode.StopServiceFailed;
throw;
}

if (_unhandledException != null)
{
ExitCode = (int)TopshelfExitCode.UnhandledServiceException;
ExitCode = (int) TopshelfExitCode.UnhandledServiceException;
_log.Info("[Topshelf] Unhandled exception detected, rethrowing to cause application to restart.");
throw new InvalidOperationException("An unhandled exception was detected", _unhandledException);
}
Expand Down Expand Up @@ -235,7 +236,7 @@ protected override void OnShutdown()
_settings.ExceptionCallback?.Invoke(ex);

_log.Fatal("The service did not shut down gracefully", ex);
ExitCode = (int)TopshelfExitCode.StopServiceFailed;
ExitCode = (int) TopshelfExitCode.StopServiceFailed;
throw;
}
}
Expand All @@ -257,7 +258,7 @@ protected override void OnSessionChange(SessionChangeDescription changeDescripti
_settings.ExceptionCallback?.Invoke(ex);

_log.Fatal("The did not handle Service session change correctly", ex);
ExitCode = (int)TopshelfExitCode.StopServiceFailed;
ExitCode = (int) TopshelfExitCode.StopServiceFailed;
throw;
}
}
Expand All @@ -281,10 +282,9 @@ protected override bool OnPowerEvent(PowerBroadcastStatus powerStatus)
_settings.ExceptionCallback?.Invoke(ex);

_log.Fatal("The service did handle the Power event correctly", ex);
ExitCode = (int)TopshelfExitCode.StopServiceFailed;
ExitCode = (int) TopshelfExitCode.StopServiceFailed;
throw;
}

}

protected override void OnCustomCommand(int command)
Expand Down Expand Up @@ -320,9 +320,9 @@ protected override void Dispose(bool disposing)

void CatchUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
_settings.ExceptionCallback?.Invoke((Exception)e.ExceptionObject);
_settings.ExceptionCallback?.Invoke((Exception) e.ExceptionObject);

_log.Fatal("The service threw an unhandled exception", (Exception)e.ExceptionObject);
_log.Fatal("The service threw an unhandled exception", (Exception) e.ExceptionObject);

HostLogger.Shutdown();

Expand All @@ -331,7 +331,7 @@ void CatchUnhandledException(object sender, UnhandledExceptionEventArgs e)
// return;
// This needs to be a configuration option to avoid breaking compatibility

ExitCode = (int)TopshelfExitCode.UnhandledServiceException;
ExitCode = (int) TopshelfExitCode.UnhandledServiceException;
_unhandledException = (Exception) e.ExceptionObject;

Stop();
Expand Down Expand Up @@ -359,7 +359,7 @@ public WindowsSessionChangedArguments(SessionChangeDescription changeDescription
{
_reasonCode =
(SessionChangeReasonCode)
Enum.ToObject(typeof(SessionChangeReasonCode), (int)changeDescription.Reason);
Enum.ToObject(typeof(SessionChangeReasonCode), (int) changeDescription.Reason);
_sessionId = changeDescription.SessionId;
}

Expand All @@ -381,7 +381,7 @@ class WindowsPowerEventArguments :

public WindowsPowerEventArguments(PowerBroadcastStatus powerStatus)
{
_eventCode = (PowerEventCode) Enum.ToObject(typeof(PowerEventCode), (int)powerStatus);
_eventCode = (PowerEventCode) Enum.ToObject(typeof(PowerEventCode), (int) powerStatus);
}


Expand All @@ -391,4 +391,4 @@ public PowerEventCode EventCode
}
}
}
}
}

0 comments on commit fec8cc2

Please sign in to comment.