starting point towards running as a service

This commit is contained in:
Luke Pulverenti 2013-09-20 13:32:10 -04:00
parent e50c29ffca
commit f3ce127a62
13 changed files with 285 additions and 163 deletions

View File

@ -133,8 +133,8 @@ namespace MediaBrowser.Api
{ {
Task.Run(async () => Task.Run(async () =>
{ {
await Task.Delay(100); await Task.Delay(100).ConfigureAwait(false);
_appHost.Restart(); await _appHost.Restart().ConfigureAwait(false);
}); });
} }
@ -146,8 +146,8 @@ namespace MediaBrowser.Api
{ {
Task.Run(async () => Task.Run(async () =>
{ {
await Task.Delay(100); await Task.Delay(100).ConfigureAwait(false);
_appHost.Shutdown(); await _appHost.Shutdown().ConfigureAwait(false);
}); });
} }

View File

@ -552,7 +552,7 @@ namespace MediaBrowser.Common.Implementations
/// <summary> /// <summary>
/// Restarts this instance. /// Restarts this instance.
/// </summary> /// </summary>
public abstract void Restart(); public abstract Task Restart();
/// <summary> /// <summary>
/// Gets or sets a value indicating whether this instance can self update. /// Gets or sets a value indicating whether this instance can self update.
@ -582,7 +582,7 @@ namespace MediaBrowser.Common.Implementations
/// <summary> /// <summary>
/// Shuts down. /// Shuts down.
/// </summary> /// </summary>
public abstract void Shutdown(); public abstract Task Shutdown();
/// <summary> /// <summary>
/// Called when [application updated]. /// Called when [application updated].

View File

@ -37,7 +37,7 @@ namespace MediaBrowser.Common
/// <summary> /// <summary>
/// Restarts this instance. /// Restarts this instance.
/// </summary> /// </summary>
void Restart(); Task Restart();
/// <summary> /// <summary>
/// Gets the application version. /// Gets the application version.
@ -113,7 +113,7 @@ namespace MediaBrowser.Common
/// <summary> /// <summary>
/// Shuts down. /// Shuts down.
/// </summary> /// </summary>
void Shutdown(); Task Shutdown();
/// <summary> /// <summary>
/// Gets the plugins. /// Gets the plugins.

View File

@ -25,5 +25,11 @@ namespace MediaBrowser.Controller
/// </summary> /// </summary>
/// <value>The HTTP server URL prefix.</value> /// <value>The HTTP server URL prefix.</value>
string HttpServerUrlPrefix { get; } string HttpServerUrlPrefix { get; }
/// <summary>
/// Gets a value indicating whether this instance is background service.
/// </summary>
/// <value><c>true</c> if this instance is background service; otherwise, <c>false</c>.</value>
bool IsBackgroundService { get; }
} }
} }

View File

