|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#nullable disable
using System.Runtime.Versioning;
using System.Security.Principal;
using Microsoft.DotNet.Cli.Telemetry;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.Win32;
namespace Microsoft.DotNet.Cli.Installer.Windows;
[SupportedOSPlatform("windows")]
/// <summary>
/// Utility methods, specific to Windows.
/// </summary>
public static class WindowsUtils
{
/// <summary>
/// Generate a pseudo-random pipe name using the specified process ID, hashed MAC address and process path.
/// </summary>
/// <param name="processId">The process ID to use for generating the pipe name.</param>
/// <param name="values">Additional values to incorporate into the generated name.</param>
/// <returns>A string containing the pipe name.</returns>
public static string CreatePipeName(int processId, params string[] values)
{
// Reinvoking the host can cause differences between the original path, e.g.,
// "C:\Program Files" and "c:\Program Files". This will generate different UUID values and cause
// deadlock when the client and server are trying to connect, so always use the lower invariant of the process.
return Uuid.Create($"{processId};{Environment.ProcessPath.ToLowerInvariant()};{Sha256Hasher.Hash(MacAddressGetter.GetMacAddress())};{string.Join(";", values)}")
.ToString("B");
}
/// <summary>
/// Determines whether the current user has the Administrator role.
/// </summary>
/// <returns><see langword="true"/> if the user has the Administrator role.</returns>
public static bool IsAdministrator()
{
WindowsPrincipal principal = new(WindowsIdentity.GetCurrent());
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
/// <summary>
/// Determine if an install is running by trying to open the global _MSIExecute mutex. The mutex is
/// only set while processing the InstallExecuteSequence, AdminExecuteSequence or AdvtExecuteSequence tables.
/// </summary>
/// <returns><see langword="true" /> if another install is already running; <see langword="false"/> otherwise.</returns>
/// See the <see href="https://docs.microsoft.com/en-us/windows/win32/msi/-msiexecute-mutex">_MSIMutex</see> documentation.
public static bool InstallRunning()
{
return !Mutex.TryOpenExisting(@"Global\_MSIExecute", out _);
}
/// <summary>
/// Queries the Windows Update Agent, Component Based Servicing (CBS), and pending file rename registry keys to determine if there is a pending reboot.
/// </summary>
/// <returns><see langword="true"/> if there is a pending reboot; <see langword="false"> otherwise.</see></returns>
public static bool RebootRequired()
{
using RegistryKey localMachineKey = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry64);
using RegistryKey auKey = localMachineKey?.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\WindowsUpdate\Auto Update\RebootRequired");
using RegistryKey cbsKey = localMachineKey?.OpenSubKey(@"SOFTWARE\Microsoft\Windows\CurrentVersion\Component Based Servicing\RebootPending");
using RegistryKey sessionKey = localMachineKey?.OpenSubKey(@"SYSTEM\CurrentControlSet\Control\Session Manager");
string[] pendingFileRenameOperations = (string[])sessionKey?.GetValue("PendingFileRenameOperations") ?? [];
// Destination files for pending renames start with !\??\, whereas the source does not have the leading "!".
bool hasPendingFileRenames = pendingFileRenameOperations.Any(s => !string.IsNullOrWhiteSpace(s) && s.StartsWith(@"!\??\"));
return auKey != null || cbsKey != null || hasPendingFileRenames;
}
}
|