Skip to content

Commit 64a29c8

Browse files
committed
feat: Improved system information collection
A number of less useful information (e.g. disk space) have been removed, and more useful information (e.g. Direct3D feature levels) added.
1 parent d279e38 commit 64a29c8

File tree

1 file changed

+134
-185
lines changed

1 file changed

+134
-185
lines changed

Source/ORTS.Common/SystemInfo.cs

Lines changed: 134 additions & 185 deletions
Original file line numberDiff line numberDiff line change
@@ -1,232 +1,152 @@
1-
// COPYRIGHT 2015 by the Open Rails project.
2-
//
1+
// COPYRIGHT 2009 - 2023 by the Open Rails project.
2+
//
33
// This file is part of Open Rails.
4-
//
4+
//
55
// Open Rails is free software: you can redistribute it and/or modify
66
// it under the terms of the GNU General Public License as published by
77
// the Free Software Foundation, either version 3 of the License, or
88
// (at your option) any later version.
9-
//
9+
//
1010
// Open Rails is distributed in the hope that it will be useful,
1111
// but WITHOUT ANY WARRANTY; without even the implied warranty of
1212
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1313
// GNU General Public License for more details.
14-
//
14+
//
1515
// You should have received a copy of the GNU General Public License
1616
// along with Open Rails. If not, see <http://www.gnu.org/licenses/>.
1717

18-
using Microsoft.Win32;
1918
using System;
20-
using System.Diagnostics;
2119
using System.IO;
20+
using System.Linq;
2221
using System.Management;
2322
using System.Runtime.InteropServices;
24-
using System.Text;
25-
using System.Windows.Forms;
26-
using Microsoft.Xna.Framework.Graphics;
23+
using System.Text.RegularExpressions;
24+
using System.Collections.Generic;
25+
using System.Globalization;
2726