@ -81,7 +81,13 @@ namespace MediaBrowser.Model.System
public int HttpServerPortNumber { get; set; } public int HttpServerPortNumber { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="SystemInfo"/> class. /// Gets or sets a value indicating whether this instance is background service.
/// </summary>
/// <value><c>true</c> if this instance is background service; otherwise, <c>false</c>.</value>
public bool IsBackgroundService { get; set; }
/// <summary>
/// Initializes a new instance of the <see cref="SystemInfo" /> class.
/// </summary> /// </summary>
public SystemInfo() public SystemInfo()
{ {

View File

@ -1,89 +1,19 @@
using MediaBrowser.Common.Configuration; using MediaBrowser.Controller;
using MediaBrowser.Common.Constants;
using MediaBrowser.Common.Implementations.Updates;
using MediaBrowser.Controller;
using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Entities;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using MediaBrowser.Server.Implementations;
using MediaBrowser.ServerApplication.Splash; using MediaBrowser.ServerApplication.Splash;
using Microsoft.Win32;
using System; using System;
using System.Diagnostics; using System.Diagnostics;
using System.IO;
using System.Net.Cache;
using System.Threading;
using System.Windows; using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
namespace MediaBrowser.ServerApplication namespace MediaBrowser.ServerApplication
{ {
/// <summary> /// <summary>
/// Interaction logic for App.xaml /// Interaction logic for App.xaml
/// </summary> /// </summary>
public partial class App : Application public partial class App : Application, IApplicationInterface
{ {
/// <summary>
/// The single instance mutex
/// </summary>
private static Mutex _singleInstanceMutex;
/// <summary>
/// Defines the entry point of the application.
/// </summary>
[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();
}
/// <summary>
/// Gets the instance.
/// </summary>
/// <value>The instance.</value>
public static App Instance
{
get
{
return Current as App;
}
}
/// <summary> /// <summary>
/// Gets or sets the logger. /// Gets or sets the logger.
/// </summary> /// </summary>
@ -95,7 +25,7 @@ namespace MediaBrowser.ServerApplication
/// </summary> /// </summary>
/// <value>The composition root.</value> /// <value>The composition root.</value>
protected ApplicationHost CompositionRoot { get; set; } protected ApplicationHost CompositionRoot { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="App" /> class. /// Initializes a new instance of the <see cref="App" /> class.
/// </summary> /// </summary>
@ -105,6 +35,11 @@ namespace MediaBrowser.ServerApplication
InitializeComponent(); InitializeComponent();
} }
public bool IsBackgroundService
{
get { return false; }
}
/// <summary> /// <summary>
/// Gets the name of the uninstaller file. /// Gets the name of the uninstaller file.
/// </summary> /// </summary>
@ -114,63 +49,35 @@ namespace MediaBrowser.ServerApplication
get { return "MediaBrowser.Server.Uninstall.exe"; } get { return "MediaBrowser.Server.Uninstall.exe"; }
} }
/// <summary> public void OnUnhandledException(Exception ex)
/// Raises the <see cref="E:System.Windows.Application.Startup" /> event. {
/// </summary> Logger.ErrorException("UnhandledException", ex);
/// <param name="e">A <see cref="T:System.Windows.StartupEventArgs" /> that contains the event data.</param>
MessageBox.Show("Unhandled exception: " + ex.Message);
}
protected override void OnStartup(StartupEventArgs e) protected override void OnStartup(StartupEventArgs e)
{ {
AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; base.OnStartup(e);
LoadKernel();
SystemEvents.SessionEnding += SystemEvents_SessionEnding; LoadApplication();
}
/// <summary>
/// Handles the UnhandledException event of the CurrentDomain control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="UnhandledExceptionEventArgs" /> instance containing the event data.</param>
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));
}
}
/// <summary>
/// Handles the SessionEnding event of the SystemEvents control.
/// </summary>
/// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="SessionEndingEventArgs" /> instance containing the event data.</param>
void SystemEvents_SessionEnding(object sender, SessionEndingEventArgs e)
{
// Try to shut down gracefully
Shutdown();
} }
/// <summary> /// <summary>
/// Loads the kernel. /// Loads the kernel.
/// </summary> /// </summary>
protected async void LoadKernel() protected async void LoadApplication()
{ {
try try
{ {
CompositionRoot = new ApplicationHost(); CompositionRoot = new ApplicationHost(this);
Logger = CompositionRoot.LogManager.GetLogger("App"); Logger = CompositionRoot.LogManager.GetLogger("App");
var splash = new SplashWindow(CompositionRoot.ApplicationVersion); var splash = new SplashWindow(CompositionRoot.ApplicationVersion);
splash.Show(); splash.Show();
await CompositionRoot.Init(); await CompositionRoot.Init();
splash.Hide(); splash.Hide();
@ -192,13 +99,18 @@ namespace MediaBrowser.ServerApplication
} }
} }
public void ShutdownApplication()
{
Dispatcher.Invoke(Shutdown);
}
/// <summary> /// <summary>
/// Raises the <see cref="E:System.Windows.Application.Exit" /> event. /// Raises the <see cref="E:System.Windows.Application.Exit" /> event.
/// </summary> /// </summary>
/// <param name="e">An <see cref="T:System.Windows.ExitEventArgs" /> that contains the event data.</param> /// <param name="e">An <see cref="T:System.Windows.ExitEventArgs" /> that contains the event data.</param>
protected override void OnExit(ExitEventArgs e) protected override void OnExit(ExitEventArgs e)
{ {
ReleaseMutex(); MainStartup.ReleaseMutex();
base.OnExit(e); base.OnExit(e);
@ -208,22 +120,6 @@ namespace MediaBrowser.ServerApplication
} }
} }
/// <summary>
/// Releases the mutex.
/// </summary>
private void ReleaseMutex()
{
if (_singleInstanceMutex == null)
{
return;
}
_singleInstanceMutex.ReleaseMutex();
_singleInstanceMutex.Close();
_singleInstanceMutex.Dispose();
_singleInstanceMutex = null;
}
/// <summary> /// <summary>
/// Opens the dashboard page. /// Opens the dashboard page.
/// </summary> /// </summary>
@ -281,9 +177,9 @@ namespace MediaBrowser.ServerApplication
/// Restarts this instance. /// Restarts this instance.
/// </summary> /// </summary>
/// <exception cref="System.NotImplementedException"></exception> /// <exception cref="System.NotImplementedException"></exception>
public void Restart() public void RestartApplication()
{ {
Dispatcher.Invoke(ReleaseMutex); Dispatcher.Invoke(MainStartup.ReleaseMutex);
CompositionRoot.Dispose(); CompositionRoot.Dispose();

View File

@ -1,5 +1,4 @@
using System.Windows.Forms; using MediaBrowser.Api;
using MediaBrowser.Api;
using MediaBrowser.Common; using MediaBrowser.Common;
using MediaBrowser.Common.Configuration; using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Constants; using MediaBrowser.Common.Constants;
@ -7,7 +6,6 @@ using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Implementations; using MediaBrowser.Common.Implementations;
using MediaBrowser.Common.Implementations.IO; using MediaBrowser.Common.Implementations.IO;
using MediaBrowser.Common.Implementations.ScheduledTasks; using MediaBrowser.Common.Implementations.ScheduledTasks;
using MediaBrowser.Common.Implementations.Updates;
using MediaBrowser.Common.MediaInfo; using MediaBrowser.Common.MediaInfo;
using MediaBrowser.Common.Net; using MediaBrowser.Common.Net;
using MediaBrowser.Controller; using MediaBrowser.Controller;
@ -184,6 +182,13 @@ namespace MediaBrowser.ServerApplication
private IItemRepository ItemRepository { get; set; } private IItemRepository ItemRepository { get; set; }
private INotificationsRepository NotificationsRepository { get; set; } private INotificationsRepository NotificationsRepository { get; set; }
public bool IsBackgroundService
{
get { return _appInterface != null && _appInterface.IsBackgroundService; }
}
private readonly IApplicationInterface _appInterface;
/// <summary> /// <summary>
/// The full path to our startmenu shortcut /// The full path to our startmenu shortcut
/// </summary> /// </summary>
@ -194,6 +199,11 @@ namespace MediaBrowser.ServerApplication
private Task<IHttpServer> _httpServerCreationTask; private Task<IHttpServer> _httpServerCreationTask;
public ApplicationHost(IApplicationInterface appInterface)
{
_appInterface = appInterface;
}
/// <summary> /// <summary>
/// Runs the startup tasks. /// Runs the startup tasks.
/// </summary> /// </summary>
@ -505,7 +515,7 @@ namespace MediaBrowser.ServerApplication
base.OnConfigurationUpdated(sender, e); base.OnConfigurationUpdated(sender, e);
HttpServer.EnableHttpRequestLogging = ServerConfigurationManager.Configuration.EnableHttpLevelLogging; HttpServer.EnableHttpRequestLogging = ServerConfigurationManager.Configuration.EnableHttpLevelLogging;
if (!string.Equals(HttpServer.UrlPrefix, HttpServerUrlPrefix, StringComparison.OrdinalIgnoreCase)) if (!string.Equals(HttpServer.UrlPrefix, HttpServerUrlPrefix, StringComparison.OrdinalIgnoreCase))
{ {
NotifyPendingRestart(); NotifyPendingRestart();
@ -521,19 +531,18 @@ namespace MediaBrowser.ServerApplication
/// <summary> /// <summary>
/// Restarts this instance. /// Restarts this instance.
/// </summary> /// </summary>
public override void Restart() public override async Task Restart()
{ {
try try
{ {
var task = ServerManager.SendWebSocketMessageAsync("ServerRestarting", () => string.Empty, CancellationToken.None); await ServerManager.SendWebSocketMessageAsync("ServerRestarting", () => string.Empty, CancellationToken.None).ConfigureAwait(false);
task.Wait();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.ErrorException("Error sending server restart web socket message", ex); Logger.ErrorException("Error sending server restart web socket message", ex);
} }
App.Instance.Restart(); _appInterface.RestartApplication();
} }
/// <summary> /// <summary>
@ -618,7 +627,8 @@ namespace MediaBrowser.ServerApplication
Id = _systemId, Id = _systemId,
ProgramDataPath = ApplicationPaths.ProgramDataPath, ProgramDataPath = ApplicationPaths.ProgramDataPath,
MacAddress = GetMacAddress(), MacAddress = GetMacAddress(),
HttpServerPortNumber = ServerConfigurationManager.Configuration.HttpServerPortNumber HttpServerPortNumber = ServerConfigurationManager.Configuration.HttpServerPortNumber,
IsBackgroundService = IsBackgroundService
}; };
} }
@ -642,19 +652,18 @@ namespace MediaBrowser.ServerApplication
/// <summary> /// <summary>
/// Shuts down. /// Shuts down.
/// </summary> /// </summary>
public override void Shutdown() public override async Task Shutdown()
{ {
try try
{ {
var task = ServerManager.SendWebSocketMessageAsync("ServerShuttingDown", () => string.Empty, CancellationToken.None); await ServerManager.SendWebSocketMessageAsync("ServerShuttingDown", () => string.Empty, CancellationToken.None).ConfigureAwait(false);
task.Wait();
} }
catch (Exception ex) catch (Exception ex)
{ {
Logger.ErrorException("Error sending server shutdown web socket message", ex); Logger.ErrorException("Error sending server shutdown web socket message", ex);
} }
App.Instance.Dispatcher.Invoke(App.Instance.Shutdown); _appInterface.ShutdownApplication();
} }
/// <summary> /// <summary>

