4 Commits

13 changed files with 55 additions and 336 deletions
-9
View File
@@ -9,15 +9,6 @@ public static class NvmlWrapper
[DllImport("nvml.dll", EntryPoint = "nvmlShutdown")] [DllImport("nvml.dll", EntryPoint = "nvmlShutdown")]
public static extern int NvmlShutdown(); public static extern int NvmlShutdown();
// Get device count
/* [DllImport("nvml.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern int nvmlDeviceGetCount_v2(ref uint deviceCount); */
/* public static int NvmlDeviceGetCount(ref uint deviceCount)
{
return nvmlDeviceGetCount_v2(ref deviceCount);
} */
[DllImport("nvml.dll", EntryPoint = "nvmlDeviceGetHandleByIndex_v2")] [DllImport("nvml.dll", EntryPoint = "nvmlDeviceGetHandleByIndex_v2")]
public static extern int NvmlDeviceGetHandleByIndex(int index, out IntPtr device); public static extern int NvmlDeviceGetHandleByIndex(int index, out IntPtr device);
+9 -17
View File
@@ -99,21 +99,13 @@ This file provides a C# wrapper for the NVIDIA Management Library (NVML) functio
Feel free to contribute by opening issues or submitting pull requests. Make sure to follow the project's coding style and best practices. Feel free to contribute by opening issues or submitting pull requests. Make sure to follow the project's coding style and best practices.
# devnote ## devnote
dotnet run - **Commonlly use command**:
git add . - dotnet run
git commit -m "Add steam running games" - git add .
git push origin master - git commit -m "Add steam running games"
dotnet publish -c Release -o ./publish - git push origin master
- dotnet publish -c Release -o ./publish
# devtest - **Using git bash**:
Invoke-WebRequest -Uri "http://localhost:5000/api/kill-process" -Method POST -Body "1234" -Headers @{ "X-API-KEY" = "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a" } - curl -X POST "http://localhost:5000/api/kill-process" -H "Content-Type: application/json" -d '3333333'
Invoke-WebRequest -Uri "http://192.168.50.52:5000/api/resource-usage" -Method GET -Headers @{ "X-API-KEY" = "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a" }
Use 'shutdown', 'restart', or 'cancel'.
Invoke-WebRequest -Uri "http://192.168.50.52:5000/api/force-shutdown" `
-Method POST `
-Headers @{ "X-API-KEY" = "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a" } `
-Body '{"Action": "shutdown", "DelaySeconds": 120}' `
-ContentType "application/json"
+35 -223
View File
@@ -22,38 +22,25 @@ namespace ResourceMonitorService
protected override async Task ExecuteAsync(CancellationToken stoppingToken) protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{ {
var builder = WebApplication.CreateBuilder(); var builder = WebApplication.CreateBuilder();
/* builder.WebHost.ConfigureKestrel(options =>
{
options.ListenAnyIP(5000); // Replace 5000 with your desired port
}); */
/* builder.WebHost.ConfigureKestrel(options =>
{
var url = builder.Configuration.GetValue<string>("Kestrel:Endpoints:Http:Url");
var port = url.Split(':').Last();
options.ListenAnyIP(int.Parse(port));
}); */
builder.Services.AddCors(options => builder.Services.AddCors(options =>
{ {
options.AddPolicy("AllowAllOrigins", options.AddPolicy("AllowAllOrigins",
builder => builder builder => builder.AllowAnyOrigin()
.WithOrigins("http://localhost:4200","http://192.168.50.52:4200","http://vmwin11:4200")
.AllowAnyHeader() .AllowAnyHeader()
.AllowAnyMethod()); .AllowAnyMethod());
}); });
builder.Services.AddControllers().AddNewtonsoftJson(); builder.Services.AddControllers().AddNewtonsoftJson();
// Read the API key from appsettings.json
var configuration = builder.Configuration;
var apiKey = configuration["ApiSettings:ApiKey"];
var app = builder.Build(); var app = builder.Build();
// Middleware to validate API key
// This middleware checks for the presence of the API key in the request headers
// and compares it with the expected API key from appsettings.json.
// If the API key is missing or invalid, it returns a 401 Unauthorized response.
//
/* app.Use(async (context, next) =>
{
if (!context.Request.Headers.TryGetValue("X-API-KEY", out var extractedApiKey) || extractedApiKey != apiKey)
{
context.Response.StatusCode = StatusCodes.Status401Unauthorized;
await context.Response.WriteAsync("Unauthorized: Invalid API Key");
return;
}
await next();
}); */
// Apply CORS policy to allow all origins // Apply CORS policy to allow all origins
app.UseCors("AllowAllOrigins"); app.UseCors("AllowAllOrigins");
@@ -117,89 +104,7 @@ namespace ResourceMonitorService
} }
}); });
/* curl -X POST http://localhost:5000/api/force-shutdown -d "5000" */ app.RunAsync(stoppingToken);
/* Invoke-WebRequest -Uri "http://localhost:5000/api/force-shutdown" -Method POST -Body "50000" -ContentType "text/plain" */
app.MapPost("/api/force-shutdown", async context =>
{
try
{
var requestBody = await new StreamReader(context.Request.Body).ReadToEndAsync();
var parameters = JsonConvert.DeserializeObject<dynamic>(requestBody);
string action = parameters?.Action?.ToString()?.ToLower(); // "shutdown" or "restart"
int delaySeconds = parameters?.DelaySeconds ?? 0;
// Validate action input
if (action != "shutdown" && action != "restart" && action != "cancel")
{
await context.Response.WriteAsync("Invalid action. Use 'shutdown', 'restart', or 'cancel'.");
return;
}
//if action is stop, then cancel the shutdown
if (action == "cancel")
{
var processStartInfoCancel = new ProcessStartInfo
{
FileName = "shutdown",
Arguments = "/a",
CreateNoWindow = true,
UseShellExecute = false
};
Process.Start(processStartInfoCancel);
await context.Response.WriteAsync("Shutdown cancelled.");
return;
}
// Validate delay input
if (delaySeconds < 0)
{
await context.Response.WriteAsync("Delay must be a non-negative integer.");
return;
}
// Determine the shutdown command
string shutdownCommand = action == "shutdown" ? $"/s /f /t {delaySeconds}" : $"/r /f /t {delaySeconds}";
var processStartInfo = new ProcessStartInfo
{
FileName = "shutdown",
Arguments = shutdownCommand,
CreateNoWindow = true,
UseShellExecute = false
};
Process.Start(processStartInfo);
await context.Response.WriteAsync($"{action.ToUpper()} command executed with a delay of {delaySeconds} seconds.");
}
catch (Exception ex)
{
await context.Response.WriteAsync($"An error occurred: {ex.Message}");
}
});
app.MapGet("/api/stop", async context =>
{
await context.Response.WriteAsync("Stopping the service...");
_lifetime.StopApplication();
});
app.MapGet("/", () => "Resource Monitor Service is running.");
app.MapGet("/api/current-time", () => Results.Ok(GetCurrentTime()));
app.MapGet("/api/computer-info", () => Results.Ok(GetComputerInfo()));
app.MapGet("/api/cpu-usage", () => Results.Ok(GetCpuUsage()));
app.MapGet("/api/ram-usage", () => Results.Ok(GetRamUsage()));
app.MapGet("/api/gpu-usage", () => Results.Ok(GetGpuUsage()));
app.MapGet("/api/running-game", () => Results.Ok(GetCurrentlyRunningGame()));
app.MapGet("/api/total-physical-memory", () => Results.Ok(GetTotalPhysicalMemory()));
app.MapGet("/api/total-available-memory", () => Results.Ok(new { TotalAvailableMemory = Environment.WorkingSet }));
app.MapGet("/health", () => Results.Ok("Service is healthy."));
_ = app.RunAsync(stoppingToken);
await Task.Delay(Timeout.Infinite, stoppingToken); await Task.Delay(Timeout.Infinite, stoppingToken);
} }
@@ -217,40 +122,15 @@ namespace ResourceMonitorService
private object GetCpuUsage() private object GetCpuUsage()
{ {
#pragma warning disable CA1416 // Validate platform compatibility
var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total"); var cpuCounter = new PerformanceCounter("Processor", "% Processor Time", "_Total");
#pragma warning restore CA1416 // Validate platform compatibility
#pragma warning disable CA1416 // Validate platform compatibility
cpuCounter.NextValue(); cpuCounter.NextValue();
#pragma warning restore CA1416 // Validate platform compatibility
Thread.Sleep(1000); // Wait a second to get a valid reading Thread.Sleep(1000); // Wait a second to get a valid reading
#pragma warning disable CA1416 // Validate platform compatibility
var usage = cpuCounter.NextValue(); var usage = cpuCounter.NextValue();
#pragma warning restore CA1416 // Validate platform compatibility
if (usage > 80) if (usage > 80)
{ {
// Get the current processes and sort them by CPU usage in descending order // Get the current processes and sort them by CPU usage in descending order
var processes = Process.GetProcesses() var processes = Process.GetProcesses().OrderByDescending(p => p.TotalProcessorTime);
.Select(p =>
{
try
{
return new
{
Process = p,
TotalProcessorTime = p.TotalProcessorTime
};
}
catch
{
return null; // Skip processes that throw exceptions
}
})
.Where(p => p != null)
.OrderByDescending(p => p.TotalProcessorTime)
.Select(p => p.Process)
.ToList();
// Create a new anonymous type containing the CPU usage, RAM usage, and the top 3 highest CPU-using processes // Create a new anonymous type containing the CPU usage, RAM usage, and the top 3 highest CPU-using processes
return new return new
@@ -258,18 +138,21 @@ namespace ResourceMonitorService
Usage = usage, Usage = usage,
Process1 = new Process1 = new
{ {
Id = processes.ElementAt(0).Id,
Name = processes.ElementAt(0).ProcessName, Name = processes.ElementAt(0).ProcessName,
TotalProcessorTime = processes.ElementAt(0).TotalProcessorTime, TotalProcessorTime = processes.ElementAt(0).TotalProcessorTime,
WorkingSet64 = processes.ElementAt(0).WorkingSet64 / (1024 * 1024) // Convert to MB WorkingSet64 = processes.ElementAt(0).WorkingSet64 / (1024 * 1024) // Convert to MB
}, },
Process2 = new Process2 = new
{ {
Id = processes.ElementAt(1).Id,
Name = processes.ElementAt(1).ProcessName, Name = processes.ElementAt(1).ProcessName,
TotalProcessorTime = processes.ElementAt(1).TotalProcessorTime, TotalProcessorTime = processes.ElementAt(1).TotalProcessorTime,
WorkingSet64 = processes.ElementAt(1).WorkingSet64 / (1024 * 1024) // Convert to MB WorkingSet64 = processes.ElementAt(1).WorkingSet64 / (1024 * 1024) // Convert to MB
}, },
Process3 = new Process3 = new
{ {
Id = processes.ElementAt(2).Id,
Name = processes.ElementAt(2).ProcessName, Name = processes.ElementAt(2).ProcessName,
TotalProcessorTime = processes.ElementAt(2).TotalProcessorTime, TotalProcessorTime = processes.ElementAt(2).TotalProcessorTime,
WorkingSet64 = processes.ElementAt(2).WorkingSet64 / (1024 * 1024) // Convert to MB WorkingSet64 = processes.ElementAt(2).WorkingSet64 / (1024 * 1024) // Convert to MB
@@ -285,116 +168,47 @@ namespace ResourceMonitorService
private float GetRamUsage() private float GetRamUsage()
{ {
#pragma warning disable CA1416 // Validate platform compatibility
var ramCounter = new PerformanceCounter("Memory", "Available MBytes"); var ramCounter = new PerformanceCounter("Memory", "Available MBytes");
#pragma warning restore CA1416 // Validate platform compatibility
var totalMemory = GetTotalPhysicalMemory(); var totalMemory = GetTotalPhysicalMemory();
#pragma warning disable CA1416 // Validate platform compatibility
var availableMemory = ramCounter.NextValue() * 1024 * 1024; var availableMemory = ramCounter.NextValue() * 1024 * 1024;
#pragma warning restore CA1416 // Validate platform compatibility
return (float)(totalMemory - availableMemory) / totalMemory * 100; return (float)(totalMemory - availableMemory) / totalMemory * 100;
} }
private ulong GetTotalPhysicalMemory() private ulong GetTotalPhysicalMemory()
{ {
ulong totalMemory = 0; ulong totalMemory = 0;
#pragma warning disable CA1416 // Validate platform compatibility
var searcher = new ManagementObjectSearcher("SELECT TotalPhysicalMemory FROM Win32_ComputerSystem"); var searcher = new ManagementObjectSearcher("SELECT TotalPhysicalMemory FROM Win32_ComputerSystem");
#pragma warning restore CA1416 // Validate platform compatibility
#pragma warning disable CA1416 // Validate platform compatibility
foreach (var obj in searcher.Get()) foreach (var obj in searcher.Get())
{ {
#pragma warning disable CA1416 // Validate platform compatibility
totalMemory = (ulong)obj["TotalPhysicalMemory"]; totalMemory = (ulong)obj["TotalPhysicalMemory"];
#pragma warning restore CA1416 // Validate platform compatibility
} }
#pragma warning restore CA1416 // Validate platform compatibility
return totalMemory; return totalMemory;
} }
private object GetGpuUsage() private object GetGpuUsage()
{ {
/* if (!IsNvidiaGpuPresent()) NvmlWrapper.NvmlInit();
IntPtr device;
NvmlWrapper.NvmlDeviceGetHandleByIndex(0, out device);
NvmlWrapper.NvmlUtilization utilization;
NvmlWrapper.NvmlDeviceGetUtilizationRates(device, out utilization);
uint temperature;
NvmlWrapper.NvmlDeviceGetTemperature(device, 0, out temperature);
uint fanSpeed;
NvmlWrapper.NvmlDeviceGetFanSpeed(device, out fanSpeed);
NvmlWrapper.NvmlShutdown();
return new
{ {
return new Usage = utilization.Gpu,
{ Temperature = temperature,
Usage = 0, FanSpeed = fanSpeed
Temperature = 0, };
FanSpeed = 0,
IsAvailable = false,
Message = "No NVIDIA GPU detected"
};
} */
try
{
NvmlWrapper.NvmlInit();
IntPtr device;
NvmlWrapper.NvmlDeviceGetHandleByIndex(0, out device);
NvmlWrapper.NvmlUtilization utilization;
NvmlWrapper.NvmlDeviceGetUtilizationRates(device, out utilization);
uint temperature;
NvmlWrapper.NvmlDeviceGetTemperature(device, 0, out temperature);
uint fanSpeed;
NvmlWrapper.NvmlDeviceGetFanSpeed(device, out fanSpeed);
NvmlWrapper.NvmlShutdown();
return new
{
Usage = utilization.Gpu,
Temperature = temperature,
FanSpeed = fanSpeed,
IsAvailable = false,
Error = ""
};
}
catch (Exception ex)
{
return new
{
Usage = 0,
Temperature = 0,
FanSpeed = 0,
IsAvailable = false,
Error = ex.Message
};
}
} }
/* private bool IsNvidiaGpuPresent()
{
try
{
// Method 1: Try to initialize NVML
NvmlWrapper.NvmlInit();
uint deviceCount = 0;
NvmlWrapper.NvmlDeviceGetCount(ref deviceCount);
NvmlWrapper.NvmlShutdown();
return deviceCount > 0;
}
catch
{
// Method 2: Fallback to checking using WMI
try
{
using (var searcher = new ManagementObjectSearcher("SELECT * FROM Win32_VideoController WHERE Name LIKE '%NVIDIA%'"))
{
var collection = searcher.Get();
return collection.Count > 0;
}
}
catch
{
return false;
}
}
} */
private object GetCurrentlyRunningGame() private object GetCurrentlyRunningGame()
{ {
var processes = Process.GetProcesses(); var processes = Process.GetProcesses();
@@ -403,9 +217,7 @@ namespace ResourceMonitorService
{ {
try try
{ {
#pragma warning disable CS8602 // Dereference of a possibly null reference.
var filePath = process.MainModule.FileName; var filePath = process.MainModule.FileName;
#pragma warning restore CS8602 // Dereference of a possibly null reference.
if (filePath.Contains(@"\steamapps\common\")) if (filePath.Contains(@"\steamapps\common\"))
{ {
// Extract the game directory name // Extract the game directory name
+8
View File
@@ -4,5 +4,13 @@
"Default": "Information", "Default": "Information",
"Microsoft.Hosting.Lifetime": "Information" "Microsoft.Hosting.Lifetime": "Information"
} }
},
"RunAsWindowsService": false,
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://*:5000"
}
}
} }
} }
-3
View File
@@ -12,8 +12,5 @@
"Url": "http://*:5000" "Url": "http://*:5000"
} }
} }
},
"ApiSettings": {
"ApiKey": "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a"
} }
} }
+1 -1
View File
@@ -1,6 +1,6 @@
# PowerShell script to create a new inbound rule in Windows Firewall # PowerShell script to create a new inbound rule in Windows Firewall
$port = 5000 $port = 5000
$ruleName = "ResourceMonitorServicePublish" $ruleName = "ResourceMonitorService"
if (Get-NetFirewallRule -DisplayName $ruleName) { if (Get-NetFirewallRule -DisplayName $ruleName) {
Write-Host "Rule already exists, not creating a new one" Write-Host "Rule already exists, not creating a new one"
} else { } else {
+2 -2
View File
@@ -1,4 +1,4 @@
sc create ResourceMonitorService binPath="%~dp0ResourceMonitorService.exe --windows-service" start= auto sc create ResourceMonitorServicerrr binPath= "%~dp0ResourceMonitorService.exe --windows-service" start= auto
sc description ResourceMonitorService "A service that monitors system resource usage and exposes it via a web API." sc description ResourceMonitorServicerrr "A service that monitors system resource usage and exposes it via a web API."
@@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
-19
View File
@@ -1,19 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"RunAsWindowsService": true,
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://*:5000"
}
}
},
"ApiSettings": {
"ApiKey": "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a"
}
}
@@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
-19
View File
@@ -1,19 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"RunAsWindowsService": true,
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://*:5000"
}
}
},
"ApiSettings": {
"ApiKey": "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a"
}
}
@@ -1,8 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
@@ -1,19 +0,0 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting.Lifetime": "Information"
}
},
"RunAsWindowsService": true,
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://*:5000"
}
}
},
"ApiSettings": {
"ApiKey": "b7f3e8a1-4c2d-4d9f-9a6e-2a1c5d7f8e9a"
}
}