Our Sticker Tales game tracks various activities via Google Analytics using code that grew into our CSharpAnalytics open source project.
But we also want to add some system metrics that could help us answer other questions:
When is it time to exploit features of an OS update?
Do people on tablets play longer than people on laptops?
How long do various timed activities take by CPU?
Does not having a physical keyboard affect purchase flow completion?
To do this we want to record:
Processor architecture
Device manufacturer, model and form factor (category)
Windows version number
On Windows Phone and .NET this information is easily available but in Windows Store apps it is not. Microsoft have made this difficult because your application should not change it’s behavior based on this information.
The following code uses the PnPObject API to best-guess these things. It is not bullet-proof and could easily fail on machines with custom HAL drivers (I haven’t seen one of those in years) or on other language editions of Windows (not yet tried).
It is good enough in my opinion for analytics, logging or troubleshooting and for nothing more.
To use it simply:
var windowsVersion = await SystemInfoEstimate.GetWindowsVersionAsync();
var processor = await SystemInfoEstimate.GetProcessorArchitectureAsync();
using System;
using System.Linq;
using System.Threading.Tasks;
using Windows.Devices.Enumeration.Pnp;
using Windows.System;
public class SystemInfoEstimate
{
const string ItemNameKey = "System.ItemNameDisplay";
const string ModelNameKey = "System.Devices.ModelName";
const string ManufacturerKey = "System.Devices.Manufacturer";
const string DeviceClassKey = "{A45C254E-DF1C-4EFD-8020-67D146A850E0},10";
const string PrimaryCategoryKey = "{78C34FC8-104A-4ACA-9EA4-524D52996E57},97";
const string DeviceDriverVersionKey = "{A8B865DD-2E3D-4094-AD97-E593A70C75D6},3";
const string RootContainer = "{00000000-0000-0000-FFFF-FFFFFFFFFFFF}";
const string RootQuery = "System.Devices.ContainerId:=\"" + RootContainer + "\"";
const string HalDeviceClass = "4d36e966-e325-11ce-bfc1-08002be10318";
public static async Task<ProcessorArchitecture> GetProcessorArchitectureAsync()
{
var halDevice = await GetHalDevice(ItemNameKey);
if (halDevice != null && halDevice.Properties[ItemNameKey] != null) {
var halName = halDevice.Properties[ItemNameKey].ToString();
if (halName.Contains("x64")) return ProcessorArchitecture.X64;
if (halName.Contains("ARM")) return ProcessorArchitecture.Arm;
return ProcessorArchitecture.X86;
}
return ProcessorArchitecture.Unknown;
}
public static Task<string> GetDeviceManufacturerAsync()
{
return GetRootDeviceInfoAsync(ManufacturerKey);
}
public static Task<string> GetDeviceModelAsync()
{
return GetRootDeviceInfoAsync(ModelNameKey);
}
public static Task<string> GetDeviceCategoryAsync()
{
return GetRootDeviceInfoAsync(PrimaryCategoryKey);
}
public static async Task<string> GetWindowsVersionAsync()
{
// There is no good place to get this.
// The HAL driver version number should work unless you're using a custom HAL...
var hal = await GetHalDevice(DeviceDriverVersionKey);
if (hal == null || !hal.Properties.ContainsKey(DeviceDriverVersionKey))
return null;
var versionParts = hal.Properties[DeviceDriverVersionKey].ToString().Split('.');
return string.Join(".", versionParts.Take(2).ToArray());
}
private static async Task<string> GetRootDeviceInfoAsync(string propertyKey)
{
var pnp = await PnpObject.CreateFromIdAsync(PnpObjectType.DeviceContainer,
RootContainer, new[] { propertyKey });
return (string)pnp.Properties[propertyKey];
}
private static async Task<PnpObject> GetHalDevice(params string[] properties)
{
var actualProperties = properties.Concat(new[] { DeviceClassKey });
var rootDevices = await PnpObject.FindAllAsync(PnpObjectType.Device,
actualProperties, RootQuery);
foreach (var rootDevice in rootDevices.Where(d => d.Properties != null && d.Properties.Any())) {
var lastProperty = rootDevice.Properties.Last();
if (lastProperty.Value != null)
if (lastProperty.Value.ToString().Equals(HalDeviceClass))
return rootDevice;
}
return null;
}
}