View File

@ -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();
}
}
}

View File

@ -64,7 +64,10 @@ namespace MediaBrowser.ServerApplication.EntryPoints
{ {
_logger.ErrorException("Error launching startup wizard", ex); _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");
}
} }
} }

View File

@ -0,0 +1,32 @@
using System;
namespace MediaBrowser.ServerApplication
{
/// <summary>
/// Interface IApplicationInterface
/// </summary>
public interface IApplicationInterface
{
/// <summary>
/// Gets a value indicating whether this instance is background service.
/// </summary>
/// <value><c>true</c> if this instance is background service; otherwise, <c>false</c>.</value>
bool IsBackgroundService { get; }
/// <summary>
/// Shutdowns the application.
/// </summary>
void ShutdownApplication();
/// <summary>
/// Restarts the application.
/// </summary>
void RestartApplication();
/// <summary>
/// Called when [unhandled exception].
/// </summary>
/// <param name="ex">The ex.</param>
void OnUnhandledException(Exception ex);
}
}

View File

@ -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
{
/// <summary>
/// The single instance mutex
/// </summary>
private static Mutex _singleInstanceMutex;
private static IApplicationInterface _applicationInterface;
/// <summary>
/// Defines the entry point of the application.
/// </summary>
[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));
}
}
/// <summary>
/// Releases the mutex.
/// </summary>
internal static void ReleaseMutex()
{
if (_singleInstanceMutex == null)
{
return;
}
_singleInstanceMutex.ReleaseMutex();
_singleInstanceMutex.Close();
_singleInstanceMutex.Dispose();
_singleInstanceMutex = null;
}
}
}

