diff --git a/Source/ORTS.Common/SystemInfo.cs b/Source/ORTS.Common/SystemInfo.cs index dc12791aac..23842598fc 100644 --- a/Source/ORTS.Common/SystemInfo.cs +++ b/Source/ORTS.Common/SystemInfo.cs @@ -1,232 +1,178 @@ -// COPYRIGHT 2015 by the Open Rails project. -// +// COPYRIGHT 2009 - 2023 by the Open Rails project. +// // This file is part of Open Rails. -// +// // Open Rails is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. -// +// // Open Rails is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. -// +// // You should have received a copy of the GNU General Public License // along with Open Rails. If not, see . -using Microsoft.Win32; using System; -using System.Diagnostics; using System.IO; +using System.Linq; using System.Management; using System.Runtime.InteropServices; -using System.Text; -using System.Windows.Forms; -using Microsoft.Xna.Framework.Graphics; +using System.Text.RegularExpressions; +using System.Collections.Generic; +using System.Globalization; +using System.Diagnostics; +using SharpDX.DXGI; namespace ORTS.Common { public static class SystemInfo { - public static void WriteSystemDetails(TextWriter output) + static readonly Regex NameAndVersionRegex = new Regex("^(.*?) +([0-9.]+)$"); + static readonly NativeStructs.MemoryStatusExtended MemoryStatusExtended = new NativeStructs.MemoryStatusExtended() { - output.WriteLine("Date/time = {0} ({1:u})", DateTime.Now, DateTime.UtcNow); - WriteEnvironment(output); - WriteAvailableRuntimes(output); - output.WriteLine("Runtime = {0} ({1}bit)", Environment.Version, IntPtr.Size * 8); - WriteGraphicsAdapter(output); - } + Size = 64 + }; - static void WriteEnvironment(TextWriter output) + static SystemInfo() { - var buffer = new NativeStructs.MemoryStatusExtended { Size = 64 }; - NativeMethods.GlobalMemoryStatusEx(buffer); - try + Application = new Platform { - foreach (ManagementObject bios in new ManagementClass("Win32_BIOS").GetInstances()) - { - output.WriteLine("BIOS = {0} ({1})", (string)bios["Description"], (string)bios["Manufacturer"]); - } - } - catch (Exception error) - { - Trace.WriteLine(error); - } - try - { - foreach (ManagementObject processor in new ManagementClass("Win32_Processor").GetInstances()) - { - output.Write("Processor = {0} ({2} threads, {1} cores, {3:F1} GHz)", (string)processor["Name"], (uint)processor["NumberOfCores"], (uint)processor["NumberOfLogicalProcessors"], (float)(uint)processor["MaxClockSpeed"] / 1000); - foreach (ManagementObject cpuCache in processor.GetRelated("Win32_CacheMemory")) - { - output.Write(" ({0} {1:F0} KB)", (string)cpuCache["Purpose"], (float)(uint)cpuCache["InstalledSize"]); - } - output.WriteLine(); - } - } - catch (Exception error) - { - Trace.WriteLine(error); - } - output.WriteLine("Memory = {0:F1} GB", (float)buffer.TotalPhysical / 1024 / 1024 / 1024); - try - { - foreach (ManagementObject display in new ManagementClass("Win32_VideoController").GetInstances()) - { - // ? used as display["AdapterRAM"] may be null on a virtual machine (e.g. VMWare) - output.WriteLine("Video = {0} ({1:F1} GB RAM){2}", (string)display["Description"], (float?)(uint?)display["AdapterRAM"] / 1024 / 1024 / 1024, GetPnPDeviceDrivers(display)); - } - } - catch (Exception error) - { - Trace.WriteLine(error); - } - try - { - foreach (var screen in Screen.AllScreens) - { - output.WriteLine("Display = {0} ({3} x {4}, {5}-bit{6}, {1} x {2})", screen.DeviceName, screen.Bounds.X, screen.Bounds.Y, screen.Bounds.Width, screen.Bounds.Height, screen.BitsPerPixel, screen.Primary ? ", primary" : ""); - } - } - catch (Exception error) + Name = System.Windows.Forms.Application.ProductName, + Version = VersionInfo.VersionOrBuild, + Architecture = RuntimeInformation.ProcessArchitecture.ToString(), + }; + + var runtime = NameAndVersionRegex.Match(RuntimeInformation.FrameworkDescription.Trim()); + Runtime = new Platform { - Trace.WriteLine(error); - } + Name = runtime.Groups[1].Value, + Version = runtime.Groups[2].Value, + }; + try { - foreach (ManagementObject sound in new ManagementClass("Win32_SoundDevice").GetInstances()) + // Almost nothing will correctly identify Windows 11 at this point, so we have to use WMI. + var operatingSystem = new ManagementClass("Win32_OperatingSystem").GetInstances().Cast().First(); + OperatingSystem = new Platform { - output.WriteLine("Sound = {0}{1}", (string)sound["Description"], GetPnPDeviceDrivers(sound)); - } + Name = (string)operatingSystem["Caption"], + Version = (string)operatingSystem["Version"], + Architecture = RuntimeInformation.OSArchitecture.ToString(), + Language = CultureInfo.CurrentUICulture.IetfLanguageTag, + Languages = (string[])operatingSystem["MUILanguages"], + }; } - catch (Exception error) + catch (ManagementException error) { Trace.WriteLine(error); } + + NativeMethods.GlobalMemoryStatusEx(MemoryStatusExtended); + InstalledMemoryMB = (int)(MemoryStatusExtended.TotalPhysical / 1024 / 1024); + try { - foreach (ManagementObject disk in new ManagementClass("Win32_LogicalDisk").GetInstances()) + CPUs = new ManagementClass("Win32_Processor").GetInstances().Cast().Select(processor => new CPU { - output.Write("Disk = {0} ({1}, {2}", (string)disk["Name"], (string)disk["Description"], (string)disk["FileSystem"]); - if (disk["Size"] != null && disk["FreeSpace"] != null) - output.WriteLine(", {0:F1} GB, {1:F1} GB free)", (float)(ulong)disk["Size"] / 1024 / 1024 / 1024, (float)(ulong)disk["FreeSpace"] / 1024 / 1024 / 1024); - else - output.WriteLine(")"); - } + Name = (string)processor["Name"], + Manufacturer = (string)processor["Manufacturer"], + ThreadCount = (uint)processor["ThreadCount"], + MaxClockMHz = (uint)processor["MaxClockSpeed"], + }).ToList(); } - catch (Exception error) + catch (ManagementException error) { Trace.WriteLine(error); } + + // The WMI data for AdapterRAM is unreliable, so we have to use DXGI to get the real numbers. + // Alas, DXGI doesn't give us the manufacturer name for the adapter, so we combine it with WMI. + var descriptions = new Factory1().Adapters.Select(adapter => adapter.Description).ToArray(); try { - foreach (ManagementObject os in new ManagementClass("Win32_OperatingSystem").GetInstances()) + GPUs = new ManagementClass("Win32_VideoController").GetInstances().Cast().Select(adapter => new GPU { - output.WriteLine("OS = {0} {1} ({2})", (string)os["Caption"], (string)os["OSArchitecture"], (string)os["Version"]); - } + Name = (string)adapter["Name"], + Manufacturer = (string)adapter["AdapterCompatibility"], + MemoryMB = (uint)((long)descriptions.FirstOrDefault(desc => desc.Description == (string)adapter["Name"]).DedicatedVideoMemory / 1024 / 1024), + }).ToList(); } - catch (Exception error) + catch (ManagementException error) { Trace.WriteLine(error); } - } - static string GetPnPDeviceDrivers(ManagementObject device) - { - var output = new StringBuilder(); - foreach (ManagementObject pnpDevice in device.GetRelated("Win32_PnPEntity")) - { - foreach (ManagementObject dataFile in pnpDevice.GetRelated("CIM_DataFile")) + var featureLevels = new uint[] { + NativeMethods.D3D_FEATURE_LEVEL_12_2, + NativeMethods.D3D_FEATURE_LEVEL_12_1, + NativeMethods.D3D_FEATURE_LEVEL_12_0, + NativeMethods.D3D_FEATURE_LEVEL_11_1, + NativeMethods.D3D_FEATURE_LEVEL_11_0, + NativeMethods.D3D_FEATURE_LEVEL_10_1, + NativeMethods.D3D_FEATURE_LEVEL_10_0, + NativeMethods.D3D_FEATURE_LEVEL_9_3, + NativeMethods.D3D_FEATURE_LEVEL_9_2, + NativeMethods.D3D_FEATURE_LEVEL_9_1, + }; + foreach (var featureLevel in featureLevels) + { + var levels = new uint[] { featureLevel }; + try { - output.AppendFormat(" ({0} {1})", (string)dataFile["FileName"], (string)dataFile["Version"]); + var rv = NativeMethods.D3D11CreateDevice(IntPtr.Zero, NativeMethods.D3D_DRIVER_TYPE_HARDWARE, IntPtr.Zero, 0, levels, levels.Length, NativeMethods.D3D11_SDK_VERSION, IntPtr.Zero, out uint level, IntPtr.Zero); + if (level == featureLevel) Direct3DFeatureLevels.Add(string.Format("{0}_{1}", level >> 12 & 0xF, level >> 8 & 0xF)); } + catch (EntryPointNotFoundException) { } + catch (DllNotFoundException) { } } - return output.ToString(); } - static void WriteAvailableRuntimes(TextWriter output) + public static readonly Platform Application; + public static readonly Platform Runtime; + public static readonly Platform OperatingSystem; + public static readonly int InstalledMemoryMB; + public static readonly List CPUs = new List(); + public static readonly List GPUs = new List(); + public static readonly List Direct3DFeatureLevels = new List(); + + public static void WriteSystemDetails(TextWriter output) { - output.Write("Runtimes ="); - try - { - // This remote access is necessary to ensure we get the correct bitness view of the registry. - using (var frameworksKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, "").OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP", false)) - { - foreach (var versionKeyName in frameworksKey.GetSubKeyNames()) - { - if (!versionKeyName.StartsWith("v")) - continue; - - using (var versionKey = frameworksKey.OpenSubKey(versionKeyName)) - { - var fullVersion = WriteInstalledRuntimes(output, versionKeyName, versionKey); - if (fullVersion != "") - continue; - - foreach (var skuKeyName in versionKey.GetSubKeyNames()) - { - using (var skuKey = versionKey.OpenSubKey(skuKeyName)) - { - WriteInstalledRuntimes(output, versionKeyName + " " + skuKeyName, skuKey); - } - } - } - } - } - output.WriteLine(); - } - catch (Exception error) - { - Trace.WriteLine(error); - } + output.WriteLine("Date/time = {0} ({1:u})", + DateTime.Now, DateTime.UtcNow); + output.WriteLine("Application = {0} {1} ({2})", Application.Name, Application.Version, Application.Architecture); + output.WriteLine("Runtime = {0} {1}", Runtime.Name, Runtime.Version); + output.WriteLine("System = {0} {1} ({2}; {3}; {4})", OperatingSystem.Name, OperatingSystem.Version, OperatingSystem.Architecture, OperatingSystem.Language, string.Join(",", OperatingSystem.Languages ?? new string[0])); + output.WriteLine("Memory = {0:N0} MB", InstalledMemoryMB); + foreach (var cpu in CPUs) output.WriteLine("CPU = {0} ({1}; {2} threads; {3:N0} MHz)", cpu.Name, cpu.Manufacturer, cpu.ThreadCount, cpu.MaxClockMHz); + foreach (var gpu in GPUs) output.WriteLine("GPU = {0} ({1}; {2:N0} MB)", gpu.Name, gpu.Manufacturer, gpu.MemoryMB); + output.WriteLine("Direct3D = {0}", string.Join(",", Direct3DFeatureLevels)); } - static string WriteInstalledRuntimes(TextWriter output, string versionKeyName, RegistryKey versionKey) + public struct Platform { - var installed = SafeReadKey(versionKey, "Install", -1); - var fullVersion = SafeReadKey(versionKey, "Version", ""); - var servicePack = SafeReadKey(versionKey, "SP", -1); - - if (installed == 1 && servicePack != -1) - { - output.Write(" {0} SP{2} ", versionKeyName.Substring(1), fullVersion, servicePack); - } - else if (installed == 1) - { - output.Write(" {0} ", versionKeyName.Substring(1), fullVersion); - } - return fullVersion; + public string Name; + public string Version; + public string Architecture; + public string Language; + public string[] Languages; } - static void WriteGraphicsAdapter(TextWriter output) + public struct CPU { - try { - foreach (var adapter in GraphicsAdapter.Adapters) - { - try - { - output.WriteLine("{0} = {1}", adapter.DeviceName, adapter.Description); - } - catch (Exception) { } - } - } - catch (Exception error) - { - output.WriteLine(error); - } + public string Name; + public string Manufacturer; + public uint ThreadCount; + public uint MaxClockMHz; } - static T SafeReadKey(RegistryKey key, string name, T defaultValue) + public struct GPU { - try - { - return (T)key.GetValue(name, defaultValue); - } - catch - { - return defaultValue; - } + public string Name; + public string Manufacturer; + public uint MemoryMB; } static class NativeStructs @@ -250,6 +196,35 @@ static class NativeMethods { [DllImport("kernel32.dll", SetLastError = true)] public static extern bool GlobalMemoryStatusEx([In, Out] NativeStructs.MemoryStatusExtended buffer); + + public const uint D3D11_SDK_VERSION = 7; + + public const uint D3D_DRIVER_TYPE_HARDWARE = 1; + + public const uint D3D_FEATURE_LEVEL_9_1 = 0x9100; + public const uint D3D_FEATURE_LEVEL_9_2 = 0x9200; + public const uint D3D_FEATURE_LEVEL_9_3 = 0x9300; + public const uint D3D_FEATURE_LEVEL_10_0 = 0xA000; + public const uint D3D_FEATURE_LEVEL_10_1 = 0xA100; + public const uint D3D_FEATURE_LEVEL_11_0 = 0xB000; + public const uint D3D_FEATURE_LEVEL_11_1 = 0xB100; + public const uint D3D_FEATURE_LEVEL_12_0 = 0xC000; + public const uint D3D_FEATURE_LEVEL_12_1 = 0xC100; + public const uint D3D_FEATURE_LEVEL_12_2 = 0xC200; + + [DllImport("d3d11.dll", ExactSpelling = true)] + public static extern uint D3D11CreateDevice( + IntPtr adapter, + uint driverType, + IntPtr software, + uint flags, + [In] uint[] featureLevels, + int featureLevelCount, + uint sdkVersion, + IntPtr device, + out uint featureLevel, + IntPtr immediateContext + ); } } }