/// <summary> /// Loads the Resource from the specified <see cref="Stream"/>. You shouldn't need this method in almost all cases. /// Only use it when you know exactly what you're doing. Consider requesting the Resource from the <see cref="ContentProvider"/> instead. /// </summary> /// <typeparam name="T"> /// Desired Type of the returned reference. Does not affect the loaded Resource in any way - it is simply returned as T. /// Results in returning null if the loaded Resource's Type isn't assignable to T. /// </typeparam> /// <param name="str">The stream to load the Resource from.</param> /// <param name="resPath">The path that is assumed as the loaded Resource's origin.</param> /// <param name="loadCallback">An optional callback that is invoked right after loading the Resource, but before initializing it.</param> /// <param name="initResource"> /// Specifies whether or not the Resource is initialized by calling <see cref="Resource.OnLoaded"/>. Never attempt to use /// uninitialized Resources or register them in the <see cref="ContentProvider"/>. /// </param> /// <returns>The Resource that has been loaded.</returns> public static T Load <T>(Serializer formatter, string resPath = null, Action <T> loadCallback = null, bool initResource = true) where T : Resource { T newContent = null; try { Resource res = formatter.ReadObject <Resource>(); if (res == null) { throw new Exception("Deserializing Resource failed."); } res.initState = InitState.Initializing; res.path = resPath; if (loadCallback != null) { loadCallback(res as T); // Callback before initializing. } if (initResource) { Init(res); } newContent = res as T; } catch (Exception e) { Logs.Core.WriteError("Can't load {0} from '{1}', because an error occurred: {3}{2}", LogFormat.Type(typeof(T)), resPath ?? formatter.ToString(), LogFormat.Exception(e), Environment.NewLine); } return(newContent); }
/// <summary> /// Disposes all loaded plugins and discards all related content / data. /// </summary> public void ClearPlugins() { T[] oldPlugins = this.LoadedPlugins.ToArray(); if (oldPlugins.Length == 0) { return; } foreach (T plugin in oldPlugins) { this.disposedPlugins.Add(plugin.PluginAssembly); } this.OnPluginsRemoving(oldPlugins); foreach (T plugin in oldPlugins) { try { plugin.Dispose(); } catch (Exception e) { this.pluginLog.WriteError("Error disposing plugin {1}: {0}", LogFormat.Exception(e), plugin.AssemblyName); } } this.OnPluginsRemoved(oldPlugins); this.pluginRegistry.Clear(); }
protected T LoadPlugin(string pluginFilePath) { // Check for already loaded plugins first string asmName = PathOp.GetFileNameWithoutExtension(pluginFilePath); T plugin = this.pluginRegistry.Values.FirstOrDefault(p => p.AssemblyName == asmName); if (plugin != null) { return(plugin); } // Load the assembly from the specified path Assembly pluginAssembly = null; try { pluginAssembly = this.assemblyLoader.LoadAssembly(pluginFilePath); } catch (Exception e) { this.pluginLog.WriteError("Error loading plugin Assembly: {0}", LogFormat.Exception(e)); plugin = null; } // If we succeeded, register the loaded assembly as a plugin if (pluginAssembly != null) { plugin = this.LoadPlugin(pluginAssembly, pluginFilePath); } return(plugin); }
internal static void ShutdownBackend <T>(ref T backend) where T : class, ICoheeBackend { if (backend == null) { return; } Logs.Core.Write("Shutting down {0}...", backend.Name); Logs.Core.PushIndent(); { try { backend.Shutdown(); TypeInfo backendType = backend.GetType().GetTypeInfo(); corePluginManager.UnlockPlugin(backendType.Assembly); backend = null; } catch (Exception e) { Logs.Core.WriteError("Failed: {0}", LogFormat.Exception(e)); } } Logs.Core.PopIndent(); }
/// <summary> /// Returns a string that can be used for representing a <see cref="System.Reflection.TypeInfo"/> in log entries. /// </summary> /// <param name="type"></param> /// <returns></returns> public static string Type(TypeInfo type) { if (type == null) { return("null"); } return(LogFormat.Type(type.AsType())); }
/// <summary> /// Reloads the specified plugin. Does not initialize it. /// </summary> /// <param name="pluginFilePath"></param> public T ReloadPlugin(string pluginFilePath) { // If we're trying to reload an active backend plugin, stop foreach (var pair in this.pluginRegistry) { T plugin = pair.Value; if (PathOp.ArePathsEqual(plugin.FilePath, pluginFilePath)) { foreach (Assembly lockedAssembly in this.lockedPlugins) { if (plugin.PluginAssembly == lockedAssembly) { this.pluginLog.WriteError( "Can't reload plugin {0}, because it has been locked by the runtime. " + "This usually happens for plugins that implement a currently active backend.", LogFormat.Assembly(lockedAssembly)); return(null); } } break; } } // Load the updated plugin Assembly Assembly pluginAssembly = null; try { pluginAssembly = this.assemblyLoader.LoadAssembly(pluginFilePath); } catch (Exception e) { this.pluginLog.WriteError("Error loading plugin Assembly: {0}", LogFormat.Exception(e)); return(null); } // If we're overwriting an old plugin here, add the old version to the "disposed" blacklist string assemblyName = pluginAssembly.GetShortAssemblyName(); T oldPlugin; if (this.pluginRegistry.TryGetValue(assemblyName, out oldPlugin)) { this.pluginRegistry.Remove(assemblyName); this.disposedPlugins.Add(oldPlugin.PluginAssembly); this.OnPluginsRemoving(new[] { oldPlugin }); oldPlugin.Dispose(); } // Load the new plugin from the updated Assembly T updatedPlugin = this.LoadPlugin(pluginAssembly, pluginFilePath); // Discard temporary plugin-related data (cached Types, etc.) this.OnPluginsRemoved(new[] { oldPlugin }); return(updatedPlugin); }
/// <summary> /// Initializes the specified plugin. This concludes a manual plugin load or reload operation /// using API like <see cref="LoadPlugin(Assembly, string)"/> and <see cref="ReloadPlugin"/>. /// </summary> /// <param name="plugin"></param> public void InitPlugin(T plugin) { try { this.OnInitPlugin(plugin); this.OnPluginsReady(new[] { plugin }); } catch (Exception e) { this.pluginLog.WriteError("Error initializing plugin {1}: {0}", LogFormat.Exception(e), plugin.AssemblyName); this.RemovePlugin(plugin); } }
public override string ToString() { if (this.SkipIfExists > 0) { return(string.Format("Skip {0} if {1} exists", this.SkipIfExists, LogFormat.Type(this.RequiredType))); } else { return(string.Format("Require {0} or create {1}", LogFormat.Type(this.RequiredType), LogFormat.Type(this.CreateType))); } }
/// <summary> /// Adds an already loaded plugin Assembly to the internal Cohee T registry. /// You shouldn't need to call this method in general, since Cohee manages its plugins /// automatically. /// </summary> /// <remarks> /// This method can be useful in certain cases when it is necessary to treat an Assembly as a /// Cohee plugin, even though it isn't located in the Plugins folder, or is not available /// as a file at all. A typical case for this is Unit Testing where the testing Assembly may /// specify additional Duality types such as Components, Resources, etc. /// </remarks> /// <param name="pluginAssembly"></param> /// <param name="pluginFilePath"></param> /// <returns></returns> public T LoadPlugin(Assembly pluginAssembly, string pluginFilePath) { this.disposedPlugins.Remove(pluginAssembly); string asmName = pluginAssembly.GetShortAssemblyName(); T plugin = this.pluginRegistry.Values.FirstOrDefault(p => p.AssemblyName == asmName); if (plugin != null) { return(plugin); } try { TypeInfo pluginType = pluginAssembly.ExportedTypes .Select(t => t.GetTypeInfo()) .FirstOrDefault(t => typeof(T).GetTypeInfo().IsAssignableFrom(t)); if (pluginType == null) { throw new Exception(string.Format( "Plugin does not contain a public {0} class.", typeof(T).Name)); } plugin = (T)pluginType.CreateInstanceOf(); if (plugin == null) { throw new Exception(string.Format( "Failed to instantiate {0} class.", LogFormat.Type(pluginType.GetType()))); } plugin.FilePath = pluginFilePath; plugin.FileHash = this.assemblyLoader.GetAssemblyHash(pluginFilePath); this.pluginRegistry.Add(plugin.AssemblyName, plugin); } catch (Exception e) { this.pluginLog.WriteError("Error loading plugin: {0}", LogFormat.Exception(e)); this.disposedPlugins.Add(pluginAssembly); plugin = null; } return(plugin); }
/// <summary> /// Returns a string that can be used for representing an exception in log entries. /// It usually does not include the full call stack and is significantly shorter than /// an <see cref="System.Exception">Exceptions</see> ToString method. /// </summary> /// <param name="e"></param> /// <returns></returns> public static string Exception(Exception e, bool callStack = true) { if (e == null) { return("null"); } string eName = LogFormat.Type(e.GetType()); return(string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}: {1}{3}CallStack:{3}{2}", eName, e.Message, e.StackTrace, Environment.NewLine)); }
protected void RemovePlugin(T plugin) { // Dispose plugin and discard plugin related data this.disposedPlugins.Add(plugin.PluginAssembly); this.OnPluginsRemoving(new[] { plugin }); this.pluginRegistry.Remove(plugin.AssemblyName); try { plugin.Dispose(); } catch (Exception e) { this.pluginLog.WriteError("Error disposing plugin {1}: {0}", LogFormat.Exception(e), plugin.AssemblyName); } // Discard temporary plugin-related data (cached Types, etc.) this.OnPluginsRemoved(new[] { plugin }); }
public void EnsureCreationChain(ComponentRequirementMap map) { if (this.initCreationChain == RecursiveInit.Initialized) { return; } if (this.initCreationChain == RecursiveInit.InProgress) { Logs.Core.WriteWarning( "Detected a cyclic Component requirement in {0}. Requirements can not be ensured for cyclic dependencies.", LogFormat.Type(this.Component)); return; } this.initCreationChain = RecursiveInit.InProgress; this.InitCreationChain(map); this.initCreationChain = RecursiveInit.Initialized; }
private bool CheckedOnSaved(string saveAsPath) { if (this.initState != InitState.Initialized) { return(true); } try { this.OnSaved(saveAsPath); if (ResourceSaved != null) { ResourceSaved(this, new ResourceSaveEventArgs(this, saveAsPath)); } return(true); } catch (Exception e) { Logs.Core.WriteError("OnSaved() of {0} failed: {1}", this, LogFormat.Exception(e)); return(false); } }
/// <summary> /// Clears the specified constraint graph of all loops. /// </summary> /// <param name="graph"></param> private static void ResolveConstraintLoops(Dictionary <Type, List <OrderConstraint> > graph) { while (true) { List <OrderConstraint> loop = FindConstraintLoop(graph); if (loop == null) { return; } // Found a loop? Find the weakest link in it OrderConstraint weakestLink = loop[0]; for (int i = 1; i < loop.Count; i++) { OrderConstraint link = loop[i]; if ((int)link.Priority < (int)weakestLink.Priority) { weakestLink = link; } } // If the loops weakest link was an explicit constraint, log a warning if ((int)weakestLink.Priority >= (int)ConstraintPriority.ExplicitWeak) { Logs.Core.WriteWarning( "Found a loop in the component execution order constraint graph. Ignoring the weakest constraint " + "({0} must be executed before {1}). Please check your ExecutionOrder attributes.", LogFormat.Type(weakestLink.FirstType), LogFormat.Type(weakestLink.LastType)); } // Remove the weakest link List <OrderConstraint> links = graph[weakestLink.FirstType]; links.Remove(weakestLink); if (links.Count == 0) { graph.Remove(weakestLink.FirstType); } } }
private string FormatMessage(string format, object[] obj) { if (obj == null || obj.Length == 0) { return(format); } string msg; try { msg = string.Format(System.Globalization.CultureInfo.InvariantCulture, format, obj); } catch (Exception e) { // Don't allow log message formatting to throw unhandled exceptions, // because they would result in another log - and probably more exceptions. // Instead, embed format, arguments and the exception in the resulting // log message, so the user can retrieve all necessary information for // fixing his log call. msg = format + Environment.NewLine; if (obj != null) { try { msg += obj.ToString(", ") + Environment.NewLine; } catch (Exception) { msg += "(Error in ToString call)" + Environment.NewLine; } } msg += LogFormat.Exception(e); } return(msg); }
/// <summary> /// Returns an existing <see cref="ProfileCounter"/> with the specified name. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="name">The <see cref="ProfileCounter"/> name to use for this measurement. For nested measurements, use path strings, e.g. "ParentCounter\ChildCounter"</param> /// <returns></returns> public static T GetCounter <T>(string name) where T : ProfileCounter { if (name == null) { return(null); } ProfileCounter c; if (!counterMap.TryGetValue(name, out c)) { return(null); } T cc = c as T; if (cc == null) { throw new InvalidOperationException(string.Format("The specified performance counter '{0}' is not a {1}.", name, LogFormat.Type(typeof(T)))); } return(cc); }
internal static void InitBackend <T>(out T target, Func <Type, IEnumerable <TypeInfo> > typeFinder = null) where T : class, ICoheeBackend { if (typeFinder == null) { typeFinder = GetAvailCoheeTypes; } Logs.Core.Write("Initializing {0}...", LogFormat.Type(typeof(T))); Logs.Core.PushIndent(); // Generate a list of available backends for evaluation List <ICoheeBackend> backends = new List <ICoheeBackend>(); foreach (TypeInfo backendType in typeFinder(typeof(ICoheeBackend))) { if (backendType.IsInterface) { continue; } if (backendType.IsAbstract) { continue; } if (!backendType.IsClass) { continue; } if (!typeof(T).GetTypeInfo().IsAssignableFrom(backendType)) { continue; } ICoheeBackend backend = backendType.CreateInstanceOf() as ICoheeBackend; if (backend == null) { Logs.Core.WriteWarning("Unable to create an instance of {0}. Skipping it.", backendType.FullName); continue; } backends.Add(backend); } // Sort backends from best to worst backends.StableSort((a, b) => b.Priority > a.Priority ? 1 : -1); // Try to initialize each one and select the first that works T selectedBackend = null; foreach (T backend in backends) { if (appData != null && appData.SkipBackends != null && appData.SkipBackends.Any(s => string.Equals(s, backend.Id, StringComparison.OrdinalIgnoreCase))) { Logs.Core.Write("Backend '{0}' skipped because of AppData settings.", backend.Name); continue; } bool available = false; try { available = backend.CheckAvailable(); if (!available) { Logs.Core.Write("Backend '{0}' reports to be unavailable. Skipping it.", backend.Name); } } catch (Exception e) { available = false; Logs.Core.WriteWarning("Backend '{0}' failed the availability check with an exception: {1}", backend.Name, LogFormat.Exception(e)); } if (!available) { continue; } Logs.Core.Write("{0}...", backend.Name); Logs.Core.PushIndent(); { try { backend.Init(); selectedBackend = backend; } catch (Exception e) { Logs.Core.WriteError("Failed: {0}", LogFormat.Exception(e)); } } Logs.Core.PopIndent(); if (selectedBackend != null) { break; } } // If we found a proper backend and initialized it, add it to the list of active backends if (selectedBackend != null) { target = selectedBackend; TypeInfo selectedBackendType = selectedBackend.GetType().GetTypeInfo(); corePluginManager.LockPlugin(selectedBackendType.Assembly); } else { target = null; } Logs.Core.PopIndent(); }
private static CreateMethod CreateObjectActivator(TypeInfo typeInfo, out object firstResult) { Exception lastError = null; CreateMethod activator; firstResult = null; // Filter out non-instantiatable Types if (typeInfo.IsAbstract || typeInfo.IsInterface || typeInfo.IsGenericTypeDefinition) { activator = nullObjectActivator; } // If the caller wants a string, just return an empty one else if (typeInfo.AsType() == typeof(string)) { activator = () => ""; } // If the caller wants an array, create an empty one else if (typeInfo.IsArray && typeInfo.GetArrayRank() == 1) { activator = () => Array.CreateInstance(typeInfo.GetElementType(), 0); } // For structs, boxing a default(T) is sufficient else if (typeInfo.IsValueType) { var lambda = Expression.Lambda<CreateMethod>(Expression.Convert(Expression.Default(typeInfo.AsType()), typeof(object))); activator = lambda.Compile(); } else { activator = nullObjectActivator; // Retrieve constructors, sorted from trivial to parameter-rich ConstructorInfo[] constructors = typeInfo.DeclaredConstructors .Where(c => !c.IsStatic) .Select(c => new { Info = c, ParamCount = c.GetParameters().Length }) .OrderBy(s => s.ParamCount) .Select(s => s.Info) .ToArray(); foreach (ConstructorInfo con in constructors) { // Prepare constructor argument values - just use default(T) for all of them. ParameterInfo[] conParams = con.GetParameters(); Expression[] args = new Expression[conParams.Length]; for (int i = 0; i < args.Length; i++) { Type paramType = conParams[i].ParameterType; args[i] = Expression.Default(paramType); } // Compile a lambda method invoking the constructor var lambda = Expression.Lambda<CreateMethod>(Expression.New(con, args)); activator = lambda.Compile(); // Does it work? firstResult = CheckActivator(activator, out lastError); if (firstResult != null) break; } // If there were no suitable constructors, log a generic warning. if (constructors.Length == 0) { Logs.Core.WriteWarning( "Failed to create object of Type {0}. Make sure there is a trivial constructor.", LogFormat.Type(typeInfo)); } } // Test whether our activation method really works, replace with dummy if not if (firstResult == null) { // If we didn't yet try to create an object instance or value, do it now. if (lastError == null) { firstResult = CheckActivator(activator, out lastError); } // If there was an error / Exception thrown while creating the object, inform someone. if (lastError != null) { // If it's a problem in a static constructor, get the inner exception to know what's actually wrong. if (lastError is TypeInitializationException) { Logs.Core.WriteError("Failed to initialize Type {0}: {1}", LogFormat.Type(typeInfo), LogFormat.Exception(lastError.InnerException)); } // Otherwise, just do a regular error log. else { Logs.Core.WriteError("Failed to create object of Type {0}: {1}", LogFormat.Type(typeInfo), LogFormat.Exception(lastError)); } } } // If we still don't have anything, just use a dummy. if (firstResult == null) activator = nullObjectActivator; return activator; }
private static Resource ResolveContent(string path) { if (CoheeApp.ExecContext == CoheeApp.ExecutionContext.Terminated) { return(null); } if (string.IsNullOrEmpty(path) || ResourceResolve == null) { return(null); } ResourceResolveEventArgs args = new ResourceResolveEventArgs(path); try { ResourceResolve(null, args); } catch (Exception e) { Logs.Core.WriteError("An error occurred in custom ResourceResolve code: {0}", LogFormat.Exception(e)); } if (args.Handled) { if (string.IsNullOrEmpty(args.Result.Path)) { args.Result.Path = path; } AddContent(path, args.Result); return(args.Result); } else { return(null); } }
/// <summary> /// Initializes this CoheeApp. Should be called before performing any operations within Cohee. /// </summary> /// <param name="context">The <see cref="ExecutionContext"/> in which Duality runs.</param> /// <param name="commandLineArgs"> /// Command line arguments to run this CoheeApp with. /// Usually these are just the ones from the host application, passed on. /// </param> public static void Init(ExecutionEnvironment env, ExecutionContext context, IAssemblyLoader assemLoader, string[] commandLineArgs) { if (initialized) { return; } // Process command line options if (commandLineArgs != null) { // Enter debug mode if (commandLineArgs.Contains(CmdArgDebug)) { System.Diagnostics.Debugger.Launch(); } // Run from editor if (commandLineArgs.Contains(CmdArgEditor)) { runFromEditor = true; } } // If the core was compiled in debug mode and a debugger is attached, log // to the Debug channel, so we can put the VS output window to good use. #if DEBUG bool isDebugging = System.Diagnostics.Debugger.IsAttached; if (isDebugging) { // Only add a new Debug output if we don't already have one, and don't // log to a Console channel either. VS will automatically redirect Console // output to the Output window when debugging a non-Console application, // and we don't want to end up with double log entries. bool hasDebugOut = Logs.GlobalOutput.OfType <DebugLogOutput>().Any(); bool hasConsoleOut = Logs.GlobalOutput.OfType <TextWriterLogOutput>().Any(w => w.GetType().Name.Contains("Console")); if (!hasDebugOut && !hasConsoleOut) { Logs.AddGlobalOutput(new DebugLogOutput()); } } #endif environment = env; execContext = context; // Initialize the plugin manager { assemblyLoader = assemLoader ?? new Cohee.Backend.Dummy.DummyAssemblyLoader(); Logs.Core.Write("Using '{0}' to load plugins.", assemblyLoader.GetType().Name); assemblyLoader.Init(); // Log assembly loading data for diagnostic purposes { Logs.Core.Write("Currently Loaded Assemblies:" + Environment.NewLine + "{0}", assemblyLoader.LoadedAssemblies.ToString( assembly => " " + LogFormat.Assembly(assembly), Environment.NewLine)); Logs.Core.Write("Plugin Base Directories:" + Environment.NewLine + "{0}", assemblyLoader.BaseDirectories.ToString( path => " " + path, Environment.NewLine)); Logs.Core.Write("Available Assembly Paths:" + Environment.NewLine + "{0}", assemblyLoader.AvailableAssemblyPaths.ToString( path => " " + path, Environment.NewLine)); } corePluginManager.Init(assemblyLoader); corePluginManager.PluginsRemoving += pluginManager_PluginsRemoving; corePluginManager.PluginsRemoved += pluginManager_PluginsRemoved; } // Load all plugins. This needs to be done first, so backends and Types can be located. corePluginManager.LoadPlugins(); // Initialize the system backend for system info and file system access InitBackend(out systemBackend); // Load application and user data and submit a change event, so all settings are applied LoadAppData(); LoadUserData(); OnAppDataChanged(); OnUserDataChanged(); // Initialize the graphics backend InitBackend(out graphicsBackend); // Initialize the audio backend InitBackend(out audioBackend); //sound = new SoundDevice(); // Initialize all core plugins, this may allocate Resources or establish references between plugins corePluginManager.InitPlugins(); initialized = true; // Write environment specs as a debug log Logs.Core.Write( "DualityApp initialized" + Environment.NewLine + "Debug Mode: {0}" + Environment.NewLine + "Command line arguments: {1}", System.Diagnostics.Debugger.IsAttached, commandLineArgs != null ? commandLineArgs.ToString(", ") : "null"); }