View File

@ -119,7 +119,7 @@ namespace MediaBrowser.ServerApplication
Dispatcher.InvokeAsync(() => Dispatcher.InvokeAsync(() =>
{ {
var logWindow = App.Instance.Windows.OfType<LogWindow>().FirstOrDefault(); var logWindow = App.Current.Windows.OfType<LogWindow>().FirstOrDefault();
if ((logWindow == null && _configurationManager.Configuration.ShowLogWindow) || (logWindow != null && !_configurationManager.Configuration.ShowLogWindow)) 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"); App.OpenUrl("https://github.com/MediaBrowser/MediaBrowser/wiki");
} }
/// <summary> /// <summary>
/// Occurs when [property changed]. /// Occurs when [property changed].
/// </summary> /// </summary>
@ -258,7 +258,7 @@ namespace MediaBrowser.ServerApplication
{ {
App.OpenDashboardPage("dashboard.html", loggedInUser, _configurationManager, _appHost); App.OpenDashboardPage("dashboard.html", loggedInUser, _configurationManager, _appHost);
} }
/// <summary> /// <summary>
/// Handles the click event of the cmVisitCT control. /// Handles the click event of the cmVisitCT control.
/// </summary> /// </summary>
@ -285,9 +285,9 @@ namespace MediaBrowser.ServerApplication
/// </summary> /// </summary>
/// <param name="sender">The source of the event.</param> /// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="RoutedEventArgs" /> instance containing the event data.</param> /// <param name="e">The <see cref="RoutedEventArgs" /> instance containing the event data.</param>
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);
} }
/// <summary> /// <summary>
@ -295,9 +295,9 @@ namespace MediaBrowser.ServerApplication
/// </summary> /// </summary>
/// <param name="sender">The source of the event.</param> /// <param name="sender">The source of the event.</param>
/// <param name="e">The <see cref="RoutedEventArgs" /> instance containing the event data.</param> /// <param name="e">The <see cref="RoutedEventArgs" /> instance containing the event data.</param>
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);
} }
/// <summary> /// <summary>

