using System; using System.Collections.Generic; using System.Diagnostics; using System.Threading; using System.Windows.Forms; using JD.Kernel.Classes; using JD.Kernel.StaticHelpers; using JD.LogService; using JD.TeaBoxAddinSystem.Addins; using JD.TeaBoxAddinSystem.Addins.MEF; using JD.TeaBoxAddinSystem.Addins.MEF.Integration; using JD.Threading; using Message = JD.LogService.Message; namespace JD.TeaBoxAddinSystem.Bootstrap { /// /// Base class for objects used to bootstrap applications. /// public class ApplicationStartup { #region Variables internal const string CoreBootstrapPath = "Bootstrap/All"; private readonly Arguments m_arguments; private readonly string m_applicationName; private readonly RuntimeMode m_runtimeMode = RuntimeMode.Normal; private readonly ApplicationConfiguration m_configuration; private readonly LoggingContext m_bootstrapLoggingContext = new LoggingContext(); private static ApplicationStartup m_instance; private IDisposable m_splashScreenHandle; // Metrics private StartupStatistic m_startupStatistic; #endregion #region Constructor /// /// Constructor. /// /// Application Arguments. /// Application name. /// Runtime mode. public ApplicationStartup(string[] arguments, string applicationName, RuntimeMode runtimeMode) { if (string.IsNullOrEmpty(applicationName)) { applicationName = (Runtime.EntryAssembly != null ? Runtime.EntryAssembly.FullName : Runtime.ApplicationName); } m_arguments = new Arguments(arguments); m_applicationName = applicationName; m_configuration = new ApplicationConfiguration(ApplicationName); m_runtimeMode = runtimeMode; } #endregion #region Properties /// /// Unique instance of the started application startup. Not for external use. Back off. /// /// /// Consider using instead. /// public static ApplicationStartup Instance { get { return m_instance; } } /// /// Arguments. /// public Arguments Arguments { get { return m_arguments; } } /// /// Application Name. /// public string ApplicationName { get { return m_applicationName; } } /// /// Whether the application was started in embedded mode. /// public RuntimeMode RuntimeMode { get { return m_runtimeMode; } } internal ApplicationConfiguration Configuration { get { return m_configuration; } } /// /// Returns true if this application startup forces the application in command-line mode. /// protected virtual bool ForceCommandLineMode { get { return false; } } /// /// Addin Logging Context. /// public LoggingContext BootstrapLoggingContext { get { return m_bootstrapLoggingContext; } } #endregion #region Methods #region Main Methods /// /// Starts the application (must be in embedded mode). /// /// Dispose the result to quit the application. public IDisposable StartEmbedded() { Throw.Assert(RuntimeMode == RuntimeMode.EmbeddedInProcess || RuntimeMode == RuntimeMode.SystemTests); Start(); return new DisposableHelper(Quit); } /// /// Starts the application. /// /// Exit code for the application. public int Start() { if (Runtime.Initialized) { throw new InvalidOperationException("Cannot start an ApplicationStartup twice in a same AppDomain"); } if (string.IsNullOrEmpty(Thread.CurrentThread.Name)) { Thread.CurrentThread.Name = "Main Thread"; } Debug.Assert(m_instance == null); m_instance = this; m_startupStatistic = new StartupStatistic(); if (ForceCommandLineMode == false) { Idle.Event += Application_Idle; } #if DEBUG if (Debugger.IsAttached) { return StartImpl(); } #endif return StartImpl(); } private int StartImpl() { #if DEBUG Control.CheckForIllegalCrossThreadCalls = true; #endif // To have invariant type conversion ThreadHelper.SetDefaultThreadCulture(Thread.CurrentThread); LogService.LogService.LogMessage("Starting {0}...", ApplicationName); ConfigureInternal(); // Initialize the runtime. LogService.LogService.LogMessage("Initializing runtime..."); Runtime.Initialize(this, m_bootstrapLoggingContext); LogService.LogService.LogMessage("Starting core services..."); StartCoreServices(); LogService.LogService.LogMessage("Running..."); return Run(); } private void ConfigureInternal() { Configure(m_configuration); } /// /// Starts the main services /// protected virtual void StartCoreServices() { StringParserService.StringParserService.Properties["ApplicationName"] = ApplicationName; } /// /// Configures the application, using the given . /// /// protected virtual void Configure(ApplicationConfiguration configuration) { } /// /// Initializes and processes the command-line arguments /// /// Thrown when some arguments are invalid. private void InitializeCommandLineArguments() { Runtime.CommandLineMode = ShouldStartInCommandLineMode(Runtime.CommandLineHandlerPaths); Runtime.CommandLineMode |= ForceCommandLineMode; } private bool ShouldStartInCommandLineMode(IEnumerable handlersPath) { bool commandLineMode = ForceCommandLineMode; foreach (string path in handlersPath) { foreach (object obj in AddinManager.GetExtensionObjects(path)) { ICommandLineHandler handler = obj as ICommandLineHandler; if (handler != null && handler.Initialize()) { commandLineMode = true; } } } return commandLineMode; } private void BootstrapAddinEngine() { m_bootstrapLoggingContext.MessageAdded += delegate(object sender, ItemEventArgs e) { LogService.LogService.LogMessage(e.Item); }; m_bootstrapLoggingContext.WarningAdded += delegate(object sender, ItemEventArgs e) { LogService.LogService.LogWarning(e.Item); }; m_bootstrapLoggingContext.ErrorAdded += delegate(object sender, ItemEventArgs e) { LogService.LogService.LogError(e.Item); }; AddinManager.Loggers.Add(m_bootstrapLoggingContext); Runtime.AddinRepository.Update(m_bootstrapLoggingContext); Runtime.AddinRepository.Load(); } private void BootstrapMEFAddinEngine() { AddinRepository repository = new AddinRepository(Runtime.ApplicationRootPath, m_bootstrapLoggingContext); repository.Load(); var service = new MEFAddinService(repository, m_bootstrapLoggingContext); AddinService.Use(service); } private int Run() { LogService.LogService.LogMessage("Initializing Addin Tree subsystem"); var stopwatch = Stopwatch.StartNew(); BootstrapAddinEngine(); LogService.LogService.LogMessage($"Addin Tree subsystem initialized in {stopwatch.Elapsed.TotalSeconds}s"); LogService.LogService.LogMessage("Initializing MEF subsystem"); stopwatch.Restart(); BootstrapMEFAddinEngine(); LogService.LogService.LogMessage($"MEF subsystem initialized in {stopwatch.Elapsed.TotalSeconds}s"); try { LogService.LogService.LogMessage("Parsing command-line arguments..."); InitializeCommandLineArguments(); ShowSplashScreen(); int exitCode; OnStarting(out exitCode); if (exitCode != 0) { return exitCode; } // run bootstrap commands LogService.LogService.LogMessage("Running Bootstrap commands..."); var coreBootstrap = new BootstrapCommandRunner(AddinManager.GetExtensionObjects(CoreBootstrapPath)); coreBootstrap.Run(); var addinBootstrap = new BootstrapCommandRunner(AddinManager.GetExtensionObjects(m_configuration.BootstrapAddinPath)); addinBootstrap.Run(); if (Runtime.RuntimeMode != RuntimeMode.Normal) { OnAfterApplicationStarted(); } else { Quit(); } return Runtime.ExitCode; } catch (CommandLineArgumentException e) { LogService.LogService.LogError("Error in arguments: {0}", e.Message); Console.Error.WriteLine(e.Message); return -1; } } /// /// Should not be called directly. To quit properly the application, use Application.Quit(). /// /// Exit code private void Quit() { LogService.LogService.LogMessage("Leaving application..."); OnQuitting(); OnQuitted(EventArgs.Empty); } private void ShowSplashScreen() { #if !DEBUG bool noLogo = Arguments["nologo"].Count > 0; if (!noLogo && !Runtime.CommandLineMode && Runtime.ShouldStartOwnInstance) { Idle.Event -= Application_Idle; m_splashScreenHandle = OnShowSplashScreen(); Application.DoEvents(); Idle.Event += Application_Idle; } #endif } protected virtual IDisposable OnShowSplashScreen() { return null; } /// /// Called just before the application starts. /// protected virtual void OnStarting(out int exitCode) { PropertyService.PropertyService.Load(); MessageService.MessageService.Initialize(); exitCode = 0; } /// /// Called just before the application quits. /// protected virtual void OnQuitting() { PropertyService.PropertyService.Close(); MessageService.MessageService.Close(); DisposableHelper.SafeDispose(ref m_splashScreenHandle); } private void OnAfterApplicationStarted() { m_startupStatistic.MemoryUsedAfterStartUp = Process.GetCurrentProcess().PrivateMemorySize64; m_startupStatistic.Dispose(); } #endregion #region Event Handlers void Application_Idle(object sender, EventArgs e) { Idle.Event -= Application_Idle; DisposableHelper.SafeDispose(ref m_splashScreenHandle); OnAfterApplicationStarted(); } #endregion #endregion #region Events public event EventHandler Quitted; private void OnQuitted(EventArgs e) { Quitted?.Invoke(this, e); } #endregion } }