#nullable disable
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using NuGet.Versioning;
using Velopack.Util;
#if !NETFRAMEWORK
using InteropArchitecture = System.Runtime.InteropServices.Architecture;
#endif
#if !NET6_0_OR_GREATER
namespace System.Runtime.Versioning
{
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
internal class SupportedOSPlatformGuardAttribute : Attribute
{
public SupportedOSPlatformGuardAttribute(string platformName) { }
}
}
#endif
#if NETFRAMEWORK || NETSTANDARD2_0_OR_GREATER
namespace System.Runtime.Versioning
{
[AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = true)]
internal class SupportedOSPlatformAttribute : Attribute
{
public SupportedOSPlatformAttribute(string platformName) { }
}
}
namespace System.Runtime.CompilerServices
{
internal static class IsExternalInit { }
}
#endif
namespace Velopack
{
// constants from winnt.h
/// The Runtime CPU Architecture
public enum RuntimeCpu : ushort
{
/// Unknown or unsupported
Unknown = 0,
/// Intel x86
x86 = 0x014c,
/// x64 / Amd64
x64 = 0x8664,
/// Arm64
arm64 = 0xAA64,
}
/// The Runtime OS
public enum RuntimeOs
{
/// Unknown or unsupported
Unknown = 0,
/// Windows
Windows = 1,
/// Linux
Linux = 2,
/// OSX
OSX = 3,
}
///
/// Convenience class which provides runtime information about the current executing process,
/// in a way that is safe in older and newer versions of the framework.
///
public static class VelopackRuntimeInfo
{
/// The current compiled Velopack display version.
public static string VelopackDisplayVersion { get; }
/// The current compiled Velopack NuGetVersion.
public static NuGetVersion VelopackNugetVersion { get; }
/// The current compiled Velopack ProductVersion.
public static NuGetVersion VelopackProductVersion { get; }
/// The path on disk of the entry assembly.
public static string EntryExePath { get; }
/// The current executing process ID.
public static uint ProcessId { get; }
/// The current machine architecture, ignoring the current process / pe architecture.
public static RuntimeCpu SystemArch { get; private set; }
/// The name of the current OS - eg. 'windows', 'linux', or 'osx'.
public static RuntimeOs SystemOs { get; private set; }
/// The current system RID.
public static string SystemRid => $"{SystemOs.GetOsShortName()}-{SystemArch}";
/// True if executing on a Windows platform.
[SupportedOSPlatformGuard("windows")]
public static bool IsWindows => SystemOs == RuntimeOs.Windows;
/// True if executing on a Linux platform.
[SupportedOSPlatformGuard("linux")]
public static bool IsLinux => SystemOs == RuntimeOs.Linux;
/// True if executing on a MacOS / OSX platform.
[SupportedOSPlatformGuard("osx")]
public static bool IsOSX => SystemOs == RuntimeOs.OSX;
internal static bool InUnitTestRunner { get; }
internal static StringComparer PathStringComparer =>
IsWindows ? StringComparer.OrdinalIgnoreCase : StringComparer.Ordinal;
internal static StringComparison PathStringComparison =>
IsWindows ? StringComparison.OrdinalIgnoreCase : StringComparison.Ordinal;
static VelopackRuntimeInfo()
{
var currentProcess = System.Diagnostics.Process.GetCurrentProcess();
EntryExePath = currentProcess.MainModule.FileName;
ProcessId = (uint)currentProcess.Id;
#if DEBUG
InUnitTestRunner = CheckForUnitTestRunner();
#endif
// get git/nuget version from nbgv metadata
VelopackProductVersion = NuGetVersion.Parse(ThisAssembly.AssemblyInformationalVersion);
#pragma warning disable CS0162
if (ThisAssembly.IsPublicRelease) {
VelopackNugetVersion = NuGetVersion.Parse(NuGetVersion.Parse(ThisAssembly.AssemblyInformationalVersion).ToNormalizedString());
} else {
VelopackNugetVersion = NuGetVersion.Parse(ThisAssembly.AssemblyInformationalVersion);
if (VelopackNugetVersion.HasMetadata) {
VelopackNugetVersion = NuGetVersion.Parse(VelopackNugetVersion.ToNormalizedString() + "-g" + VelopackNugetVersion.Metadata);
}
}
VelopackDisplayVersion = VelopackNugetVersion.ToNormalizedString() + (VelopackNugetVersion.IsPrerelease ? " (prerelease)" : "");
#pragma warning restore CS0612
// get real cpu architecture, even when virtualized by Wow64
#if NETFRAMEWORK
CheckArchitectureWindows();
#else
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
CheckArchitectureWindows();
} else {
CheckArchitectureOther();
}
#endif
}
#if DEBUG
internal static bool CheckForUnitTestRunner()
{
bool searchForAssembly(IEnumerable assemblyList)
{
return AppDomain.CurrentDomain.GetAssemblies()
.Any(x => assemblyList.Any(name => x.FullName.ToUpperInvariant().Contains(name)));
}
var testAssemblies = new[] {
"CSUNIT",
"NUNIT",
"XUNIT",
"MBUNIT",
"NBEHAVE",
};
try {
return searchForAssembly(testAssemblies);
} catch (Exception) {
return false;
}
}
#endif
///
/// Returns the shortened OS name as a string, suitable for creating an RID.
///
public static string GetOsShortName(this RuntimeOs os)
{
return os switch {
RuntimeOs.Windows => "win",
RuntimeOs.Linux => "linux",
RuntimeOs.OSX => "osx",
_ => "",
};
}
///
/// Returns the long OS name, suitable for showing to a human.
///
public static string GetOsLongName(this RuntimeOs os)
{
return os switch {
RuntimeOs.Windows => "Windows",
RuntimeOs.Linux => "Linux",
RuntimeOs.OSX => "OSX",
_ => "",
};
}
[DllImport("kernel32", EntryPoint = "IsWow64Process2", SetLastError = true)]
private static extern bool IsWow64Process2(IntPtr hProcess, out ushort pProcessMachine, out ushort pNativeMachine);
[DllImport("kernel32")]
private static extern IntPtr GetCurrentProcess();
private static void CheckArchitectureWindows()
{
SystemOs = RuntimeOs.Windows;
// find the actual OS architecture. We can't rely on the framework alone for this on Windows
// because Wow64 virtualization is good enough to trick us to believing we're running natively
// in some cases unless we use functions that are not virtualized (such as IsWow64Process2)
try {
if (IsWow64Process2(GetCurrentProcess(), out var _, out var nativeMachine)) {
if (CoreUtil.TryParseEnumU16(nativeMachine, out var val)) {
SystemArch = val;
}
}
} catch {
// don't care if this function is missing
}
if (SystemArch != RuntimeCpu.Unknown) {
return;
}
// https://docs.microsoft.com/windows/win32/winprog64/wow64-implementation-details?redirectedfrom=MSDN
var pf64compat =
Environment.GetEnvironmentVariable("PROCESSOR_ARCHITEW6432") ??
Environment.GetEnvironmentVariable("PROCESSOR_ARCHITECTURE");
if (!String.IsNullOrEmpty(pf64compat)) {
switch (pf64compat) {
case "ARM64":
SystemArch = RuntimeCpu.arm64;
break;
case "AMD64":
SystemArch = RuntimeCpu.x64;
break;
}
}
if (SystemArch != RuntimeCpu.Unknown) {
return;
}
#if NETFRAMEWORK
SystemArch = Environment.Is64BitOperatingSystem ? RuntimeCpu.x64 : RuntimeCpu.x86;
#else
CheckArchitectureOther();
#endif
}
#if !NETFRAMEWORK
private static void CheckArchitectureOther()
{
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) {
SystemOs = RuntimeOs.Windows;
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) {
SystemOs = RuntimeOs.Linux;
} else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) {
SystemOs = RuntimeOs.OSX;
}
SystemArch = RuntimeInformation.OSArchitecture switch {
InteropArchitecture.X86 => RuntimeCpu.x86,
InteropArchitecture.X64 => RuntimeCpu.x64,
InteropArchitecture.Arm64 => RuntimeCpu.arm64,
_ => RuntimeCpu.Unknown,
};
}
#endif
}
}