View File

@ -57,7 +57,7 @@
<WarningLevel>4</WarningLevel> <WarningLevel>4</WarningLevel>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<StartupObject>MediaBrowser.ServerApplication.App</StartupObject> <StartupObject>MediaBrowser.ServerApplication.MainStartup</StartupObject>
</PropertyGroup> </PropertyGroup>
<PropertyGroup> <PropertyGroup>
<ApplicationIcon>Resources\Images\icon.ico</ApplicationIcon> <ApplicationIcon>Resources\Images\icon.ico</ApplicationIcon>
@ -187,6 +187,7 @@
<Reference Include="System.Net.Http" /> <Reference Include="System.Net.Http" />
<Reference Include="System.Net.Http.WebRequest" /> <Reference Include="System.Net.Http.WebRequest" />
<Reference Include="System.Runtime.Remoting" /> <Reference Include="System.Runtime.Remoting" />
<Reference Include="System.ServiceProcess" />
<Reference Include="System.Windows.Forms" /> <Reference Include="System.Windows.Forms" />
<Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL"> <Reference Include="System.Windows.Interactivity, Version=4.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35, processorArchitecture=MSIL">
<HintPath>..\packages\MahApps.Metro.0.11.0.17-ALPHA\lib\net45\System.Windows.Interactivity.dll</HintPath> <HintPath>..\packages\MahApps.Metro.0.11.0.17-ALPHA\lib\net45\System.Windows.Interactivity.dll</HintPath>
@ -203,8 +204,13 @@
<Reference Include="PresentationFramework" /> <Reference Include="PresentationFramework" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Compile Include="BackgroundService.cs">
<SubType>Component</SubType>
</Compile>
<Compile Include="EntryPoints\StartupWizard.cs" /> <Compile Include="EntryPoints\StartupWizard.cs" />
<Compile Include="EntryPoints\UdpServerEntryPoint.cs" /> <Compile Include="EntryPoints\UdpServerEntryPoint.cs" />
<Compile Include="IApplicationInterface.cs" />
<Compile Include="MainStartup.cs" />
<Compile Include="Splash\SplashWindow.xaml.cs"> <Compile Include="Splash\SplashWindow.xaml.cs">
<DependentUpon>SplashWindow.xaml</DependentUpon> <DependentUpon>SplashWindow.xaml</DependentUpon>
</Compile> </Compile>