2827
namespace ORTS.Common
2928
{
3029
public static class SystemInfo
3130
{
32-
public static void WriteSystemDetails(TextWriter output)
31+
static readonly Regex NameAndVersionRegex = new Regex("^(.*?) +([0-9.]+)$");
32+
static readonly NativeStructs.MemoryStatusExtended MemoryStatusExtended = new NativeStructs.MemoryStatusExtended()
3333
{
34-
output.WriteLine("Date/time = {0} ({1:u})", DateTime.Now, DateTime.UtcNow);
35-
WriteEnvironment(output);
36-
WriteAvailableRuntimes(output);
37-
output.WriteLine("Runtime = {0} ({1}bit)", Environment.Version, IntPtr.Size * 8);
38-
WriteGraphicsAdapter(output);
39-
}
34+
Size = 64
35+
};
4036

41-
static void WriteEnvironment(TextWriter output)
37+
static SystemInfo()
4238
{
43-
var buffer = new NativeStructs.MemoryStatusExtended { Size = 64 };
44-
NativeMethods.GlobalMemoryStatusEx(buffer);
45-
try
46-
{
47-
foreach (ManagementObject bios in new ManagementClass("Win32_BIOS").GetInstances())
48-
{
49-
output.WriteLine("BIOS = {0} ({1})", (string)bios["Description"], (string)bios["Manufacturer"]);
50-
}
51-
}
52-
catch (Exception error)
53-
{
54-
Trace.WriteLine(error);
55-
}
56-
try
39+
Application = new Platform
5740
{
58-
foreach (ManagementObject processor in new ManagementClass("Win32_Processor").GetInstances())
59-
{
60-
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);
61-
foreach (ManagementObject cpuCache in processor.GetRelated("Win32_CacheMemory"))
62-
{
63-
output.Write(" ({0} {1:F0} KB)", (string)cpuCache["Purpose"], (float)(uint)cpuCache["InstalledSize"]);
64-
}
65-
output.WriteLine();
66-
}
67-
}
68-
catch (Exception error)
69-
{
70-
Trace.WriteLine(error);
71-
}
72-
output.WriteLine("Memory = {0:F1} GB", (float)buffer.TotalPhysical / 1024 / 1024 / 1024);
73-
try
74-
{
75-
foreach (ManagementObject display in new ManagementClass("Win32_VideoController").GetInstances())
76-
{
77-
// ? used as display["AdapterRAM"] may be null on a virtual machine (e.g. VMWare)
78-
output.WriteLine("Video = {0} ({1:F1} GB RAM){2}", (string)display["Description"], (float?)(uint?)display["AdapterRAM"] / 1024 / 1024 / 1024, GetPnPDeviceDrivers(display));
79-
}
80-
}
81-
catch (Exception error)
82-
{
83-
Trace.WriteLine(error);
84-
}
85-
try
86-
{
87-
foreach (var screen in Screen.AllScreens)
88-
{
89-
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" : "");
90-
}
91-
}
92-
catch (Exception error)
93-
{
94-
Trace.WriteLine(error);
95-
}
96-
try
97-
{
98-
foreach (ManagementObject sound in new ManagementClass("Win32_SoundDevice").GetInstances())
99-
{
100-
output.WriteLine("Sound = {0}{1}", (string)sound["Description"], GetPnPDeviceDrivers(sound));
101-
}
102-
}
103-
catch (Exception error)
104-
{
105-
Trace.WriteLine(error);
106-
}
107-
try
108-
{
109-
foreach (ManagementObject disk in new ManagementClass("Win32_LogicalDisk").GetInstances())
110-
{
111-
output.Write("Disk = {0} ({1}, {2}", (string)disk["Name"], (string)disk["Description"], (string)disk["FileSystem"]);
112-
if (disk["Size"] != null && disk["FreeSpace"] != null)
113-
output.WriteLine(", {0:F1} GB, {1:F1} GB free)", (float)(ulong)disk["Size"] / 1024 / 1024 / 1024, (float)(ulong)disk["FreeSpace"] / 1024 / 1024 / 1024);
114-
else
115-
output.WriteLine(")");
116-
}
117-
}
118-
catch (Exception error)
119-
{
120-
Trace.WriteLine(error);
121-
}
122-
try
41+
Name = System.Windows.Forms.Application.ProductName,
42+
Version = VersionInfo.VersionOrBuild,
43+
Architecture = RuntimeInformation.ProcessArchitecture.ToString(),
44+
};
45+
46+
var runtime = NameAndVersionRegex.Match(RuntimeInformation.FrameworkDescription.Trim());
47+
Runtime = new Platform
12348
{
124-
foreach (ManagementObject os in new ManagementClass("Win32_OperatingSystem").GetInstances())
125-
{
126-
output.WriteLine("OS = {0} {1} ({2})", (string)os["Caption"], (string)os["OSArchitecture"], (string)os["Version"]);
127-
}
128-
}
129-
catch (Exception error)
49+
Name = runtime.Groups[1].Value,
50+
Version = runtime.Groups[2].Value,
51+
};
52+
53+
// Almost nothing will correctly identify Windows 11 at this point, so we have to use WMI.
54+
var operatingSystem = new ManagementClass("Win32_OperatingSystem").GetInstances().Cast<ManagementObject>().First();
55+
OperatingSystem = new Platform
56+
{
57+
Name = (string)operatingSystem["Caption"],
58+
Version = (string)operatingSystem["Version"],
59+
Architecture = RuntimeInformation.OSArchitecture.ToString(),
60+
Language = CultureInfo.CurrentUICulture.IetfLanguageTag,
61+
Languages = (string[])operatingSystem["MUILanguages"],
62+
};
63+
64+
NativeMethods.GlobalMemoryStatusEx(MemoryStatusExtended);
65+
InstalledMemoryMB = (int)(MemoryStatusExtended.TotalPhysical / 1024 / 1024);
66+
67+
CPUs = new ManagementClass("Win32_Processor").GetInstances().Cast<ManagementObject>().Select(processor => new CPU
13068
{
131-
Trace.WriteLine(error);
132-
}
133-
}
69+
Name = (string)processor["Name"],
70+
Manufacturer = (string)processor["Manufacturer"],
71+
ThreadCount = (uint)processor["ThreadCount"],
72+
MaxClockMHz = (uint)processor["MaxClockSpeed"],
73+
}).ToList();
13474

