Initial commit: IT Nexus Web-App
This commit is contained in:
89
agent-cs/Services/ApiService.cs
Normal file
89
agent-cs/Services/ApiService.cs
Normal file
@@ -0,0 +1,89 @@
|
||||
using ITNexusAgent.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
|
||||
namespace ITNexusAgent.Services;
|
||||
|
||||
public class ApiService(string serverUrl, string agentKey)
|
||||
{
|
||||
private readonly HttpClient _http = new() { Timeout = TimeSpan.FromSeconds(30) };
|
||||
private readonly string _baseUrl = serverUrl.TrimEnd('/');
|
||||
private readonly string _agentKey = agentKey;
|
||||
|
||||
private HttpRequestMessage BuildRequest(HttpMethod method, string path, object? body = null)
|
||||
{
|
||||
var req = new HttpRequestMessage(method, $"{_baseUrl}{path}");
|
||||
req.Headers.Add("X-Agent-Key", _agentKey);
|
||||
if (body != null)
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(body);
|
||||
req.Content = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
}
|
||||
return req;
|
||||
}
|
||||
|
||||
public async Task<CheckinResponse> CheckinAsync(CheckinPayload payload)
|
||||
{
|
||||
var req = BuildRequest(HttpMethod.Post, "/api/monitoring/checkin", payload);
|
||||
var resp = await _http.SendAsync(req);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
var body = await resp.Content.ReadAsStringAsync();
|
||||
return JsonConvert.DeserializeObject<CheckinResponse>(body) ?? new CheckinResponse();
|
||||
}
|
||||
|
||||
public async Task<List<Announcement>> PollAnnouncementsAsync(string hostname)
|
||||
{
|
||||
var req = BuildRequest(HttpMethod.Post, "/api/monitoring/announcements-poll",
|
||||
new { hostname });
|
||||
var resp = await _http.SendAsync(req);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
var body = await resp.Content.ReadAsStringAsync();
|
||||
var result = JsonConvert.DeserializeAnonymousType(body, new { announcements = new List<Announcement>() });
|
||||
return result?.announcements ?? [];
|
||||
}
|
||||
|
||||
public async Task AckAnnouncementAsync(int id, string hostname)
|
||||
{
|
||||
var req = BuildRequest(HttpMethod.Post, $"/api/announcements/{id}/ack-agent",
|
||||
new { hostname });
|
||||
var resp = await _http.SendAsync(req);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public async Task ReportCommandResultAsync(CommandResult result)
|
||||
{
|
||||
var req = BuildRequest(HttpMethod.Post, "/api/patch/commands/result", result);
|
||||
var resp = await _http.SendAsync(req);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public async Task<byte[]> DownloadAgentAsync()
|
||||
{
|
||||
var req = BuildRequest(HttpMethod.Get, "/api/monitoring/agent-script");
|
||||
var resp = await _http.SendAsync(req);
|
||||
return await resp.Content.ReadAsByteArrayAsync();
|
||||
}
|
||||
|
||||
public async Task<byte[]> DownloadSetupAsync()
|
||||
{
|
||||
var req = BuildRequest(HttpMethod.Get, "/api/monitoring/agent-setup");
|
||||
var resp = await _http.SendAsync(req);
|
||||
resp.EnsureSuccessStatusCode();
|
||||
return await resp.Content.ReadAsByteArrayAsync();
|
||||
}
|
||||
|
||||
public async Task SendMessageAsync(string hostname, string message)
|
||||
{
|
||||
var req = BuildRequest(HttpMethod.Post, "/api/monitoring/message",
|
||||
new { hostname, message });
|
||||
await _http.SendAsync(req);
|
||||
}
|
||||
|
||||
public async Task SendRebootRequestAsync(string hostname)
|
||||
{
|
||||
var req = BuildRequest(HttpMethod.Post, "/api/monitoring/reboot-request",
|
||||
new { hostname });
|
||||
await _http.SendAsync(req);
|
||||
}
|
||||
}
|
||||
123
agent-cs/Services/CommandExecutor.cs
Normal file
123
agent-cs/Services/CommandExecutor.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using ITNexusAgent.Models;
|
||||
using System.Diagnostics;
|
||||
using System.Management;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace ITNexusAgent.Services;
|
||||
|
||||
public class CommandExecutor(ApiService api, string hostname, string dataDir, string exePath)
|
||||
{
|
||||
private readonly ApiService _api = api;
|
||||
private readonly string _hostname = hostname;
|
||||
private readonly string _dataDir = dataDir;
|
||||
private readonly string _exePath = exePath;
|
||||
|
||||
private static void RunSchtasks(string args) =>
|
||||
Process.Start(new ProcessStartInfo("schtasks.exe", args) { CreateNoWindow = true })?.WaitForExit();
|
||||
|
||||
public async Task ExecuteAsync(PatchCommand cmd)
|
||||
{
|
||||
string result;
|
||||
try
|
||||
{
|
||||
result = cmd.Command switch
|
||||
{
|
||||
"check_updates" => await CheckUpdates(),
|
||||
"install_updates" => await InstallUpdates(),
|
||||
"reboot" => await Reboot(cmd.Id),
|
||||
"upgrade_win11" => await UpgradeWin11(),
|
||||
"update_agent" => await UpdateAgent(),
|
||||
_ => $"Unbekannter Command: {cmd.Command}"
|
||||
};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
result = $"Fehler: {ex.Message}";
|
||||
}
|
||||
|
||||
if (cmd.Command != "reboot")
|
||||
{
|
||||
await _api.ReportCommandResultAsync(new CommandResult
|
||||
{
|
||||
CommandId = cmd.Id,
|
||||
Status = "done",
|
||||
Result = result
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private Task<string> CheckUpdates()
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("UsoClient.exe", "StartScan") { CreateNoWindow = true });
|
||||
Thread.Sleep(5000);
|
||||
return Task.FromResult("Update-Scan gestartet");
|
||||
}
|
||||
|
||||
private Task<string> InstallUpdates()
|
||||
{
|
||||
Process.Start(new ProcessStartInfo("UsoClient.exe", "StartDownload") { CreateNoWindow = true });
|
||||
Thread.Sleep(3000);
|
||||
Process.Start(new ProcessStartInfo("UsoClient.exe", "StartInstall") { CreateNoWindow = true });
|
||||
return Task.FromResult("Update-Installation gestartet");
|
||||
}
|
||||
|
||||
private async Task<string> Reboot(int cmdId)
|
||||
{
|
||||
await _api.ReportCommandResultAsync(new CommandResult
|
||||
{
|
||||
CommandId = cmdId,
|
||||
Status = "done",
|
||||
Result = "Neustart wird in 60 Sekunden durchgeführt"
|
||||
});
|
||||
Process.Start("shutdown.exe", "/r /t 60 /c \"IT Nexus Patch Management - Geplanter Neustart\"");
|
||||
return "";
|
||||
}
|
||||
|
||||
private Task<string> UpgradeWin11()
|
||||
{
|
||||
var exePath = Path.Combine(_dataDir, "Win11Upgrade.exe");
|
||||
using var client = new HttpClient();
|
||||
var bytes = client.GetByteArrayAsync("https://go.microsoft.com/fwlink/?linkid=2171764").Result;
|
||||
if (bytes.Length < 1024 * 1024) return Task.FromResult("Download fehlgeschlagen");
|
||||
File.WriteAllBytes(exePath, bytes);
|
||||
|
||||
var fullUser = NotificationService.GetLoggedOnUser();
|
||||
if (!string.IsNullOrEmpty(fullUser))
|
||||
{
|
||||
RunSchtasks("/delete /tn \"IT Nexus Win11 Upgrade\" /f");
|
||||
RunSchtasks($"/create /tn \"IT Nexus Win11 Upgrade\" /tr \"\\\"{exePath}\\\" /skipeula /auto upgrade\" /sc ONCE /st {DateTime.Now.AddSeconds(20):HH:mm} /ru \"{fullUser}\" /it /rl HIGHEST /f");
|
||||
}
|
||||
else
|
||||
{
|
||||
Process.Start(new ProcessStartInfo(exePath, "/quietinstall /skipeula /auto upgrade")
|
||||
{ CreateNoWindow = true });
|
||||
}
|
||||
return Task.FromResult("running");
|
||||
}
|
||||
|
||||
private async Task<string> UpdateAgent()
|
||||
{
|
||||
var bytes = await _api.DownloadSetupAsync();
|
||||
if (bytes.Length < 512 * 1024) return "Download fehlgeschlagen - Installer zu klein";
|
||||
|
||||
var setupPath = Path.Combine(_dataDir, "IT-Nexus-Agent-Setup-Update.exe");
|
||||
var batchPath = Path.Combine(_dataDir, "update.cmd");
|
||||
|
||||
await File.WriteAllBytesAsync(setupPath, bytes);
|
||||
|
||||
// Batch: wartet kurz, führt Installer silent aus, löscht sich selbst
|
||||
var batch = $"""
|
||||
@echo off
|
||||
timeout /t 5 /nobreak > nul
|
||||
"{setupPath}" /VERYSILENT /SUPPRESSMSGBOXES /NORESTART
|
||||
del "{setupPath}" > nul
|
||||
del "%~f0"
|
||||
""";
|
||||
|
||||
await File.WriteAllTextAsync(batchPath, batch, System.Text.Encoding.ASCII);
|
||||
Process.Start(new ProcessStartInfo("cmd.exe", $"/c \"{batchPath}\"")
|
||||
{ CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden });
|
||||
|
||||
return "Update-Installer gestartet";
|
||||
}
|
||||
}
|
||||
110
agent-cs/Services/NotificationService.cs
Normal file
110
agent-cs/Services/NotificationService.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using ITNexusAgent.Models;
|
||||
using Newtonsoft.Json;
|
||||
using System.Diagnostics;
|
||||
using System.Management;
|
||||
using System.Text;
|
||||
|
||||
namespace ITNexusAgent.Services;
|
||||
|
||||
public class NotificationService
|
||||
{
|
||||
private readonly string _exePath;
|
||||
private readonly string _dataDir;
|
||||
private readonly string _shownIdsPath;
|
||||
private readonly HashSet<int> _shownIds;
|
||||
|
||||
public NotificationService(string exePath, string dataDir)
|
||||
{
|
||||
_exePath = exePath;
|
||||
_dataDir = dataDir;
|
||||
_shownIdsPath = Path.Combine(dataDir, "shown_announcements.json");
|
||||
_shownIds = LoadShownIds();
|
||||
}
|
||||
|
||||
private HashSet<int> LoadShownIds()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (File.Exists(_shownIdsPath))
|
||||
{
|
||||
var ids = JsonConvert.DeserializeObject<List<int>>(File.ReadAllText(_shownIdsPath));
|
||||
return ids != null ? new HashSet<int>(ids) : [];
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return [];
|
||||
}
|
||||
|
||||
private void SaveShownIds()
|
||||
{
|
||||
try { File.WriteAllText(_shownIdsPath, JsonConvert.SerializeObject(_shownIds.ToList())); }
|
||||
catch { }
|
||||
}
|
||||
|
||||
public void ShowAnnouncements(List<Announcement> announcements)
|
||||
{
|
||||
foreach (var ann in announcements)
|
||||
{
|
||||
if (_shownIds.Contains(ann.Id)) continue;
|
||||
_shownIds.Add(ann.Id);
|
||||
SaveShownIds();
|
||||
ShowNotification(ann);
|
||||
}
|
||||
}
|
||||
|
||||
// Vollständigen eingeloggten User aus WMI holen (z.B. "WINKEL\gruessing" oder "AzureAD\gruessing")
|
||||
public static string GetLoggedOnUser()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var searcher = new ManagementObjectSearcher("SELECT UserName FROM Win32_ComputerSystem");
|
||||
foreach (ManagementObject obj in searcher.Get())
|
||||
{
|
||||
var user = obj["UserName"]?.ToString();
|
||||
if (!string.IsNullOrEmpty(user)) return user;
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return "";
|
||||
}
|
||||
|
||||
private void ShowNotification(Announcement ann)
|
||||
{
|
||||
try
|
||||
{
|
||||
var fullUser = GetLoggedOnUser();
|
||||
if (string.IsNullOrEmpty(fullUser)) return;
|
||||
|
||||
var json = Convert.ToBase64String(Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(ann)));
|
||||
var taskName = $"ITNexus-Ann-{ann.Id}";
|
||||
|
||||
// Alten Task entfernen
|
||||
Process.Start(new ProcessStartInfo("schtasks.exe", $"/delete /tn \"{taskName}\" /f")
|
||||
{ CreateNoWindow = true })?.WaitForExit();
|
||||
|
||||
// Task mit Trigger weit in der Zukunft erstellen (damit er nicht abläuft vor /run)
|
||||
var triggerTime = DateTime.Now.AddMinutes(60).ToString("HH:mm:ss");
|
||||
var args = $"/create /tn \"{taskName}\" /tr \"\\\"{_exePath}\\\" --notify {json}\" " +
|
||||
$"/sc ONCE /st {triggerTime} /ru \"{fullUser}\" /it /f";
|
||||
var p = Process.Start(new ProcessStartInfo("schtasks.exe", args)
|
||||
{ CreateNoWindow = true, RedirectStandardError = true, UseShellExecute = false });
|
||||
p?.WaitForExit();
|
||||
|
||||
// Sofort ausführen
|
||||
var run = Process.Start(new ProcessStartInfo("schtasks.exe", $"/run /tn \"{taskName}\"")
|
||||
{ CreateNoWindow = true });
|
||||
run?.WaitForExit();
|
||||
|
||||
// Task löschen damit der +60min Trigger nicht nochmal feuert
|
||||
Thread.Sleep(3000);
|
||||
Process.Start(new ProcessStartInfo("schtasks.exe", $"/delete /tn \"{taskName}\" /f")
|
||||
{ CreateNoWindow = true })?.WaitForExit();
|
||||
|
||||
AgentWorker.Log($"NOTIFY: Task '{taskName}' für User '{fullUser}' gestartet (ExitCode={p?.ExitCode})");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
AgentWorker.Log($"NOTIFY ERROR: {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
64
agent-cs/Services/SecurityInfoService.cs
Normal file
64
agent-cs/Services/SecurityInfoService.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System.Management;
|
||||
|
||||
namespace ITNexusAgent.Services;
|
||||
|
||||
public static class SecurityInfoService
|
||||
{
|
||||
public static string GetBitlockerStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
var scope = new ManagementScope(@"\\.\root\cimv2\Security\MicrosoftVolumeEncryption");
|
||||
scope.Connect();
|
||||
using var searcher = new ManagementObjectSearcher(scope,
|
||||
new System.Management.ObjectQuery("SELECT * FROM Win32_EncryptableVolume WHERE DriveLetter='C:'"));
|
||||
foreach (ManagementObject obj in searcher.Get())
|
||||
{
|
||||
var protection = Convert.ToInt32(obj["ProtectionStatus"]);
|
||||
return protection switch
|
||||
{
|
||||
1 => "encrypted",
|
||||
2 => "unknown",
|
||||
_ => "off"
|
||||
};
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
public static (bool Enabled, int SignaturesAgeDays, DateTime? LastScan) GetDefenderStatus()
|
||||
{
|
||||
try
|
||||
{
|
||||
var scope = new ManagementScope(@"\\.\root\Microsoft\Windows\Defender");
|
||||
scope.Connect();
|
||||
using var searcher = new ManagementObjectSearcher(scope,
|
||||
new System.Management.ObjectQuery("SELECT * FROM MSFT_MpComputerStatus"));
|
||||
foreach (ManagementObject obj in searcher.Get())
|
||||
{
|
||||
var enabled = Convert.ToBoolean(obj["AMServiceEnabled"] ?? false);
|
||||
var sigDate = obj["AntivirusSignatureLastUpdated"]?.ToString();
|
||||
var lastScanAge = Convert.ToInt32(obj["QuickScanAge"] ?? 999);
|
||||
|
||||
int sigAge = 0;
|
||||
if (!string.IsNullOrEmpty(sigDate) && sigDate.Length >= 8)
|
||||
{
|
||||
if (DateTime.TryParseExact(sigDate[..14], "yyyyMMddHHmmss",
|
||||
null, System.Globalization.DateTimeStyles.None, out var sigDateTime))
|
||||
{
|
||||
sigAge = (int)(DateTime.Now - sigDateTime).TotalDays;
|
||||
}
|
||||
}
|
||||
|
||||
DateTime? lastScan = lastScanAge < 999
|
||||
? DateTime.Now.AddDays(-lastScanAge)
|
||||
: null;
|
||||
|
||||
return (enabled, sigAge, lastScan);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return (false, -1, null);
|
||||
}
|
||||
}
|
||||
267
agent-cs/Services/SystemInfoService.cs
Normal file
267
agent-cs/Services/SystemInfoService.cs
Normal file
@@ -0,0 +1,267 @@
|
||||
using ITNexusAgent.Models;
|
||||
using System.Diagnostics;
|
||||
using System.Management;
|
||||
using System.Net;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security.Principal;
|
||||
using Microsoft.Win32;
|
||||
|
||||
namespace ITNexusAgent.Services;
|
||||
|
||||
public static class SystemInfoService
|
||||
{
|
||||
public static string GetHostname() => Environment.MachineName;
|
||||
|
||||
public static string GetDomain() =>
|
||||
Environment.UserDomainName != Environment.MachineName
|
||||
? Environment.UserDomainName
|
||||
: "";
|
||||
|
||||
public static string GetOs()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion");
|
||||
var name = key?.GetValue("ProductName")?.ToString() ?? "Windows";
|
||||
var build = key?.GetValue("CurrentBuildNumber")?.ToString();
|
||||
var ubr = key?.GetValue("UBR")?.ToString();
|
||||
|
||||
// Build >= 22000 = Windows 11 — Registry ProductName kann noch "Windows 10" sagen nach Upgrade
|
||||
if (build != null && int.TryParse(build, out var buildNum) && buildNum >= 22000)
|
||||
name = name.Replace("Windows 10", "Windows 11");
|
||||
|
||||
return build != null ? $"{name} (Build {build}.{ubr})" : name;
|
||||
}
|
||||
catch { return RuntimeInformation.OSDescription; }
|
||||
}
|
||||
|
||||
public static string GetOsVersion()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion");
|
||||
return key?.GetValue("CurrentBuildNumber")?.ToString() ?? Environment.OSVersion.Version.ToString();
|
||||
}
|
||||
catch { return Environment.OSVersion.Version.ToString(); }
|
||||
}
|
||||
|
||||
public static string GetIpAddress()
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var iface in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
if (iface.OperationalStatus != OperationalStatus.Up) continue;
|
||||
if (iface.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue;
|
||||
var name = iface.Name.ToLower();
|
||||
if (name.Contains("virtual") || name.Contains("wsl") || name.Contains("vmware")) continue;
|
||||
|
||||
foreach (var addr in iface.GetIPProperties().UnicastAddresses)
|
||||
{
|
||||
if (addr.Address.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) continue;
|
||||
var ip = addr.Address.ToString();
|
||||
if (ip.StartsWith("169.254") || ip.StartsWith("172.")) continue;
|
||||
return ip;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return "";
|
||||
}
|
||||
|
||||
public static string GetMacAddress()
|
||||
{
|
||||
try
|
||||
{
|
||||
foreach (var iface in NetworkInterface.GetAllNetworkInterfaces())
|
||||
{
|
||||
if (iface.OperationalStatus != OperationalStatus.Up) continue;
|
||||
if (iface.NetworkInterfaceType == NetworkInterfaceType.Loopback) continue;
|
||||
var mac = iface.GetPhysicalAddress().ToString();
|
||||
if (mac.Length == 12)
|
||||
return string.Join(":", Enumerable.Range(0, 6).Select(i => mac.Substring(i * 2, 2)));
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return "";
|
||||
}
|
||||
|
||||
public static string GetLastUser()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var searcher = new ManagementObjectSearcher("SELECT UserName FROM Win32_ComputerSystem");
|
||||
foreach (ManagementObject obj in searcher.Get())
|
||||
{
|
||||
var user = obj["UserName"]?.ToString();
|
||||
if (!string.IsNullOrEmpty(user))
|
||||
return user.Contains('\\') ? user.Split('\\')[1] : user;
|
||||
}
|
||||
// Fallback: letztes Profil
|
||||
using var key = Registry.LocalMachine.OpenSubKey(
|
||||
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI");
|
||||
return key?.GetValue("LastLoggedOnUser")?.ToString()?.Split('\\').LastOrDefault() ?? "";
|
||||
}
|
||||
catch { return ""; }
|
||||
}
|
||||
|
||||
public static (string Model, int Cores) GetCpuInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var searcher = new ManagementObjectSearcher("SELECT Name, NumberOfCores FROM Win32_Processor");
|
||||
foreach (ManagementObject obj in searcher.Get())
|
||||
return (obj["Name"]?.ToString()?.Trim() ?? "", Convert.ToInt32(obj["NumberOfCores"]));
|
||||
}
|
||||
catch { }
|
||||
return ("", 0);
|
||||
}
|
||||
|
||||
public static double GetCpuUsage()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var counter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
|
||||
counter.NextValue();
|
||||
Thread.Sleep(800);
|
||||
return Math.Round(counter.NextValue(), 1);
|
||||
}
|
||||
catch { return 0; }
|
||||
}
|
||||
|
||||
public static (double Total, double Used) GetRamInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var searcher = new ManagementObjectSearcher("SELECT TotalVisibleMemorySize, FreePhysicalMemory FROM Win32_OperatingSystem");
|
||||
foreach (ManagementObject obj in searcher.Get())
|
||||
{
|
||||
var total = Math.Round(Convert.ToDouble(obj["TotalVisibleMemorySize"]) / (1024 * 1024), 2);
|
||||
var free = Math.Round(Convert.ToDouble(obj["FreePhysicalMemory"]) / (1024 * 1024), 2);
|
||||
return (total, Math.Round(total - free, 2));
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return (0, 0);
|
||||
}
|
||||
|
||||
public static (double Total, double Free) GetDiskInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
var drive = new DriveInfo("C");
|
||||
return (
|
||||
Math.Round(drive.TotalSize / (double)(1024 * 1024 * 1024), 2),
|
||||
Math.Round(drive.AvailableFreeSpace / (double)(1024 * 1024 * 1024), 2)
|
||||
);
|
||||
}
|
||||
catch { return (0, 0); }
|
||||
}
|
||||
|
||||
public static double GetUptimeHours()
|
||||
{
|
||||
try
|
||||
{
|
||||
var uptime = TimeSpan.FromMilliseconds(Environment.TickCount64);
|
||||
return Math.Round(uptime.TotalHours, 1);
|
||||
}
|
||||
catch { return 0; }
|
||||
}
|
||||
|
||||
public static int GetPendingUpdates()
|
||||
{
|
||||
try
|
||||
{
|
||||
var type = Type.GetTypeFromProgID("Microsoft.Update.Session");
|
||||
if (type == null) return 0;
|
||||
dynamic session = Activator.CreateInstance(type)!;
|
||||
var searcher = session.CreateUpdateSearcher();
|
||||
var result = searcher.Search("IsInstalled=0 and Type='Software'");
|
||||
return result.Updates.Count;
|
||||
}
|
||||
catch { return 0; }
|
||||
}
|
||||
|
||||
public static (bool Present, string Version, bool V2) GetTpmInfo()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var searcher = new ManagementObjectSearcher(@"root\CIMv2\Security\MicrosoftTpm",
|
||||
"SELECT * FROM Win32_Tpm");
|
||||
foreach (ManagementObject obj in searcher.Get())
|
||||
{
|
||||
var version = obj["SpecVersion"]?.ToString() ?? "";
|
||||
return (true, version, version.StartsWith("2.0"));
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return (false, "", false);
|
||||
}
|
||||
|
||||
public static bool GetSecureBoot()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var key = Registry.LocalMachine.OpenSubKey(
|
||||
@"SYSTEM\CurrentControlSet\Control\SecureBoot\State");
|
||||
return Convert.ToInt32(key?.GetValue("UEFISecureBootEnabled") ?? 0) == 1;
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
public static bool GetWin11Readiness(bool tpmV2, bool secureBoot, int cpuCores, double ramGb)
|
||||
{
|
||||
// Wenn bereits Win 11 läuft → immer true
|
||||
try
|
||||
{
|
||||
using var key = Registry.LocalMachine.OpenSubKey(@"SOFTWARE\Microsoft\Windows NT\CurrentVersion");
|
||||
var build = key?.GetValue("CurrentBuildNumber")?.ToString();
|
||||
if (build != null && int.TryParse(build, out var b) && b >= 22000) return true;
|
||||
}
|
||||
catch { }
|
||||
return tpmV2 && secureBoot && cpuCores >= 2 && ramGb >= 4;
|
||||
}
|
||||
|
||||
public static string GetHardwareSerial()
|
||||
{
|
||||
try
|
||||
{
|
||||
using var searcher = new ManagementObjectSearcher("SELECT SerialNumber FROM Win32_BIOS");
|
||||
foreach (ManagementObject obj in searcher.Get())
|
||||
return obj["SerialNumber"]?.ToString()?.Trim() ?? "";
|
||||
}
|
||||
catch { }
|
||||
return "";
|
||||
}
|
||||
|
||||
public static List<string> GetInstalledSoftware()
|
||||
{
|
||||
var apps = new HashSet<string>();
|
||||
var paths = new[]
|
||||
{
|
||||
@"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall",
|
||||
@"SOFTWARE\WOW6432Node\Microsoft\Windows\CurrentVersion\Uninstall"
|
||||
};
|
||||
|
||||
foreach (var path in paths)
|
||||
{
|
||||
try
|
||||
{
|
||||
using var key = Registry.LocalMachine.OpenSubKey(path);
|
||||
if (key == null) continue;
|
||||
foreach (var subName in key.GetSubKeyNames())
|
||||
{
|
||||
using var sub = key.OpenSubKey(subName);
|
||||
var name = sub?.GetValue("DisplayName")?.ToString();
|
||||
if (string.IsNullOrWhiteSpace(name)) continue;
|
||||
if (System.Text.RegularExpressions.Regex.IsMatch(name, @"^KB\d+")) continue;
|
||||
apps.Add(name.Trim());
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
return [.. apps.OrderBy(x => x).Take(100)];
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user