diff --git a/Setup/Runtime.cs b/Setup/Runtime.cs deleted file mode 100644 index ed332a6..0000000 --- a/Setup/Runtime.cs +++ /dev/null @@ -1,169 +0,0 @@ -using System.ComponentModel; -using System.Diagnostics; -using System.IO; -using System.Net; -using System.Net.Http; -using System.Runtime.InteropServices; -using System.Security.Cryptography.X509Certificates; -using Microsoft.Win32; - -namespace DeskCoreSetup; - -/// Prüft und installiert die von DeskCore benötigte "Microsoft .NET 9 Desktop Runtime". -public static class Runtime -{ - public const string DisplayName = "Microsoft .NET 9 Desktop Runtime"; - public const int Major = 9; - public const string DownloadPage = "https://dotnet.microsoft.com/de-de/download/dotnet/9.0"; - // offizieller Microsoft-Kurzlink auf die jeweils aktuelle 9.0-Version - private const string InstallerUrl = "https://aka.ms/dotnet/9.0/windowsdesktop-runtime-win-x64.exe"; - - public static bool Is64BitWindows => Environment.Is64BitOperatingSystem; - - public static bool IsInstalled - { - get - { - try - { - var dir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), - "dotnet", "shared", "Microsoft.WindowsDesktop.App"); - if (Directory.Exists(dir) && Directory.GetDirectories(dir).Any(d => Path.GetFileName(d).StartsWith($"{Major}."))) - return true; - } - catch { } - try - { - using var hklm = RegistryKey.OpenBaseKey(RegistryHive.LocalMachine, RegistryView.Registry32); - using var k = hklm.OpenSubKey(@"SOFTWARE\dotnet\Setup\InstalledVersions\x64\sharedfx\Microsoft.WindowsDesktop.App"); - if (k?.GetValueNames().Any(n => n.StartsWith($"{Major}.")) == true) return true; - } - catch { } - return false; - } - } - - /// Lädt die Laufzeit von Microsoft und installiert sie (Windows fragt einmal nach Administratorrechten). - public static void Install(Action progress) - { - ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; - var file = Path.Combine(Path.GetTempPath(), $"windowsdesktop-runtime-{Major}-win-x64.exe"); - - using (var http = new HttpClient { Timeout = TimeSpan.FromMinutes(20) }) - { - http.DefaultRequestHeaders.UserAgent.ParseAdd("DeskCore-Setup"); - using var resp = http.GetAsync(InstallerUrl, HttpCompletionOption.ResponseHeadersRead).GetAwaiter().GetResult(); - resp.EnsureSuccessStatusCode(); - long total = resp.Content.Headers.ContentLength ?? 0; - using var input = resp.Content.ReadAsStreamAsync().GetAwaiter().GetResult(); - using var output = File.Create(file); - var buffer = new byte[81920]; - long done = 0; - int read; - while ((read = input.Read(buffer, 0, buffer.Length)) > 0) - { - output.Write(buffer, 0, read); - done += read; - progress(total > 0 ? (double)done / total : 0, - total > 0 ? $"{DisplayName} wird heruntergeladen … {done / 1048576} von {total / 1048576} MB" - : $"{DisplayName} wird heruntergeladen … {done / 1048576} MB"); - } - } - - // Sicherheit: nur ausführen, wenn die Datei gültig von Microsoft signiert ist - if (!IsSignedByMicrosoft(file)) - { - try { File.Delete(file); } catch { } - throw new InvalidOperationException("Die heruntergeladene Datei ist nicht gültig von Microsoft signiert und wurde nicht ausgeführt."); - } - - progress(1, $"{DisplayName} wird installiert – bitte die Windows-Abfrage bestätigen …"); - Process p; - try - { - p = Process.Start(new ProcessStartInfo(file, "/install /quiet /norestart") { UseShellExecute = true, Verb = "runas" })!; - } - catch (Win32Exception ex) when (ex.NativeErrorCode == 1223) - { - throw new InvalidOperationException($"Die Installation von {DisplayName} wurde abgebrochen."); - } - p.WaitForExit(); - try { File.Delete(file); } catch { } - // 0 = ok, 3010/1641 = ok, Neustart empfohlen - if (p.ExitCode is not (0 or 3010 or 1641) || !IsInstalled) - throw new InvalidOperationException($"{DisplayName} konnte nicht installiert werden (Code {p.ExitCode})."); - } - - // ---------- Signaturprüfung (WinVerifyTrust) ---------- - - private static bool IsSignedByMicrosoft(string file) - { - try - { - var subject = X509Certificate.CreateFromSignedFile(file).Subject; - if (subject.IndexOf("O=Microsoft Corporation", StringComparison.OrdinalIgnoreCase) < 0) return false; - } - catch { return false; } - return VerifyTrust(file); - } - - private static bool VerifyTrust(string file) - { - var fileInfo = new WINTRUST_FILE_INFO - { - cbStruct = (uint)Marshal.SizeOf(), - pcwszFilePath = file, - }; - var pFile = Marshal.AllocHGlobal(Marshal.SizeOf()); - try - { - Marshal.StructureToPtr(fileInfo, pFile, false); - var data = new WINTRUST_DATA - { - cbStruct = (uint)Marshal.SizeOf(), - dwUIChoice = 2, // WTD_UI_NONE - fdwRevocationChecks = 0, // WTD_REVOKE_NONE - dwUnionChoice = 1, // WTD_CHOICE_FILE - pFile = pFile, - dwStateAction = 0, - dwProvFlags = 0x40, // WTD_SAFER_FLAG - }; - var action = new Guid("00AAC56B-CD44-11d0-8CC2-00C04FC295EE"); // WINTRUST_ACTION_GENERIC_VERIFY_V2 - return WinVerifyTrust(IntPtr.Zero, ref action, ref data) == 0; - } - finally - { - Marshal.FreeHGlobal(pFile); - } - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - private struct WINTRUST_FILE_INFO - { - public uint cbStruct; - public string pcwszFilePath; - public IntPtr hFile; - public IntPtr pgKnownSubject; - } - - [StructLayout(LayoutKind.Sequential)] - private struct WINTRUST_DATA - { - public uint cbStruct; - public IntPtr pPolicyCallbackData; - public IntPtr pSIPClientData; - public uint dwUIChoice; - public uint fdwRevocationChecks; - public uint dwUnionChoice; - public IntPtr pFile; - public uint dwStateAction; - public IntPtr hWVTStateData; - public IntPtr pwszURLReference; - public uint dwProvFlags; - public uint dwUIContext; - public IntPtr pSignatureSettings; - } - - [DllImport("wintrust.dll", ExactSpelling = true, SetLastError = false, CharSet = CharSet.Unicode)] - private static extern int WinVerifyTrust(IntPtr hwnd, ref Guid pgActionID, ref WINTRUST_DATA pWVTData); -}