瀏覽代碼

starting point towards running as a service

Luke Pulverenti 11 年之前
父節點
當前提交
f3ce127a62

+ 4 - 4
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);
             });
         }
 

+ 2 - 2
MediaBrowser.Common.Implementations/BaseApplicationHost.cs

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

+ 2 - 2
MediaBrowser.Common/IApplicationHost.cs

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

+ 6 - 0
MediaBrowser.Controller/IServerApplicationHost.cs

@@ -25,5 +25,11 @@ namespace MediaBrowser.Controller
         /// </summary>
         /// <value>The HTTP server URL prefix.</value>
         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; }
     }
 }

+ 7 - 1
MediaBrowser.Model/System/SystemInfo.cs

@@ -81,7 +81,13 @@ namespace MediaBrowser.Model.System
         public int HttpServerPortNumber { get; set; }
 
         /// <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>
         public SystemInfo()
         {

+ 25 - 129
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
 {
     /// <summary>
     /// Interaction logic for App.xaml
     /// </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>
         /// Gets or sets the logger.
         /// </summary>
@@ -95,7 +25,7 @@ namespace MediaBrowser.ServerApplication
         /// </summary>
         /// <value>The composition root.</value>
         protected ApplicationHost CompositionRoot { get; set; }
-        
+
         /// <summary>
         /// Initializes a new instance of the <see cref="App" /> class.
         /// </summary>
@@ -105,6 +35,11 @@ namespace MediaBrowser.ServerApplication
             InitializeComponent();
         }
 
+        public bool IsBackgroundService
+        {
+            get { return false; }
+        }
+
         /// <summary>
         /// Gets the name of the uninstaller file.
         /// </summary>
@@ -114,63 +49,35 @@ namespace MediaBrowser.ServerApplication
             get { return "MediaBrowser.Server.Uninstall.exe"; }
         }
 
-        /// <summary>
-        /// Raises the <see cref="E:System.Windows.Application.Startup" /> event.
-        /// </summary>
-        /// <param name="e">A <see cref="T:System.Windows.StartupEventArgs" /> that contains the event data.</param>
-        protected override void OnStartup(StartupEventArgs e)
+        public void OnUnhandledException(Exception ex)
         {
-            AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
-            LoadKernel();
+            Logger.ErrorException("UnhandledException", ex);
 
-            SystemEvents.SessionEnding += SystemEvents_SessionEnding;
+            MessageBox.Show("Unhandled exception: " + ex.Message);
         }
 
-        /// <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)
+        protected override void OnStartup(StartupEventArgs e)
         {
-            var exception = (Exception)e.ExceptionObject;
-
-            Logger.ErrorException("UnhandledException", exception);
+            base.OnStartup(e);
 
-            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();
+            LoadApplication();
         }
 
         /// <summary>
         /// Loads the kernel.
         /// </summary>
-        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);
+        }
+
         /// <summary>
         /// Raises the <see cref="E:System.Windows.Application.Exit" /> event.
         /// </summary>
         /// <param name="e">An <see cref="T:System.Windows.ExitEventArgs" /> that contains the event data.</param>
         protected override void OnExit(ExitEventArgs e)
         {
-            ReleaseMutex();
+            MainStartup.ReleaseMutex();
 
             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>
         /// Opens the dashboard page.
         /// </summary>
@@ -281,9 +177,9 @@ namespace MediaBrowser.ServerApplication
         /// Restarts this instance.
         /// </summary>
         /// <exception cref="System.NotImplementedException"></exception>
-        public void Restart()
+        public void RestartApplication()
         {
-            Dispatcher.Invoke(ReleaseMutex);
+            Dispatcher.Invoke(MainStartup.ReleaseMutex);
 
             CompositionRoot.Dispose();
 

+ 22 - 13
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;
+
         /// <summary>
         /// The full path to our startmenu shortcut
         /// </summary>
@@ -194,6 +199,11 @@ namespace MediaBrowser.ServerApplication
 
         private Task<IHttpServer> _httpServerCreationTask;
 
+        public ApplicationHost(IApplicationInterface appInterface)
+        {
+            _appInterface = appInterface;
+        }
+
         /// <summary>
         /// Runs the startup tasks.
         /// </summary>
@@ -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
         /// <summary>
         /// Restarts this instance.
         /// </summary>
-        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();
         }
 
         /// <summary>
@@ -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
         /// <summary>
         /// Shuts down.
         /// </summary>
-        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();
         }
 
         /// <summary>

+ 30 - 0
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();
+        }
+    }
+}

+ 4 - 1
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");
+                }
             }
         }
 

+ 32 - 0
MediaBrowser.ServerApplication/IApplicationInterface.cs

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

+ 134 - 0
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
+    {
+        /// <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;
+        }
+    }
+}

+ 7 - 7
MediaBrowser.ServerApplication/MainWindow.xaml.cs

@@ -119,7 +119,7 @@ namespace MediaBrowser.ServerApplication
 
             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))
                 {
@@ -204,7 +204,7 @@ namespace MediaBrowser.ServerApplication
         {
             App.OpenUrl("https://github.com/MediaBrowser/MediaBrowser/wiki");
         }
-        
+
         /// <summary>
         /// Occurs when [property changed].
         /// </summary>
@@ -258,7 +258,7 @@ namespace MediaBrowser.ServerApplication
         {
             App.OpenDashboardPage("dashboard.html", loggedInUser, _configurationManager, _appHost);
         }
-        
+
         /// <summary>
         /// Handles the click event of the cmVisitCT control.
         /// </summary>
@@ -285,9 +285,9 @@ namespace MediaBrowser.ServerApplication
         /// </summary>
         /// <param name="sender">The source of the event.</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>
@@ -295,9 +295,9 @@ namespace MediaBrowser.ServerApplication
         /// </summary>
         /// <param name="sender">The source of the event.</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>

+ 7 - 1
MediaBrowser.ServerApplication/MediaBrowser.ServerApplication.csproj

@@ -57,7 +57,7 @@
     <WarningLevel>4</WarningLevel>
   </PropertyGroup>
   <PropertyGroup>
-    <StartupObject>MediaBrowser.ServerApplication.App</StartupObject>
+    <StartupObject>MediaBrowser.ServerApplication.MainStartup</StartupObject>
   </PropertyGroup>
   <PropertyGroup>
     <ApplicationIcon>Resources\Images\icon.ico</ApplicationIcon>
@@ -187,6 +187,7 @@
     <Reference Include="System.Net.Http" />
     <Reference Include="System.Net.Http.WebRequest" />
     <Reference Include="System.Runtime.Remoting" />
+    <Reference Include="System.ServiceProcess" />
     <Reference Include="System.Windows.Forms" />
     <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>
@@ -203,8 +204,13 @@
     <Reference Include="PresentationFramework" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="BackgroundService.cs">
+      <SubType>Component</SubType>
+    </Compile>
     <Compile Include="EntryPoints\StartupWizard.cs" />
     <Compile Include="EntryPoints\UdpServerEntryPoint.cs" />
+    <Compile Include="IApplicationInterface.cs" />
+    <Compile Include="MainStartup.cs" />
     <Compile Include="Splash\SplashWindow.xaml.cs">
       <DependentUpon>SplashWindow.xaml</DependentUpon>
     </Compile>