using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; using WindowsDisplayAPI; using WindowsDisplayAPI.DisplayConfig; using NvAPIWrapper.GPU; using NvAPIWrapper.Mosaic; using AudioSwitcher.AudioApi.CoreAudio; using DisplayMagicianShared; namespace DisplayMagicianLogReporter { internal class Program { private static StreamWriter _writer; internal static string AppDataPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "DisplayMagician"); internal static string LoggingDirPath = Path.Combine(AppDataPath, "Logs"); private static void DumpObject( IEnumerable items, string title, Tuple, string>[] actions = null, int deepIn = 0) { Console.Write($"- {title}..."); _writer.WriteLine(title + new string('=', Console.BufferWidth - title.Length)); var totalTime = TimeSpan.Zero; var stopWatch = new Stopwatch(); stopWatch.Start(); foreach (var item in items) { var actionResults = actions?.Select(tuple => new Tuple(tuple.Item2, tuple.Item1.Invoke(item))).ToArray(); stopWatch.Stop(); WriteObject(item, 0, stopWatch.Elapsed, actionResults, deepIn); totalTime += stopWatch.Elapsed; stopWatch.Reset(); stopWatch.Start(); } stopWatch.Stop(); totalTime += stopWatch.Elapsed; Console.WriteLine(@"(Took {0} to complete)", totalTime); _writer.WriteLine(@"-- Total Elapsed: {0}", totalTime); _writer.WriteLine(); _writer.WriteLine(new string('#', Console.BufferWidth)); _writer.WriteLine(); } private static void IngestFile(string fileName, string title) { Console.Write($"- {title}..."); _writer.WriteLine(new string('=', Console.BufferWidth)); _writer.WriteLine(title + new string('=', Console.BufferWidth - title.Length)); _writer.WriteLine(new string('=', Console.BufferWidth)); var totalTime = TimeSpan.Zero; var stopWatch = new Stopwatch(); if (File.Exists(fileName)) { stopWatch.Start(); StreamReader reader = new StreamReader(fileName); string fileContents = reader.ReadToEnd(); _writer.WriteLine(fileContents); stopWatch.Stop(); totalTime += stopWatch.Elapsed; Console.WriteLine(@"(Took {0} to complete)", totalTime); _writer.WriteLine(@"-- Total Elapsed: {0}", totalTime); _writer.WriteLine(); _writer.WriteLine(new string('#', Console.BufferWidth)); _writer.WriteLine(); } else { } } private static void Main(string[] args) { // This sets the Application User Model ID to "LittleBitBig.DisplayMagician" so that // Windows 10 recognises the application, and allows features such as Toasts, // taskbar pinning and similar. // This is a helper function that sets the AUMID to a static default defined // within ShellUtils under the DisplayMagicianShared project. ShellUtils.SetDefaultProcessExplicitAppUserModelID(); Console.WriteLine("DisplayMagician LogReporter"); Console.WriteLine("======================"); Console.WriteLine(); Console.WriteLine("This program will interrogate your DisplayMagician configuration settings, the Windows Display "); Console.WriteLine("Subsystem and any NVIDIA Display Drivers to record what they can detect and report. This"); Console.WriteLine("recorded information is then stored in a log file. If you report a problem then you may be"); Console.WriteLine("asked to send us the log file generated by this program in order for us to help you."); Console.WriteLine(); Console.WriteLine("Starting the interrogation..."); Console.WriteLine(); try { Directory.CreateDirectory(LoggingDirPath); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"ERROR - Cannot create logging directory as unauthorised: {LoggingDirPath}"); } catch (ArgumentException ex) { Console.WriteLine($"ERROR - Cannot create logging directory as LoggingDirPath argument caused an ArgumentException: {LoggingDirPath}"); } catch (PathTooLongException ex) { Console.WriteLine($"ERROR - Cannot create logging directory as the path is too long for Windows to create: {LoggingDirPath}"); } catch (DirectoryNotFoundException ex) { Console.WriteLine($"ERROR - Cannot create logging directory as the DisplayMagician Local Application Data directory doesn't exist: {LoggingDirPath}"); } catch (NotSupportedException ex) { Console.WriteLine($"ERROR - Cannot create logging directory as the Directory.CreateDirectory function isn't supported: {LoggingDirPath}"); } string date = DateTime.Now.ToString("yyyyMMdd.HHmmss"); string logFile = Path.Combine(LoggingDirPath, $"LogReporter.{date}.log"); try { _writer = new StreamWriter(new FileStream( logFile, FileMode.CreateNew)); } catch (System.Security.SecurityException ex) { Console.WriteLine($"ERROR - Cannot create log file due to a security exception: {logFile}"); } catch (UnauthorizedAccessException ex) { Console.WriteLine($"ERROR - Cannot create log file as unauthorised: {logFile}"); } catch (ArgumentException ex) { Console.WriteLine($"ERROR - Cannot create log file as LogFile argument caused an ArgumentException while creating Filestream or StreamWriter: {logFile}"); } catch (PathTooLongException ex) { Console.WriteLine($"ERROR - Cannot create log file as the path is too long for Windows to create: {logFile}"); } catch (DirectoryNotFoundException ex) { Console.WriteLine($"ERROR - Cannot create log file as the DisplayMagician\\Logs Local Application Data directory doesn't exist: {logFile}"); } try { IngestFile(Path.Combine(AppDataPath, "Settings_1.0.json"), "Storing DisplayMagician Settings JSON File"); } catch (Exception e) { WriteException(e); } try { IngestFile(Path.Combine(AppDataPath, "Profiles", "DisplayProfiles_1.0.json"), "Storing DisplayMagician Display Profiles JSON File"); } catch (Exception e) { WriteException(e); } try { IngestFile(Path.Combine(AppDataPath, "Shortcuts", "Shortcuts_1.0.json"), "Storing DisplayMagician Shortcuts JSON File"); } catch (Exception e) { WriteException(e); } try { DumpObject(DisplayAdapter.GetDisplayAdapters(), "Storing a list of Display Adapters currently in this computer"); } catch (Exception e) { WriteException(e); } try { DumpObject(Display.GetDisplays(), "Storing a list of possible settings for Displays currently connected to this computer", new[] { new Tuple, string>(display => display.GetPossibleSettings(), "GetPossibleSettings()") }); } catch (Exception e) { WriteException(e); } try { DumpObject(UnAttachedDisplay.GetUnAttachedDisplays(), "Storing a list of Displays not currently in use, but currently connected to this computer"); } catch (Exception e) { WriteException(e); } try { DumpObject(PathDisplayAdapter.GetAdapters(), "Storing a list of Displays Targets for all Display Adapters currently in this computer", new[] { new Tuple, string>(adapter => adapter.ToDisplayAdapter(), "ToDisplayAdapter()") }); } catch (Exception e) { WriteException(e); } try { DumpObject(PathDisplaySource.GetDisplaySources(), "Storing a list of Display Sources for Displays currently connected to this computer", new[] { new Tuple, string>(source => source.ToDisplayDevices(), "ToDisplayDevices()") }); } catch (Exception e) { WriteException(e); } try { DumpObject(PathDisplayTarget.GetDisplayTargets(), "Storing a list of Displays Targets for Displays currently connected to this computer", new[] { new Tuple, string>(target => target.ToDisplayDevice(), "ToDisplayDevice()") }); } catch (Exception e) { WriteException(e); } try { if (PathInfo.IsSupported) { DumpObject(PathInfo.GetActivePaths(), "Storing a list of Active Displays currently connected to this computer", null, 2); } } catch (Exception e) { WriteException(e); } try { DumpObject(LogicalGPU.GetLogicalGPUs(), "Storing a list of logical NVIDIA GPUs formed by NVIDIA GPUs currently in this computer (e.g. SLI)", null, 1); } catch (Exception e) { WriteException(e); } try { DumpObject(PhysicalGPU.GetPhysicalGPUs(), "Storing a list of physical NVIDIA GPUs currently in this computer"); } catch (Exception e) { WriteException(e); } try { DumpObject(NvAPIWrapper.Display.Display.GetDisplays(), "Storing a list of Displays currently connected to this computer through a NVIDIA GPU", new[] { new Tuple, string>( display => display.GetSupportedViews(), "GetSupportedViews()") }); } catch (Exception e) { WriteException(e); } try { DumpObject(NvAPIWrapper.Display.UnAttachedDisplay.GetUnAttachedDisplays(), "Storing a list of Displays currently connected to this computer through a NVIDIA GPU but not active"); } catch (Exception e) { WriteException(e); } try { DumpObject(NvAPIWrapper.Display.PathInfo.GetDisplaysConfig(), "Storing a list of configuration of Displays currently connected to this computer through a NVIDIA GPU", null, 3); } catch (Exception e) { WriteException(e); } try { DumpObject(GridTopology.GetGridTopologies(), "Storing a list of configured NVIDIA Surround/Mosaic settings involving multiple Displays via a NVIDIA GPU", null, 3); } catch (Exception e) { WriteException(e); } try { CoreAudioController audioController = new CoreAudioController(); DumpObject(audioController.GetPlaybackDevices(), "Storing a list of detected Audio Devices", null, 3); } catch (Exception e) { WriteException(e); } _writer.Flush(); _writer.Close(); _writer.Dispose(); Console.WriteLine(new string('=', Console.BufferWidth)); Console.WriteLine(); Console.WriteLine(@"Finished! Press enter to exit LogReporter."); Console.ReadLine(); } private static void WriteException(Exception ex) { Console.WriteLine("{0} - Error: {1}", ex.GetType().Name, ex.Message); _writer.WriteLine("{0} - Error: {1}", ex.GetType().Name, ex.Message); } private static void WriteObject( object obj, int padding = 0, TimeSpan elapsed = default(TimeSpan), Tuple[] extraProperties = null, int deepIn = 0) { try { if (padding == 0) { if (!elapsed.Equals(TimeSpan.Zero)) { var elapsedFormated = string.Format("_{0}_", elapsed); _writer.WriteLine(elapsedFormated + new string('_', Console.BufferWidth - elapsedFormated.Length)); } else { _writer.WriteLine(new string('_', Console.BufferWidth)); } _writer.WriteLine("({0}) {{", obj.GetType().Name); } if (obj.GetType().IsValueType || obj is string) { _writer.WriteLine(new string(' ', padding * 3 + 2) + obj); } else if (obj is IEnumerable) { var i = 0; foreach (var arrayItem in (IEnumerable) obj) { _writer.WriteLine(new string(' ', padding * 3 + 2) + "[{0}]: ({1}) {{", i, arrayItem?.GetType().Name); WriteObject(arrayItem, padding + 1, default(TimeSpan), null, deepIn - 1); _writer.WriteLine(new string(' ', padding * 3 + 2) + "},"); i++; } } else { foreach (var propertyInfo in obj.GetType().GetProperties().OrderBy(info => info.Name)) { if (propertyInfo.CanRead) { object value; try { value = propertyInfo.GetValue(obj); } catch (TargetInvocationException ex) { value = ex.InnerException?.GetType().ToString(); } catch (Exception ex) { value = ex.GetType().ToString(); } if (deepIn > 0 && value != null && !value.GetType().IsValueType && value.GetType() != typeof(string)) { _writer.WriteLine(new string(' ', padding * 3 + 2) + "{0}: ({1}) {{", propertyInfo.Name, propertyInfo.PropertyType.Name); WriteObject(value, padding + 1, default(TimeSpan), null, deepIn - 1); _writer.WriteLine(new string(' ', padding * 3 + 2) + "},"); } else { _writer.WriteLine( $"{new string(' ', padding * 3 + 2)}{propertyInfo.Name}: {value ?? "[NULL]"},"); } } } } if (extraProperties != null) { foreach (var extraProperty in extraProperties) { _writer.WriteLine(new string(' ', padding * 3 + 2) + "{0}: ({1}) {{", extraProperty.Item1, extraProperty.Item2?.GetType().Name); WriteObject(extraProperty.Item2, padding + 1, default(TimeSpan), null, deepIn); _writer.WriteLine(new string(' ', padding * 3 + 2) + "},"); } } if (padding == 0) { _writer.WriteLine("};"); _writer.WriteLine(new string('_', Console.BufferWidth)); } } catch (Exception ex) { WriteException(ex); } } } }