Skip to content

Commit 8951c4e

Browse files
authored
Merge pull request #1177 from cjakeman/fix/avoid-type-initialization-exception
Fix: Avoid fatal type-initialization exception at start-up
2 parents 9962874 + f188128 commit 8951c4e

File tree

1 file changed

+88
-50
lines changed

1 file changed

+88
-50
lines changed

Source/Orts.Common/SystemInfo.cs

Lines changed: 88 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,8 @@ public static class SystemInfo
3838

3939
static SystemInfo()
4040
{
41+
var systemInfoHelper = new SystemInfoHelper();
42+
4143
Application = new Platform
4244
{
4345
Name = ApplicationInfo.ProductName,
@@ -52,60 +54,13 @@ static SystemInfo()
5254
Version = runtime.Groups[2].Value,
5355
};
5456

55-
try
56-
{
57-
// Almost nothing will correctly identify Windows 11 at this point, so we have to use WMI.
58-
var operatingSystem = new ManagementClass("Win32_OperatingSystem").GetInstances().Cast<ManagementObject>().First();
59-
OperatingSystem = new Platform
60-
{
61-
Name = (string)operatingSystem["Caption"],
62-
Version = (string)operatingSystem["Version"],
63-
Architecture = RuntimeInformation.OSArchitecture.ToString(),
64-
Language = CultureInfo.CurrentUICulture.IetfLanguageTag,
65-
Languages = (string[])operatingSystem["MUILanguages"],
66-
};
67-
}
68-
catch (ManagementException error)
69-
{
70-
// Likely to catch multiple exceptions like:
71-
// Exception thrown: 'System.IO.InvalidDataException' in ORTS.Menu.dll
72-
Trace.WriteLine(error);
73-
}
57+
OperatingSystem = systemInfoHelper.GetOperatingSystem();
7458

7559
NativeMethods.GlobalMemoryStatusEx(MemoryStatusExtended);
7660
InstalledMemoryMB = (int)(MemoryStatusExtended.TotalPhysical / 1024 / 1024);
7761

78-
try
79-
{
80-
CPUs = new ManagementClass("Win32_Processor").GetInstances().Cast<ManagementObject>().Select(processor => new CPU
81-
{
82-
Name = (string)processor["Name"],
83-
Manufacturer = (string)processor["Manufacturer"],
84-
ThreadCount = (uint)processor["ThreadCount"],
85-
MaxClockMHz = (uint)processor["MaxClockSpeed"],
86-
}).ToList();
87-
}
88-
catch (ManagementException error)
89-
{
90-
Trace.WriteLine(error);
91-
}
92-
93-
// The WMI data for AdapterRAM is unreliable, so we have to use DXGI to get the real numbers.
94-
// Alas, DXGI doesn't give us the manufacturer name for the adapter, so we combine it with WMI.
95-
var descriptions = new Factory1().Adapters.Select(adapter => adapter.Description).ToArray();
96-
try
97-
{
98-
GPUs = new ManagementClass("Win32_VideoController").GetInstances().Cast<ManagementObject>().Select(adapter => new GPU
99-
{
100-
Name = (string)adapter["Name"],
101-
Manufacturer = (string)adapter["AdapterCompatibility"],
102-
MemoryMB = (uint)((long)descriptions.FirstOrDefault(desc => desc.Description == (string)adapter["Name"]).DedicatedVideoMemory / 1024 / 1024),
103-
}).ToList();
104-
}
105-
catch (ManagementException error)
106-
{
107-
Trace.WriteLine(error);
108-
}
62+
CPUs = systemInfoHelper.GetCPUs();
63+
GPUs = systemInfoHelper.GetGPUs();
10964

11065
var featureLevels = new uint[] {
11166
NativeMethods.D3D_FEATURE_LEVEL_12_2,
@@ -228,5 +183,88 @@ public static extern uint D3D11CreateDevice(
228183
IntPtr immediateContext
229184
);
230185
}
186+
187+
/// <summary>
188+
/// Provides helper methods for retrieving information about the system's operating system, CPUs, and GPUs.
189+
/// These methods were moved into a non-static class so that an exception thrown and caught would not lead to a fatal System.TypeInitializationException in the constructor of the static SystemInfo class
190+
/// </summary>
191+
/// <remarks>This class offers methods to query system hardware and software details using
192+
/// platform-specific APIs. The information returned by these methods may vary depending on the underlying
193+
/// operating system and available system permissions. Methods may return partial or default information if
194+
/// system queries fail or are unsupported on the current platform.</remarks>
195+
public class SystemInfoHelper
196+
{
197+
public Platform GetOperatingSystem()
198+
{
199+
var platform = new Platform();
200+
try
201+
{
202+
// Uncomment this throw to prove the exception reported in https://bugs.launchpad.net/or/+bug/2131143 is no longer fatal.
203+
//throw new ManagementException();
204+
205+
// Almost nothing will correctly identify Windows 11 at this point, so we have to use WMI.
206+
var operatingSystem = new ManagementClass("Win32_OperatingSystem").GetInstances().Cast<ManagementObject>().First();
207+
platform = new Platform
208+
{
209+
Name = (string)operatingSystem["Caption"],
210+
Version = (string)operatingSystem["Version"],
211+
Architecture = RuntimeInformation.OSArchitecture.ToString(),
212+
Language = CultureInfo.CurrentUICulture.IetfLanguageTag,
213+
Languages = (string[])operatingSystem["MUILanguages"],
214+
};
215+
}
216+
catch (Exception ex)
217+
{
218+
// Likely to catch multiple exceptions like:
219+
// Exception thrown: 'System.IO.InvalidDataException' in ORTS.Menu.dll
220+
Trace.WriteLine($"Exception in GetOperatingSystem(): {ex}");
221+
}
222+
return platform;
223+
}
224+
225+
public List<CPU> GetCPUs()
226+
{
227+
var cpus = new List<CPU>();
228+
try
229+
{
230+
cpus = new ManagementClass("Win32_Processor").GetInstances().Cast<ManagementObject>().Select(processor => new CPU
231+
{
232+
Name = (string)processor["Name"],
233+
Manufacturer = (string)processor["Manufacturer"],
234+
ThreadCount = (uint)processor["ThreadCount"],
235+
MaxClockMHz = (uint)processor["MaxClockSpeed"],
236+
}).ToList();
237+
}
238+
catch (Exception ex)
239+
{
240+
Trace.WriteLine($"Exception in GetCPUs(): {ex}");
241+
}
242+
return cpus;
243+
}
244+
245+
public List<GPU> GetGPUs()
246+
{
247+
var gpus = new List<GPU>();
248+
249+
// The WMI data for AdapterRAM is unreliable, so we have to use DXGI to get the real numbers.
250+
// Alas, DXGI doesn't give us the manufacturer name for the adapter, so we combine it with WMI.
251+
var descriptions = new Factory1().Adapters.Select(adapter => adapter.Description).ToArray();
252+
253+
try
254+
{
255+
gpus = new ManagementClass("Win32_VideoController").GetInstances().Cast<ManagementObject>().Select(adapter => new GPU
256+
{
257+
Name = (string)adapter["Name"],
258+
Manufacturer = (string)adapter["AdapterCompatibility"],
259+
MemoryMB = (uint)((long)descriptions.FirstOrDefault(desc => desc.Description == (string)adapter["Name"]).DedicatedVideoMemory / 1024 / 1024),
260+
}).ToList();
261+
}
262+
catch (Exception ex)
263+
{
264+
Trace.WriteLine($"Exception in GetGPUs(): {ex}");
265+
}
266+
return gpus;
267+
}
268+
}
231269
}
232270
}

0 commit comments

Comments
 (0)