File: Telemetry\WindowsMachineInformationProvider.cs
Web Access
Project: src\src\Aspire.Cli\Aspire.Cli.Tool.csproj (aspire)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Runtime.Versioning;
using Microsoft.Extensions.Logging;
using Microsoft.Win32;
 
namespace Aspire.Cli.Telemetry;
 
// This is copied from https://github.com/microsoft/mcp/tree/6bb4d76a63d24854efe0fa0bd96f5ab6f699ed3a/core/Azure.Mcp.Core/src/Services/Telemetry
// Keep in sync with updates there.
 
[SupportedOSPlatform("windows")]
internal class WindowsMachineInformationProvider(ILogger<WindowsMachineInformationProvider> logger)
    : MachineInformationProviderBase(logger)
{
    // Construct the parts necessary to cache the ids in the registry.
    // The final path is HKEY_CURRENT_USER/SOFTWARE/Microsoft/DeveloperTools
    private const RegistryHive Hive = RegistryHive.CurrentUser;
    private const string RegistryPathRoot = $"SOFTWARE\\{MicrosoftDirectory}\\{DeveloperToolsDirectory}";
 
    private readonly ILogger<WindowsMachineInformationProvider> _logger = logger;
 
    public override Task<string?> GetOrCreateDeviceId()
    {
        return Task.Run<string?>(() =>
        {
            try
            {
                if (TryGetRegistryValue(RegistryPathRoot, DeviceId, out var existingDeviceId))
                {
                    return existingDeviceId;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Unable to fetch {Key} value from {RegistryRoot}.", DeviceId, RegistryPathRoot);
            }
 
            var newDeviceId = GenerateDeviceId();
 
            try
            {
                if (TrySetRegistryValue(RegistryPathRoot, DeviceId, newDeviceId))
                {
                    return newDeviceId;
                }
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Unable to persist {Key} in {RegistryPath}.", DeviceId, RegistryPathRoot);
            }
 
            return newDeviceId;
        });
    }
 
    private static bool TryGetRegistryValue(string registryRoot, string keyName, out string value)
    {
        using var registry = RegistryKey.OpenBaseKey(Hive, RegistryView.Registry64);
        using var key = registry.OpenSubKey(registryRoot, writable: false);
 
        if (key == null)
        {
            value = string.Empty;
            return false;
        }
 
        var matchingKeyName = key.GetValueNames().SingleOrDefault(x => string.Equals(x, keyName), null);
        if (matchingKeyName != null)
        {
            var existingValue = key.GetValue(matchingKeyName)?.ToString();
            value = existingValue ?? string.Empty;
 
            return !string.IsNullOrEmpty(existingValue);
        }
 
        value = string.Empty;
        return false;
    }
 
    private static bool TrySetRegistryValue(string keyPath, string keyName, string value)
    {
        using var keyRoot = RegistryKey.OpenBaseKey(Hive, RegistryView.Registry64);
        using var key = keyRoot.OpenSubKey(keyPath, writable: true)
            ?? keyRoot.CreateSubKey(keyPath, RegistryKeyPermissionCheck.ReadWriteSubTree);
 
        if (key == null)
        {
            return false;
        }
 
        key.SetValue(keyName, value, RegistryValueKind.String);
 
        return true;
    }
}