135-
static string GetPnPDeviceDrivers(ManagementObject device)
136-
{
137-
var output = new StringBuilder();
138-
foreach (ManagementObject pnpDevice in device.GetRelated("Win32_PnPEntity"))
75+
GPUs = new ManagementClass("Win32_VideoController").GetInstances().Cast<ManagementObject>().Select(adapter => new GPU
13976
{
140-
foreach (ManagementObject dataFile in pnpDevice.GetRelated("CIM_DataFile"))
77+
Name = (string)adapter["Name"],
78+
Manufacturer = (string)adapter["AdapterCompatibility"],
79+
MemoryMB = (uint)adapter["AdapterRAM"] / 1024 / 1024,
80+
}).ToList();
81+
82+
var featureLevels = new uint[] {
83+
NativeMethods.D3D_FEATURE_LEVEL_12_2,
84+
NativeMethods.D3D_FEATURE_LEVEL_12_1,
85+
NativeMethods.D3D_FEATURE_LEVEL_12_0,
86+
NativeMethods.D3D_FEATURE_LEVEL_11_1,
87+
NativeMethods.D3D_FEATURE_LEVEL_11_0,
88+
NativeMethods.D3D_FEATURE_LEVEL_10_1,
89+
NativeMethods.D3D_FEATURE_LEVEL_10_0,
90+
NativeMethods.D3D_FEATURE_LEVEL_9_3,
91+
NativeMethods.D3D_FEATURE_LEVEL_9_2,
92+
NativeMethods.D3D_FEATURE_LEVEL_9_1,
93+
};
94+
foreach (var featureLevel in featureLevels)
95+
{
96+
var levels = new uint[] { featureLevel };
97+
try
14198
{
142-
output.AppendFormat(" ({0} {1})", (string)dataFile["FileName"], (string)dataFile["Version"]);
99+
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);
100+
if (level == featureLevel) Direct3DFeatureLevels.Add(string.Format("{0}_{1}", level >> 12 & 0xF, level >> 8 & 0xF));
143101
}
102+
catch (EntryPointNotFoundException) { }
103+
catch (DllNotFoundException) { }
144104
}
145-
return output.ToString();
146105
}
147106

148-
static void WriteAvailableRuntimes(TextWriter output)
107+
public static readonly Platform Application;
108+
public static readonly Platform Runtime;
109+
public static readonly Platform OperatingSystem;
110+
public static readonly int InstalledMemoryMB;
111+
public static readonly List<CPU> CPUs;
112+
public static readonly List<GPU> GPUs;
113+
public static readonly List<string> Direct3DFeatureLevels = new List<string>();
114+
115+
public static void WriteSystemDetails(TextWriter output)
149116
{
150-
output.Write("Runtimes =");
151-
try
152-
{
153-
// This remote access is necessary to ensure we get the correct bitness view of the registry.
154-
using (var frameworksKey = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, "").OpenSubKey(@"SOFTWARE\Microsoft\NET Framework Setup\NDP", false))
155-
{
156-
foreach (var versionKeyName in frameworksKey.GetSubKeyNames())
157-
{
158-
if (!versionKeyName.StartsWith("v"))
159-
continue;
160-
161-
using (var versionKey = frameworksKey.OpenSubKey(versionKeyName))
162-
{
163-
var fullVersion = WriteInstalledRuntimes(output, versionKeyName, versionKey);
164-
if (fullVersion != "")
165-
continue;
166-
167-
foreach (var skuKeyName in versionKey.GetSubKeyNames())
168-
{
169-
using (var skuKey = versionKey.OpenSubKey(skuKeyName))
170-
{
171-
WriteInstalledRuntimes(output, versionKeyName + " " + skuKeyName, skuKey);
172-
}
173-
}
174-
}
175-
}
176-
}
177-
output.WriteLine();
178-
}
179-
catch (Exception error)
180-
{
181-
Trace.WriteLine(error);
182-
}
117+
output.WriteLine("Date/time = {0} ({1:u})",
118+
DateTime.Now, DateTime.UtcNow);
119+
output.WriteLine("Application = {0} {1} ({2})", Application.Name, Application.Version, Application.Architecture);
120+
output.WriteLine("Runtime = {0} {1}", Runtime.Name, Runtime.Version);
121+
output.WriteLine("System = {0} {1} ({2}; {3}; {4})", OperatingSystem.Name, OperatingSystem.Version, OperatingSystem.Architecture, OperatingSystem.Language, string.Join(",", OperatingSystem.Languages));
122+
output.WriteLine("Memory = {0:N0} MB", InstalledMemoryMB);
123+
foreach (var cpu in CPUs) output.WriteLine("CPU = {0} ({1}; {2} threads; {3:N0} MHz)", cpu.Name, cpu.Manufacturer, cpu.ThreadCount, cpu.MaxClockMHz);
124+
foreach (var gpu in GPUs) output.WriteLine("GPU = {0} ({1}; {2:N0} MB)", gpu.Name, gpu.Manufacturer, gpu.MemoryMB);
125+
output.WriteLine("Direct3D = {0}", string.Join(",", Direct3DFeatureLevels));
183126
}
184127

