diff --git a/MediaBrowser.Api/SystemService.cs b/MediaBrowser.Api/SystemService.cs
index 3360e5e9e6..9bbd6a5883 100644
--- a/MediaBrowser.Api/SystemService.cs
+++ b/MediaBrowser.Api/SystemService.cs
@@ -133,8 +133,8 @@ namespace MediaBrowser.Api
{
Task.Run(async () =>
{
- await Task.Delay(100);
- _appHost.Restart();
+ await Task.Delay(100).ConfigureAwait(false);
+ await _appHost.Restart().ConfigureAwait(false);
});
}
@@ -146,8 +146,8 @@ namespace MediaBrowser.Api
{
Task.Run(async () =>
{
- await Task.Delay(100);
- _appHost.Shutdown();
+ await Task.Delay(100).ConfigureAwait(false);
+ await _appHost.Shutdown().ConfigureAwait(false);
});
}
diff --git a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
index bae2f9f07a..f333eb22c7 100644
--- a/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
+++ b/MediaBrowser.Common.Implementations/BaseApplicationHost.cs
@@ -552,7 +552,7 @@ namespace MediaBrowser.Common.Implementations
///
/// Restarts this instance.
///
- public abstract void Restart();
+ public abstract Task Restart();
///
/// Gets or sets a value indicating whether this instance can self update.
@@ -582,7 +582,7 @@ namespace MediaBrowser.Common.Implementations
///
/// Shuts down.
///
- public abstract void Shutdown();
+ public abstract Task Shutdown();
///
/// Called when [application updated].
diff --git a/MediaBrowser.Common/IApplicationHost.cs b/MediaBrowser.Common/IApplicationHost.cs
index 1ff28d924f..7cb58f580b 100644
--- a/MediaBrowser.Common/IApplicationHost.cs
+++ b/MediaBrowser.Common/IApplicationHost.cs
@@ -37,7 +37,7 @@ namespace MediaBrowser.Common
///
/// Restarts this instance.
///
- void Restart();
+ Task Restart();
///
/// Gets the application version.
@@ -113,7 +113,7 @@ namespace MediaBrowser.Common
///
/// Shuts down.
///
- void Shutdown();
+ Task Shutdown();
///
/// Gets the plugins.
diff --git a/MediaBrowser.Controller/IServerApplicationHost.cs b/MediaBrowser.Controller/IServerApplicationHost.cs
index f96c2536e6..495147125b 100644
--- a/MediaBrowser.Controller/IServerApplicationHost.cs
+++ b/MediaBrowser.Controller/IServerApplicationHost.cs
@@ -25,5 +25,11 @@ namespace MediaBrowser.Controller
///
/// The HTTP server URL prefix.
string HttpServerUrlPrefix { get; }
+
+ ///
+ /// Gets a value indicating whether this instance is background service.
+ ///
+ /// true if this instance is background service; otherwise, false.
+ bool IsBackgroundService { get; }
}
}
diff --git a/MediaBrowser.Model/System/SystemInfo.cs b/MediaBrowser.Model/System/SystemInfo.cs
index f77856c6e4..b00d7f29d3 100644
--- a/MediaBrowser.Model/System/SystemInfo.cs
+++ b/MediaBrowser.Model/System/SystemInfo.cs
@@ -81,7 +81,13 @@ namespace MediaBrowser.Model.System
public int HttpServerPortNumber { get; set; }
///
- /// Initializes a new instance of the class.
+ /// Gets or sets a value indicating whether this instance is background service.
+ ///
+ /// true if this instance is background service; otherwise, false.
+ public bool IsBackgroundService { get; set; }
+
+ ///
+ /// Initializes a new instance of the class.
///
public SystemInfo()
{
diff --git a/MediaBrowser.ServerApplication/App.xaml.cs b/MediaBrowser.ServerApplication/App.xaml.cs
index 362dd4bc08..42045257af 100644
--- a/MediaBrowser.ServerApplication/App.xaml.cs
+++ b/MediaBrowser.ServerApplication/App.xaml.cs
@@ -1,89 +1,19 @@
-using MediaBrowser.Common.Configuration;
-using MediaBrowser.Common.Constants;
-using MediaBrowser.Common.Implementations.Updates;
-using MediaBrowser.Controller;
+using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Logging;
-using MediaBrowser.Server.Implementations;
using MediaBrowser.ServerApplication.Splash;
-using Microsoft.Win32;
using System;
using System.Diagnostics;
-using System.IO;
-using System.Net.Cache;
-using System.Threading;
using System.Windows;
-using System.Windows.Controls;
-using System.Windows.Media;
-using System.Windows.Media.Imaging;
namespace MediaBrowser.ServerApplication
{
///
/// Interaction logic for App.xaml
///
- public partial class App : Application
+ public partial class App : Application, IApplicationInterface
{
- ///
- /// The single instance mutex
- ///
- private static Mutex _singleInstanceMutex;
-
- ///
- /// Defines the entry point of the application.
- ///
- [STAThread]
- public static void Main()
- {
- bool createdNew;
-
- var runningPath = Process.GetCurrentProcess().MainModule.FileName.Replace(Path.DirectorySeparatorChar.ToString(), string.Empty);
-
- _singleInstanceMutex = new Mutex(true, @"Local\" + runningPath, out createdNew);
-
- if (!createdNew)
- {
- _singleInstanceMutex = null;
- return;
- }
-
- // Look for the existence of an update archive
- var appPaths = new ServerApplicationPaths();
- var updateArchive = Path.Combine(appPaths.TempUpdatePath, Constants.MbServerPkgName + ".zip");
- if (File.Exists(updateArchive))
- {
- // Update is there - execute update
- try
- {
- new ApplicationUpdater().UpdateApplication(MBApplication.MBServer, appPaths, updateArchive);
-
- // And just let the app exit so it can update
- return;
- }
- catch (Exception e)
- {
- MessageBox.Show(string.Format("Error attempting to update application.\n\n{0}\n\n{1}", e.GetType().Name, e.Message));
- }
- }
-
- var application = new App();
-
- application.Run();
- }
-
- ///
- /// Gets the instance.
- ///
- /// The instance.
- public static App Instance
- {
- get
- {
- return Current as App;
- }
- }
-
///
/// Gets or sets the logger.
///
@@ -95,7 +25,7 @@ namespace MediaBrowser.ServerApplication
///
/// The composition root.
protected ApplicationHost CompositionRoot { get; set; }
-
+
///
/// Initializes a new instance of the class.
///
@@ -105,6 +35,11 @@ namespace MediaBrowser.ServerApplication
InitializeComponent();
}
+ public bool IsBackgroundService
+ {
+ get { return false; }
+ }
+
///
/// Gets the name of the uninstaller file.
///
@@ -114,63 +49,35 @@ namespace MediaBrowser.ServerApplication
get { return "MediaBrowser.Server.Uninstall.exe"; }
}
- ///
- /// Raises the event.
- ///
- /// A that contains the event data.
+ public void OnUnhandledException(Exception ex)
+ {
+ Logger.ErrorException("UnhandledException", ex);
+
+ MessageBox.Show("Unhandled exception: " + ex.Message);
+ }
+
protected override void OnStartup(StartupEventArgs e)
{
- AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
- LoadKernel();
+ base.OnStartup(e);
- SystemEvents.SessionEnding += SystemEvents_SessionEnding;
- }
-
- ///
- /// Handles the UnhandledException event of the CurrentDomain control.
- ///
- /// The source of the event.
- /// The instance containing the event data.
- void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
- {
- var exception = (Exception)e.ExceptionObject;
-
- Logger.ErrorException("UnhandledException", exception);
-
- MessageBox.Show("Unhandled exception: " + exception.Message);
-
- if (!Debugger.IsAttached)
- {
- Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(exception));
- }
- }
-
- ///
- /// Handles the SessionEnding event of the SystemEvents control.
- ///
- /// The source of the event.
- /// The instance containing the event data.
- void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
- {
- // Try to shut down gracefully
- Shutdown();
+ LoadApplication();
}
///
/// Loads the kernel.
///
- protected async void LoadKernel()
+ protected async void LoadApplication()
{
try
{
- CompositionRoot = new ApplicationHost();
+ CompositionRoot = new ApplicationHost(this);
Logger = CompositionRoot.LogManager.GetLogger("App");
var splash = new SplashWindow(CompositionRoot.ApplicationVersion);
splash.Show();
-
+
await CompositionRoot.Init();
splash.Hide();
@@ -192,13 +99,18 @@ namespace MediaBrowser.ServerApplication
}
}
+ public void ShutdownApplication()
+ {
+ Dispatcher.Invoke(Shutdown);
+ }
+
///
/// Raises the event.
///
/// An that contains the event data.
protected override void OnExit(ExitEventArgs e)
{
- ReleaseMutex();
+ MainStartup.ReleaseMutex();
base.OnExit(e);
@@ -208,22 +120,6 @@ namespace MediaBrowser.ServerApplication
}
}
- ///
- /// Releases the mutex.
- ///
- private void ReleaseMutex()
- {
- if (_singleInstanceMutex == null)
- {
- return;
- }
-
- _singleInstanceMutex.ReleaseMutex();
- _singleInstanceMutex.Close();
- _singleInstanceMutex.Dispose();
- _singleInstanceMutex = null;
- }
-
///
/// Opens the dashboard page.
///
@@ -281,9 +177,9 @@ namespace MediaBrowser.ServerApplication
/// Restarts this instance.
///
///
- public void Restart()
+ public void RestartApplication()
{
- Dispatcher.Invoke(ReleaseMutex);
+ Dispatcher.Invoke(MainStartup.ReleaseMutex);
CompositionRoot.Dispose();
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index 13afb41e72..7288d70d92 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -1,5 +1,4 @@
-using System.Windows.Forms;
-using MediaBrowser.Api;
+using MediaBrowser.Api;
using MediaBrowser.Common;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Constants;
@@ -7,7 +6,6 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Implementations;
using MediaBrowser.Common.Implementations.IO;
using MediaBrowser.Common.Implementations.ScheduledTasks;
-using MediaBrowser.Common.Implementations.Updates;
using MediaBrowser.Common.MediaInfo;
using MediaBrowser.Common.Net;
using MediaBrowser.Controller;
@@ -184,6 +182,13 @@ namespace MediaBrowser.ServerApplication
private IItemRepository ItemRepository { get; set; }
private INotificationsRepository NotificationsRepository { get; set; }
+ public bool IsBackgroundService
+ {
+ get { return _appInterface != null && _appInterface.IsBackgroundService; }
+ }
+
+ private readonly IApplicationInterface _appInterface;
+
///
/// The full path to our startmenu shortcut
///
@@ -194,6 +199,11 @@ namespace MediaBrowser.ServerApplication
private Task _httpServerCreationTask;
+ public ApplicationHost(IApplicationInterface appInterface)
+ {
+ _appInterface = appInterface;
+ }
+
///
/// Runs the startup tasks.
///
@@ -505,7 +515,7 @@ namespace MediaBrowser.ServerApplication
base.OnConfigurationUpdated(sender, e);
HttpServer.EnableHttpRequestLogging = ServerConfigurationManager.Configuration.EnableHttpLevelLogging;
-
+
if (!string.Equals(HttpServer.UrlPrefix, HttpServerUrlPrefix, StringComparison.OrdinalIgnoreCase))
{
NotifyPendingRestart();
@@ -521,19 +531,18 @@ namespace MediaBrowser.ServerApplication
///
/// Restarts this instance.
///
- public override void Restart()
+ public override async Task Restart()
{
try
{
- var task = ServerManager.SendWebSocketMessageAsync("ServerRestarting", () => string.Empty, CancellationToken.None);
- task.Wait();
+ await ServerManager.SendWebSocketMessageAsync("ServerRestarting", () => string.Empty, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.ErrorException("Error sending server restart web socket message", ex);
}
- App.Instance.Restart();
+ _appInterface.RestartApplication();
}
///
@@ -618,7 +627,8 @@ namespace MediaBrowser.ServerApplication
Id = _systemId,
ProgramDataPath = ApplicationPaths.ProgramDataPath,
MacAddress = GetMacAddress(),
- HttpServerPortNumber = ServerConfigurationManager.Configuration.HttpServerPortNumber
+ HttpServerPortNumber = ServerConfigurationManager.Configuration.HttpServerPortNumber,
+ IsBackgroundService = IsBackgroundService
};
}
@@ -642,19 +652,18 @@ namespace MediaBrowser.ServerApplication
///
/// Shuts down.
///
- public override void Shutdown()
+ public override async Task Shutdown()
{
try
{
- var task = ServerManager.SendWebSocketMessageAsync("ServerShuttingDown", () => string.Empty, CancellationToken.None);
- task.Wait();
+ await ServerManager.SendWebSocketMessageAsync("ServerShuttingDown", () => string.Empty, CancellationToken.None).ConfigureAwait(false);
}
catch (Exception ex)
{
Logger.ErrorException("Error sending server shutdown web socket message", ex);
}
- App.Instance.Dispatcher.Invoke(App.Instance.Shutdown);
+ _appInterface.ShutdownApplication();
}
///
diff --git a/MediaBrowser.ServerApplication/BackgroundService.cs b/MediaBrowser.ServerApplication/BackgroundService.cs
new file mode 100644
index 0000000000..a8a9a5b50d
--- /dev/null
+++ b/MediaBrowser.ServerApplication/BackgroundService.cs
@@ -0,0 +1,30 @@
+using System.ServiceProcess;
+
+namespace MediaBrowser.ServerApplication
+{
+ public class BackgroundService : ServiceBase
+ {
+ public BackgroundService()
+ {
+ CanPauseAndContinue = false;
+ CanHandleSessionChangeEvent = true;
+ CanStop = false;
+ CanShutdown = true;
+ ServiceName = "Media Browser";
+ }
+
+ protected override void OnSessionChange(SessionChangeDescription changeDescription)
+ {
+ base.OnSessionChange(changeDescription);
+ }
+
+ protected override void OnStart(string[] args)
+ {
+ }
+
+ protected override void OnShutdown()
+ {
+ base.OnShutdown();
+ }
+ }
+}
diff --git a/MediaBrowser.ServerApplication/EntryPoints/StartupWizard.cs b/MediaBrowser.ServerApplication/EntryPoints/StartupWizard.cs
index 87578ef84c..aac5a8cb83 100644
--- a/MediaBrowser.ServerApplication/EntryPoints/StartupWizard.cs
+++ b/MediaBrowser.ServerApplication/EntryPoints/StartupWizard.cs
@@ -64,7 +64,10 @@ namespace MediaBrowser.ServerApplication.EntryPoints
{
_logger.ErrorException("Error launching startup wizard", ex);
- MessageBox.Show("There was an error launching the Media Browser startup wizard. Please ensure a web browser is installed on the machine and is configured as the default browser.", "Media Browser");
+ if (!_appHost.IsBackgroundService)
+ {
+ MessageBox.Show("There was an error launching the Media Browser startup wizard. Please ensure a web browser is installed on the machine and is configured as the default browser.", "Media Browser");
+ }
}
}
diff --git a/MediaBrowser.ServerApplication/IApplicationInterface.cs b/MediaBrowser.ServerApplication/IApplicationInterface.cs
new file mode 100644
index 0000000000..e753248262
--- /dev/null
+++ b/MediaBrowser.ServerApplication/IApplicationInterface.cs
@@ -0,0 +1,32 @@
+using System;
+
+namespace MediaBrowser.ServerApplication
+{
+ ///
+ /// Interface IApplicationInterface
+ ///
+ public interface IApplicationInterface
+ {
+ ///
+ /// Gets a value indicating whether this instance is background service.
+ ///
+ /// true if this instance is background service; otherwise, false.
+ bool IsBackgroundService { get; }
+
+ ///
+ /// Shutdowns the application.
+ ///
+ void ShutdownApplication();
+
+ ///
+ /// Restarts the application.
+ ///
+ void RestartApplication();
+
+ ///
+ /// Called when [unhandled exception].
+ ///
+ /// The ex.
+ void OnUnhandledException(Exception ex);
+ }
+}
diff --git a/MediaBrowser.ServerApplication/MainStartup.cs b/MediaBrowser.ServerApplication/MainStartup.cs
new file mode 100644
index 0000000000..e5d44c0f5b
--- /dev/null
+++ b/MediaBrowser.ServerApplication/MainStartup.cs
@@ -0,0 +1,134 @@
+using MediaBrowser.Common.Constants;
+using MediaBrowser.Common.Implementations.Updates;
+using MediaBrowser.Server.Implementations;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Windows;
+using Microsoft.Win32;
+
+namespace MediaBrowser.ServerApplication
+{
+ public class MainStartup
+ {
+ ///
+ /// The single instance mutex
+ ///
+ private static Mutex _singleInstanceMutex;
+
+ private static IApplicationInterface _applicationInterface;
+
+ ///
+ /// Defines the entry point of the application.
+ ///
+ [STAThread]
+ public static void Main()
+ {
+ AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
+
+ bool createdNew;
+
+ var runningPath = Process.GetCurrentProcess().MainModule.FileName.Replace(Path.DirectorySeparatorChar.ToString(), string.Empty);
+
+ _singleInstanceMutex = new Mutex(true, @"Local\" + runningPath, out createdNew);
+
+ if (!createdNew)
+ {
+ _singleInstanceMutex = null;
+ return;
+ }
+
+ // Look for the existence of an update archive
+ var appPaths = new ServerApplicationPaths();
+ var updateArchive = Path.Combine(appPaths.TempUpdatePath, Constants.MbServerPkgName + ".zip");
+ if (File.Exists(updateArchive))
+ {
+ // Update is there - execute update
+ try
+ {
+ new ApplicationUpdater().UpdateApplication(MBApplication.MBServer, appPaths, updateArchive);
+
+ // And just let the app exit so it can update
+ return;
+ }
+ catch (Exception e)
+ {
+ MessageBox.Show(string.Format("Error attempting to update application.\n\n{0}\n\n{1}", e.GetType().Name, e.Message));
+ }
+ }
+
+ StartApplication();
+ }
+
+ private static void StartApplication()
+ {
+ SystemEvents.SessionEnding += SystemEvents_SessionEnding;
+ var commandLineArgs = Environment.GetCommandLineArgs();
+
+ if (commandLineArgs.Length > 1 && commandLineArgs[1].Equals("-service"))
+ {
+ // Start application as a service
+ StartBackgroundService();
+ }
+ else
+ {
+ StartWpfApp();
+ }
+ }
+
+ static void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
+ {
+ // Try to shutdown gracefully
+ if (_applicationInterface != null)
+ {
+ _applicationInterface.ShutdownApplication();
+ }
+ }
+
+ private static void StartWpfApp()
+ {
+ var app = new App();
+
+ _applicationInterface = app;
+
+ app.Run();
+ }
+
+ private static void StartBackgroundService()
+ {
+
+ }
+
+ static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
+ {
+ var exception = (Exception)e.ExceptionObject;
+
+ if (_applicationInterface != null)
+ {
+ _applicationInterface.OnUnhandledException(exception);
+ }
+
+ if (!Debugger.IsAttached)
+ {
+ Environment.Exit(System.Runtime.InteropServices.Marshal.GetHRForException(exception));
+ }
+ }
+
+ ///
+ /// Releases the mutex.
+ ///
+ internal static void ReleaseMutex()
+ {
+ if (_singleInstanceMutex == null)
+ {
+ return;
+ }
+
+ _singleInstanceMutex.ReleaseMutex();
+ _singleInstanceMutex.Close();
+ _singleInstanceMutex.Dispose();
+ _singleInstanceMutex = null;
+ }
+ }
+}
diff --git a/MediaBrowser.ServerApplication/MainWindow.xaml.cs b/MediaBrowser.ServerApplication/MainWindow.xaml.cs
index 974bb6f481..4dcdeef599 100644
--- a/MediaBrowser.ServerApplication/MainWindow.xaml.cs
+++ b/MediaBrowser.ServerApplication/MainWindow.xaml.cs
@@ -119,7 +119,7 @@ namespace MediaBrowser.ServerApplication
Dispatcher.InvokeAsync(() =>
{
- var logWindow = App.Instance.Windows.OfType().FirstOrDefault();
+ var logWindow = App.Current.Windows.OfType().FirstOrDefault();
if ((logWindow == null && _configurationManager.Configuration.ShowLogWindow) || (logWindow != null && !_configurationManager.Configuration.ShowLogWindow))
{
@@ -204,7 +204,7 @@ namespace MediaBrowser.ServerApplication
{
App.OpenUrl("https://github.com/MediaBrowser/MediaBrowser/wiki");
}
-
+
///
/// Occurs when [property changed].
///
@@ -258,7 +258,7 @@ namespace MediaBrowser.ServerApplication
{
App.OpenDashboardPage("dashboard.html", loggedInUser, _configurationManager, _appHost);
}
-
+
///
/// Handles the click event of the cmVisitCT control.
///
@@ -285,9 +285,9 @@ namespace MediaBrowser.ServerApplication
///
/// The source of the event.
/// The instance containing the event data.
- private void cmExit_click(object sender, RoutedEventArgs e)
+ private async void cmExit_click(object sender, RoutedEventArgs e)
{
- Application.Current.Shutdown();
+ await _appHost.Shutdown().ConfigureAwait(false);
}
///
@@ -295,9 +295,9 @@ namespace MediaBrowser.ServerApplication
///
/// The source of the event.
/// The instance containing the event data.
- private void cmdReloadServer_click(object sender, RoutedEventArgs e)
+ private async void cmdReloadServer_click(object sender, RoutedEventArgs e)
{
- App.Instance.Restart();
+ await _appHost.Restart().ConfigureAwait(false);
}
///
diff --git a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
index bc84fca506..34d7eaf02e 100644
--- a/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
+++ b/MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj
@@ -57,7 +57,7 @@
4
- MediaBrowser.ServerApplication.App
+ MediaBrowser.ServerApplication.MainStartup
Resources\Images\icon.ico
@@ -187,6 +187,7 @@
+
..\packages\MahApps.Metro.0.11.0.17-ALPHA\lib\net45\System.Windows.Interactivity.dll
@@ -203,8 +204,13 @@
+
+ Component
+
+
+
SplashWindow.xaml