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