185-
static string WriteInstalledRuntimes(TextWriter output, string versionKeyName, RegistryKey versionKey)
128+
public struct Platform
186129
{
187-
var installed = SafeReadKey(versionKey, "Install", -1);
188-
var fullVersion = SafeReadKey(versionKey, "Version", "");
189-
var servicePack = SafeReadKey(versionKey, "SP", -1);
190-
191-
if (installed == 1 && servicePack != -1)
192-
{
193-
output.Write(" {0} SP{2} ", versionKeyName.Substring(1), fullVersion, servicePack);
194-
}
195-
else if (installed == 1)
196-
{
197-
output.Write(" {0} ", versionKeyName.Substring(1), fullVersion);
198-
}
199-
return fullVersion;
130+
public string Name;
131+
public string Version;
132+
public string Architecture;
133+
public string Language;
134+
public string[] Languages;
200135
}
201136

202-
static void WriteGraphicsAdapter(TextWriter output)
137+
public struct CPU
203138
{
204-
try {
205-
foreach (var adapter in GraphicsAdapter.Adapters)
206-
{
207-
try
208-
{
209-
output.WriteLine("{0} = {1}", adapter.DeviceName, adapter.Description);
210-
}
211-
catch (Exception) { }
212-
}
213-
}
214-
catch (Exception error)
215-
{
216-
output.WriteLine(error);
217-
}
139+
public string Name;
140+
public string Manufacturer;
141+
public uint ThreadCount;
142+
public uint MaxClockMHz;
218143
}
219144

220-
static T SafeReadKey<T>(RegistryKey key, string name, T defaultValue)
145+
public struct GPU
221146
{
222-
try
223-
{
224-
return (T)key.GetValue(name, defaultValue);
225-
}
226-
catch
227-
{
228-
return defaultValue;
229-
}
147+
public string Name;
148+
public string Manufacturer;
149+
public uint MemoryMB;
230150
}
231151

232152
static class NativeStructs
@@ -250,6 +170,35 @@ static class NativeMethods
250170
{
251171
[DllImport("kernel32.dll", SetLastError = true)]
252172
public static extern bool GlobalMemoryStatusEx([In, Out] NativeStructs.MemoryStatusExtended buffer);
173+
174+
public const uint D3D11_SDK_VERSION = 7;
175+
176+
public const uint D3D_DRIVER_TYPE_HARDWARE = 1;
177+
178+
public const uint D3D_FEATURE_LEVEL_9_1 = 0x9100;
179+
public const uint D3D_FEATURE_LEVEL_9_2 = 0x9200;
180+
public const uint D3D_FEATURE_LEVEL_9_3 = 0x9300;
181+
public const uint D3D_FEATURE_LEVEL_10_0 = 0xA000;
182+
public const uint D3D_FEATURE_LEVEL_10_1 = 0xA100;
183+
public const uint D3D_FEATURE_LEVEL_11_0 = 0xB000;
184+
public const uint D3D_FEATURE_LEVEL_11_1 = 0xB100;
185+
public const uint D3D_FEATURE_LEVEL_12_0 = 0xC000;
186+
public const uint D3D_FEATURE_LEVEL_12_1 = 0xC100;
187+
public const uint D3D_FEATURE_LEVEL_12_2 = 0xC200;
188+
189+
[DllImport("d3d11.dll", ExactSpelling = true)]
190+
public static extern uint D3D11CreateDevice(
191+
IntPtr adapter,
192+
uint driverType,
193+
IntPtr software,
194+
uint flags,
195+
[In] uint[] featureLevels,
196+
int featureLevelCount,
197+
uint sdkVersion,
198+
IntPtr device,
199+
out uint featureLevel,
200+
IntPtr immediateContext
201+
);
253202
}
254203
}
255204
}

0 commit comments

Comments
 (0)