Compare commits
5 Commits
Author | SHA1 | Date | |
---|---|---|---|
37d4f290e3 | |||
c0ccca98cf | |||
757ece5967 | |||
1ffccd6e77 | |||
3d7ff9aac0 |
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,4 +1,4 @@
|
|||||||
/.vs/
|
/.*/
|
||||||
/bin/
|
/bin/
|
||||||
/obj/
|
/obj/
|
||||||
/Properties/
|
/Properties/
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Configuration/AppSettings.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
namespace WebmrAPI.Configuration
|
namespace WebmrAPI.Configuration
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Controllers/ProcessController.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@ -22,7 +23,7 @@ namespace WebmrAPI.Controllers
|
|||||||
_monitor = monitor;
|
_monitor = monitor;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
private string GetFormattedJson<T>(T data, bool pretty)
|
internal static string GetFormattedJson<T>(T data, bool pretty)
|
||||||
{
|
{
|
||||||
var options = new JsonSerializerOptions
|
var options = new JsonSerializerOptions
|
||||||
{
|
{
|
||||||
@ -268,11 +269,53 @@ namespace WebmrAPI.Controllers
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static IEnumerable<T1> Sort<T1, T2>(IEnumerable<T1> data, Func<T1, T2> selector, bool desc)
|
[HttpGet("{pid}/windows")]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<BaseWindowInfo>))]
|
||||||
|
[ProducesResponseType(StatusCodes.Status404NotFound)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||||
|
async public Task<IActionResult> GetProcessWindowsById(int pid,
|
||||||
|
[FromQuery] bool pretty = false,
|
||||||
|
[FromQuery] string sortBy = "",
|
||||||
|
[FromQuery] bool desc = false,
|
||||||
|
[FromQuery] int limit = 0,
|
||||||
|
[FromQuery] int offset = 0
|
||||||
|
)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = await _monitor.GetProcessWindows(pid);
|
||||||
|
|
||||||
|
if (data == null)
|
||||||
|
{
|
||||||
|
return NotFound($"The process with the PID {pid} was not found or its windows could not be obtained.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!String.IsNullOrEmpty(sortBy))
|
||||||
|
{
|
||||||
|
sortBy = sortBy.ToLowerInvariant();
|
||||||
|
switch (sortBy)
|
||||||
|
{
|
||||||
|
case "title": data = ProcessController.Sort(data, p => p.Title, desc); break;
|
||||||
|
case "id": data = ProcessController.Sort(data, p => p.Id, desc); break;
|
||||||
|
default: return StatusCode(StatusCodes.Status400BadRequest, $"Unexpected search filter {sortBy}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Content(GetFormattedJson(Paginate(data, limit, offset), pretty), "application/json");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, $"An error occurred while receiving threads for PID {pid}.");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError, $"An internal server error occurred while receiving threads for PID {pid}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static IEnumerable<T1> Sort<T1, T2>(IEnumerable<T1> data, Func<T1, T2> selector, bool desc)
|
||||||
{
|
{
|
||||||
return desc ? data.OrderByDescending(selector) : data.OrderBy(selector);
|
return desc ? data.OrderByDescending(selector) : data.OrderBy(selector);
|
||||||
}
|
}
|
||||||
private IEnumerable<T> Paginate<T>(IEnumerable<T> data, int limit, int offset)
|
internal static IEnumerable<T> Paginate<T>(IEnumerable<T> data, int limit, int offset)
|
||||||
{
|
{
|
||||||
if (offset > 0) data = data.Skip(offset);
|
if (offset > 0) data = data.Skip(offset);
|
||||||
if (limit > 0) data = data.Take(limit);
|
if (limit > 0) data = data.Take(limit);
|
||||||
|
68
Controllers/WindowsController.cs
Normal file
68
Controllers/WindowsController.cs
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using WebmrAPI.Models;
|
||||||
|
using WebmrAPI.Services;
|
||||||
|
|
||||||
|
namespace WebmrAPI.Controllers
|
||||||
|
{
|
||||||
|
[ApiController]
|
||||||
|
[Route("api/v1/[controller]")]
|
||||||
|
[System.Runtime.Versioning.SupportedOSPlatform("windows")]
|
||||||
|
public class WindowsController : ControllerBase
|
||||||
|
{
|
||||||
|
private readonly ProcessMonitor _monitor;
|
||||||
|
private readonly ILogger<ProcessController> _logger;
|
||||||
|
|
||||||
|
public WindowsController(ProcessMonitor monitor, ILogger<ProcessController> logger)
|
||||||
|
{
|
||||||
|
_monitor = monitor;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet]
|
||||||
|
[ProducesResponseType(StatusCodes.Status200OK, Type = typeof(IEnumerable<WindowInfo>))]
|
||||||
|
[ProducesResponseType(StatusCodes.Status400BadRequest)]
|
||||||
|
[ProducesResponseType(StatusCodes.Status500InternalServerError)]
|
||||||
|
public IActionResult GetWindows(
|
||||||
|
[FromQuery] bool pretty = false,
|
||||||
|
[FromQuery] string sortBy = "",
|
||||||
|
[FromQuery] bool desc = false,
|
||||||
|
[FromQuery] int limit = 0,
|
||||||
|
[FromQuery] int offset = 0,
|
||||||
|
[FromQuery] string search = ""
|
||||||
|
)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var data = _monitor.GetBufferedWindows();
|
||||||
|
|
||||||
|
if (data != null && !String.IsNullOrEmpty(search))
|
||||||
|
{
|
||||||
|
data = data.Where(p => p.Title != null && p.Title.ToLowerInvariant().Contains(search.ToLowerInvariant()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data != null && !String.IsNullOrEmpty(sortBy))
|
||||||
|
{
|
||||||
|
sortBy = sortBy.ToLowerInvariant();
|
||||||
|
switch (sortBy)
|
||||||
|
{
|
||||||
|
case "pid": data = ProcessController.Sort(data, p => p.PID, desc); break;
|
||||||
|
case "title": data = ProcessController.Sort(data, p => p.Title, desc); break;
|
||||||
|
case "threadid": data = ProcessController.Sort(data, p => p.ThreadId, desc); break;
|
||||||
|
case "id": data = ProcessController.Sort(data, p => p.Id, desc); break;
|
||||||
|
default: return StatusCode(StatusCodes.Status400BadRequest, $"Unexpected search filter {sortBy}.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Content(ProcessController.GetFormattedJson(ProcessController.Paginate(data, limit, offset), pretty), "application/json");
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "An error occurred when getting the list of windows");
|
||||||
|
return StatusCode(StatusCodes.Status500InternalServerError, "An internal server error occurred when receiving windows.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
// File: Exceptions/GettingModuleInfoException.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
namespace WebmrAPI.Exceptions
|
namespace WebmrAPI.Exceptions
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Exceptions/MemoryRegionException.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
namespace WebmrAPI.Exceptions
|
namespace WebmrAPI.Exceptions
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Exceptions/ProcessAccessDeniedException.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
namespace WebmrAPI.Exceptions
|
namespace WebmrAPI.Exceptions
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Exceptions/ProcessMonitorException.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
namespace WebmrAPI.Exceptions
|
namespace WebmrAPI.Exceptions
|
||||||
{
|
{
|
||||||
|
8
LICENSE
Normal file
8
LICENSE
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
MIT License
|
||||||
|
Copyright (c) <2024-2025> <Gregory Lirent>
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@ -1,4 +1,5 @@
|
|||||||
// File: Models/MemoryRegion.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using WebmrAPI.Utils;
|
using WebmrAPI.Utils;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Models/MemoryRegionInfo.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using WebmrAPI.Utils;
|
using WebmrAPI.Utils;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Models/ProcessBaseInfo.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Models/ProcessInfo.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
@ -9,6 +10,8 @@ namespace WebmrAPI.Models
|
|||||||
[SupportedOSPlatform("windows")]
|
[SupportedOSPlatform("windows")]
|
||||||
public class ProcessInfo : ProcessBaseInfo
|
public class ProcessInfo : ProcessBaseInfo
|
||||||
{
|
{
|
||||||
|
private IEnumerable<BaseWindowInfo>? _windows;
|
||||||
|
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public LazyConcurrentContainer<MemoryRegionInfo> MemoryRegionsContainer { get; set; } = new();
|
public LazyConcurrentContainer<MemoryRegionInfo> MemoryRegionsContainer { get; set; } = new();
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
@ -33,5 +36,12 @@ namespace WebmrAPI.Models
|
|||||||
{
|
{
|
||||||
get => ThreadsContainer.Values;
|
get => ThreadsContainer.Values;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public IEnumerable<BaseWindowInfo>? Windows
|
||||||
|
{
|
||||||
|
get => LockedGet(ref _windows);
|
||||||
|
internal set => LockedSet(ref _windows, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Models/ProcessModuleInfo.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Models/ProcessThreadInfo.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
using WebmrAPI.Utils;
|
using WebmrAPI.Utils;
|
||||||
@ -9,6 +10,7 @@ namespace WebmrAPI.Models
|
|||||||
{
|
{
|
||||||
private TimeSpan _lastPTime = TimeSpan.Zero;
|
private TimeSpan _lastPTime = TimeSpan.Zero;
|
||||||
private TimeSpan _curPTime = TimeSpan.Zero;
|
private TimeSpan _curPTime = TimeSpan.Zero;
|
||||||
|
private IEnumerable<BaseWindowInfo>? _windows;
|
||||||
|
|
||||||
private int _id = 0;
|
private int _id = 0;
|
||||||
private int _currentPriority = 0;
|
private int _currentPriority = 0;
|
||||||
@ -48,5 +50,12 @@ namespace WebmrAPI.Models
|
|||||||
get => LockedGet(ref _cpuUsage);
|
get => LockedGet(ref _cpuUsage);
|
||||||
set => LockedSet(ref _cpuUsage, value);
|
set => LockedSet(ref _cpuUsage, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)]
|
||||||
|
public IEnumerable<BaseWindowInfo>? Windows
|
||||||
|
{
|
||||||
|
get => LockedGet(ref _windows);
|
||||||
|
internal set => LockedSet(ref _windows, value);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
122
Models/WindowInfo.cs
Normal file
122
Models/WindowInfo.cs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
using WebmrAPI.Utils;
|
||||||
|
|
||||||
|
namespace WebmrAPI.Models
|
||||||
|
{
|
||||||
|
public class GeometryPoint
|
||||||
|
{
|
||||||
|
internal Point _point;
|
||||||
|
public static GeometryPoint Empty { get => new GeometryPoint(Point.Empty); }
|
||||||
|
|
||||||
|
public int X { get => _point.X; }
|
||||||
|
public int Y { get => _point.Y; }
|
||||||
|
|
||||||
|
public GeometryPoint(int x, int y)
|
||||||
|
{
|
||||||
|
_point = new Point(x, y);
|
||||||
|
}
|
||||||
|
public GeometryPoint(Point point)
|
||||||
|
{
|
||||||
|
_point = point;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BaseGeometry
|
||||||
|
{
|
||||||
|
protected Rectangle _rectangle;
|
||||||
|
|
||||||
|
public int Width { get => _rectangle.Width; }
|
||||||
|
public int Height { get => _rectangle.Height; }
|
||||||
|
public static BaseGeometry Empty { get => new BaseGeometry { _rectangle = Rectangle.Empty }; }
|
||||||
|
|
||||||
|
public static Geometry FromLTRB(int left, int top, int right, int bottom)
|
||||||
|
{
|
||||||
|
return new Geometry
|
||||||
|
{
|
||||||
|
_rectangle = Rectangle.FromLTRB(left, top, right, bottom)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool Contains(GeometryPoint pt)
|
||||||
|
{
|
||||||
|
return _rectangle.Contains(pt._point);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class Geometry : BaseGeometry
|
||||||
|
{
|
||||||
|
public int X { get => _rectangle.X; }
|
||||||
|
public int Y { get => _rectangle.Y; }
|
||||||
|
public new static Geometry Empty { get => new Geometry { _rectangle = Rectangle.Empty }; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class BaseWindowInfo : ConcurrentObject
|
||||||
|
{
|
||||||
|
private IntPtr _hwnd;
|
||||||
|
private string _title = String.Empty;
|
||||||
|
private GeometryPoint _cursor = GeometryPoint.Empty;
|
||||||
|
private Geometry _gWindow = Geometry.Empty;
|
||||||
|
private BaseGeometry _gContent = BaseGeometry.Empty;
|
||||||
|
private bool _isActive;
|
||||||
|
|
||||||
|
public string Id
|
||||||
|
{
|
||||||
|
get => LockedGet(ref _hwnd).ToString();
|
||||||
|
set
|
||||||
|
{
|
||||||
|
if (!IntPtr.TryParse(value, out IntPtr hwnd))
|
||||||
|
throw new ArgumentException("Invalid window Id format. Must be a valid hwnd (IntPtr) string representation");
|
||||||
|
LockedSet(ref _hwnd, hwnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public string Title
|
||||||
|
{
|
||||||
|
get => LockedGet(ref _title);
|
||||||
|
set => LockedSet(ref _title, value);
|
||||||
|
}
|
||||||
|
public GeometryPoint CursorPosition
|
||||||
|
{
|
||||||
|
get => LockedGet(ref _cursor);
|
||||||
|
set => LockedSet(ref _cursor, value);
|
||||||
|
}
|
||||||
|
public Geometry WindowGeometry
|
||||||
|
{
|
||||||
|
get => LockedGet(ref _gWindow);
|
||||||
|
set => LockedSet(ref _gWindow, value);
|
||||||
|
}
|
||||||
|
public BaseGeometry ContentGeometry
|
||||||
|
{
|
||||||
|
get => LockedGet(ref _gContent);
|
||||||
|
set => LockedSet(ref _gContent, value);
|
||||||
|
}
|
||||||
|
public bool IsActive
|
||||||
|
{
|
||||||
|
get => LockedGet(ref _isActive);
|
||||||
|
set => LockedSet(ref _isActive, value);
|
||||||
|
}
|
||||||
|
[JsonIgnore]
|
||||||
|
public IntPtr Hwnd
|
||||||
|
{
|
||||||
|
get => LockedGet(ref _hwnd);
|
||||||
|
set => LockedSet(ref _hwnd, value);
|
||||||
|
}
|
||||||
|
public bool HasCursor { get => IsActive && ContentGeometry.Contains(CursorPosition); }
|
||||||
|
}
|
||||||
|
public class WindowInfo : BaseWindowInfo
|
||||||
|
{
|
||||||
|
private int _pid;
|
||||||
|
private int _threadId;
|
||||||
|
|
||||||
|
public int PID
|
||||||
|
{
|
||||||
|
get => LockedGet(ref _pid);
|
||||||
|
set => LockedSet(ref _pid, value);
|
||||||
|
}
|
||||||
|
public int ThreadId
|
||||||
|
{
|
||||||
|
get => LockedGet(ref _threadId);
|
||||||
|
set => LockedSet(ref _threadId, value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
// File: Program.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
@ -9,6 +10,7 @@ using WebmrAPI.Services;
|
|||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
|
builder.Services.AddWindowsService();
|
||||||
builder.Services.Configure<AppSettings>(builder.Configuration);
|
builder.Services.Configure<AppSettings>(builder.Configuration);
|
||||||
builder.Services.AddLogging(config =>
|
builder.Services.AddLogging(config =>
|
||||||
{
|
{
|
||||||
|
65
README.md
Normal file
65
README.md
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
# Process Monitoring Service (WebmrAPI)
|
||||||
|
|
||||||
|
This project implements a Windows service designed for comprehensive monitoring of running processes, their memory regions, loaded modules, and threads. It exposes this detailed system information through a RESTful API, making it accessible for remote querying, integration with other systems, or advanced analysis.
|
||||||
|
|
||||||
|
## Features
|
||||||
|
|
||||||
|
* **RESTful API:** Provides endpoints to query various aspects of process information.
|
||||||
|
|
||||||
|
* **Comprehensive Process Details:** Gathers in-depth data including:
|
||||||
|
|
||||||
|
* Basic process information (PID, Name, File Path, Command Line).
|
||||||
|
|
||||||
|
* Detailed **Memory Regions** (virtual addresses, sizes, protection flags, state, type).
|
||||||
|
|
||||||
|
* Loaded **Modules (DLLs)** with their base addresses and sizes.
|
||||||
|
|
||||||
|
* Process **Threads** including their IDs, priorities, and CPU usage.
|
||||||
|
|
||||||
|
* **Background Scanning & Caching:** Periodically scans the system for processes and caches their basic information to reduce overhead on subsequent requests. Detailed information (memory regions, modules, threads) is loaded on demand and also cached with configurable timeouts.
|
||||||
|
|
||||||
|
* **CPU Usage Calculation:** Dynamically calculates CPU utilization for processes and threads.
|
||||||
|
|
||||||
|
* **Pagination & Sorting:** API endpoints support pagination (`limit`, `offset`) and sorting by various process attributes (`sortBy`, `desc`).
|
||||||
|
|
||||||
|
* **Process Name Search:** Allows filtering the list of processes by name (partial, case-insensitive match).
|
||||||
|
|
||||||
|
* **Windows Service Deployment:** Designed to run as a robust, automatically starting Windows Service.
|
||||||
|
|
||||||
|
* **Self-Contained Deployment:** Published as a self-contained application, meaning it includes all necessary .NET runtime components and does not require .NET to be pre-installed on the target machine.
|
||||||
|
|
||||||
|
* **Swagger/OpenAPI:** Provides an interactive API documentation interface for easy exploration and testing of endpoints.
|
||||||
|
|
||||||
|
## How It Works
|
||||||
|
|
||||||
|
The `Process Monitoring Service` is an ASP.NET Core application configured to run as an `IHostedService` within a Windows Service context.
|
||||||
|
|
||||||
|
1. **Data Collection:** It leverages `System.Diagnostics.Process` and low-level Windows API calls (via P/Invoke) to gather detailed information about processes, memory regions, modules, and threads.
|
||||||
|
|
||||||
|
2. **Background Monitoring:** The `ProcessMonitor` component periodically scans for running processes and updates a buffered cache.
|
||||||
|
|
||||||
|
3. **API Exposure:** An ASP.NET Core Web API layer (`ProcessController`) provides HTTP endpoints to access the cached and on-demand collected data.
|
||||||
|
|
||||||
|
4. **Serialization:** All data is serialized to JSON with snake_case naming and pretty-printing for readability.
|
||||||
|
|
||||||
|
5. **Deployment:** The application is published as a self-contained executable for `win-x64`, allowing it to be deployed without requiring a pre-installed .NET Runtime on the target Windows machine. Installation as a Windows Service is managed via a simple batch script (`install.bat`) included in the deployment package.
|
||||||
|
|
||||||
|
## Usage (API Endpoints Examples)
|
||||||
|
|
||||||
|
* `GET /api/v1/process`: Get a paginated, sorted, and searchable list of all processes.
|
||||||
|
|
||||||
|
* Example: `/api/v1/process?pretty=true&sortBy=cpuusage&desc=true&limit=10&offset=0&search=chrome`
|
||||||
|
|
||||||
|
* `GET /api/v1/process/{pid}`: Get full details for a specific process.
|
||||||
|
|
||||||
|
* `GET /api/v1/process/{pid}/base_info`: Get basic info for a specific process.
|
||||||
|
|
||||||
|
* `GET /api/v1/process/{pid}/memory_regions`: Get memory regions for a process (with pagination/sorting).
|
||||||
|
|
||||||
|
* `GET /api/v1/process/{pid}/modules`: Get loaded modules for a process (with pagination/sorting).
|
||||||
|
|
||||||
|
* `GET /api/v1/process/{pid}/threads`: Get threads for a process (with pagination/sorting).
|
||||||
|
|
||||||
|
## Deployment
|
||||||
|
|
||||||
|
Latest releases can be found [here](https://dev.lirent.ru/lirent/winmr-api/releases).
|
@ -1,6 +1,8 @@
|
|||||||
// File: Services/ProcessMonitor.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using Microsoft.Extensions.Options;
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.Collections.Generic;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
using WebmrAPI.Configuration;
|
using WebmrAPI.Configuration;
|
||||||
using WebmrAPI.Exceptions;
|
using WebmrAPI.Exceptions;
|
||||||
@ -16,16 +18,22 @@ namespace WebmrAPI.Services
|
|||||||
private readonly ILogger<ProcessMonitor> _logger;
|
private readonly ILogger<ProcessMonitor> _logger;
|
||||||
private readonly MonitoringSettings _config;
|
private readonly MonitoringSettings _config;
|
||||||
private LazyConcurrentContainer<ProcessInfo> _processes = new();
|
private LazyConcurrentContainer<ProcessInfo> _processes = new();
|
||||||
|
private LazyConcurrentContainer<WindowInfo> _windows = new();
|
||||||
private ScanProvider _provider;
|
private ScanProvider _provider;
|
||||||
|
|
||||||
public ILogger<ProcessMonitor> Logger { get => _logger; }
|
public ILogger<ProcessMonitor> Logger { get => _logger; }
|
||||||
public MonitoringSettings Config { get => _config; }
|
public MonitoringSettings Config { get => _config; }
|
||||||
public LazyConcurrentContainer<ProcessInfo> Processes { get => _processes; }
|
public LazyConcurrentContainer<ProcessInfo> Processes { get => _processes; }
|
||||||
|
public LazyConcurrentContainer<WindowInfo> Windows { get => _windows; }
|
||||||
|
|
||||||
public IEnumerable<ProcessBaseInfo>? GetBufferedProcesses()
|
public IEnumerable<ProcessBaseInfo>? GetBufferedProcesses()
|
||||||
{
|
{
|
||||||
return _processes.Values;
|
return _processes.Values;
|
||||||
}
|
}
|
||||||
|
public IEnumerable<WindowInfo>? GetBufferedWindows()
|
||||||
|
{
|
||||||
|
return _windows.Values;
|
||||||
|
}
|
||||||
|
|
||||||
async public Task<ProcessInfo?> GetProcessDetails(int pid, ScanTarget target = ScanTarget.ProcessDetails)
|
async public Task<ProcessInfo?> GetProcessDetails(int pid, ScanTarget target = ScanTarget.ProcessDetails)
|
||||||
{
|
{
|
||||||
@ -49,6 +57,18 @@ namespace WebmrAPI.Services
|
|||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
public Task<IEnumerable<BaseWindowInfo>?> GetProcessWindows(int pid)
|
||||||
|
{
|
||||||
|
return Task.Run(() =>
|
||||||
|
{
|
||||||
|
if (_processes.Container != null && _processes.Container.TryGetValue(pid, out var info))
|
||||||
|
{
|
||||||
|
return info.Windows;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public ProcessMonitor(ILogger<ProcessMonitor> logger, IOptions<AppSettings> settings)
|
public ProcessMonitor(ILogger<ProcessMonitor> logger, IOptions<AppSettings> settings)
|
||||||
{
|
{
|
||||||
@ -87,6 +107,46 @@ namespace WebmrAPI.Services
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _provider.CreateScanTask().ScanAsync();
|
await _provider.CreateScanTask().ScanAsync();
|
||||||
|
await new WindowScanner(_provider, _windows).ScanAsync();
|
||||||
|
|
||||||
|
Dictionary<int, List<BaseWindowInfo>> pWin = new();
|
||||||
|
Dictionary<int, List<BaseWindowInfo>> tWin = new();
|
||||||
|
|
||||||
|
if (_windows.Values != null)
|
||||||
|
foreach (var window in _windows.Values)
|
||||||
|
{
|
||||||
|
List<BaseWindowInfo> p;
|
||||||
|
List<BaseWindowInfo> t;
|
||||||
|
if (!pWin.TryGetValue(window.PID, out p))
|
||||||
|
{
|
||||||
|
pWin.Add(window.PID, p = new());
|
||||||
|
}
|
||||||
|
if (!tWin.TryGetValue(window.ThreadId, out t))
|
||||||
|
{
|
||||||
|
tWin.Add(window.ThreadId, t = new());
|
||||||
|
}
|
||||||
|
|
||||||
|
p.Add(window);
|
||||||
|
t.Add(window);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_processes.Values != null)
|
||||||
|
foreach (var proc in _processes.Values)
|
||||||
|
{
|
||||||
|
if (pWin.TryGetValue(proc.PID, out var pws))
|
||||||
|
{
|
||||||
|
proc.Windows = pws;
|
||||||
|
if (proc.ThreadsContainer.Values != null)
|
||||||
|
foreach (var thread in proc.ThreadsContainer.Values)
|
||||||
|
{
|
||||||
|
if (tWin.TryGetValue(thread.ID, out var tws))
|
||||||
|
{
|
||||||
|
thread.Windows = tws;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
_logger.LogInformation($"Process buffer updated, contains {Processes.Container?.Count} processes.");
|
_logger.LogInformation($"Process buffer updated, contains {Processes.Container?.Count} processes.");
|
||||||
}
|
}
|
||||||
catch (ProcessMonitorException ex)
|
catch (ProcessMonitorException ex)
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Services/Scanners/AbstractCpuScanner.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using WebmrAPI.Utils;
|
using WebmrAPI.Utils;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Services/Scanners/AbstractScanner.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using WebmrAPI.Utils;
|
using WebmrAPI.Utils;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Services/Scanners/IScanProvider.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
namespace WebmrAPI.Services.Scanners
|
namespace WebmrAPI.Services.Scanners
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Services/Scanners/IScannable.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
namespace WebmrAPI.Services.Scanners
|
namespace WebmrAPI.Services.Scanners
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Services/Scanners/MemoryRegionScanner.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using WebmrAPI.Exceptions;
|
using WebmrAPI.Exceptions;
|
||||||
using WebmrAPI.Models;
|
using WebmrAPI.Models;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Services/Scanners/ModuleScanner.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using WebmrAPI.Exceptions;
|
using WebmrAPI.Exceptions;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Services/Scanners/ProcessScanner.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using System.Management;
|
using System.Management;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Services/Scanners/ScanProvider.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using WebmrAPI.Models;
|
using WebmrAPI.Models;
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
// File: Services/Scanners/ScanQueue.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
using System.Diagnostics;
|
|
||||||
|
|
||||||
namespace WebmrAPI.Services.Scanners
|
namespace WebmrAPI.Services.Scanners
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Services/Scanners/ScanTarget.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
namespace WebmrAPI.Services.Scanners
|
namespace WebmrAPI.Services.Scanners
|
||||||
{
|
{
|
||||||
@ -9,6 +10,7 @@ namespace WebmrAPI.Services.Scanners
|
|||||||
MemoryRegions = 0x02,
|
MemoryRegions = 0x02,
|
||||||
Modules = 0x04,
|
Modules = 0x04,
|
||||||
Threads = 0x08,
|
Threads = 0x08,
|
||||||
|
Windows = 0x10,
|
||||||
|
|
||||||
ProcessDetails = MemoryRegions | Modules | Threads,
|
ProcessDetails = MemoryRegions | Modules | Threads,
|
||||||
All = Processes | ProcessDetails
|
All = Processes | ProcessDetails
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Services/Scanners/ThreadScanner.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using System.Diagnostics;
|
using System.Diagnostics;
|
||||||
using WebmrAPI.Models;
|
using WebmrAPI.Models;
|
||||||
|
130
Services/Scanners/WindowScanner.cs
Normal file
130
Services/Scanners/WindowScanner.cs
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
using System.Drawing;
|
||||||
|
using System.Runtime.InteropServices;
|
||||||
|
using System.Text;
|
||||||
|
using WebmrAPI.Models;
|
||||||
|
using WebmrAPI.Utils;
|
||||||
|
|
||||||
|
namespace WebmrAPI.Services.Scanners
|
||||||
|
{
|
||||||
|
public class WindowScanner : AbstractScanner<WindowInfo>
|
||||||
|
{
|
||||||
|
[StructLayout(LayoutKind.Explicit)]
|
||||||
|
private struct RECT
|
||||||
|
{
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public int Left;
|
||||||
|
[FieldOffset(4)]
|
||||||
|
public int Top;
|
||||||
|
[FieldOffset(8)]
|
||||||
|
public int Right;
|
||||||
|
[FieldOffset(12)]
|
||||||
|
public int Bottom;
|
||||||
|
[FieldOffset(0)]
|
||||||
|
public int X;
|
||||||
|
[FieldOffset(4)]
|
||||||
|
public int Y;
|
||||||
|
}
|
||||||
|
private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
|
||||||
|
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool GetCursorPos(out RECT lpPoint);
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern int GetWindowText(IntPtr hwnd, StringBuilder lpString, int nMaxCount);
|
||||||
|
[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
||||||
|
private static extern int GetWindowTextLength(IntPtr hWnd);
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool GetClientRect(IntPtr hwnd, out RECT lpRect);
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool ScreenToClient(IntPtr hWnd, ref RECT lpPoint);
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool IsWindowVisible(IntPtr hWnd);
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
[return: MarshalAs(UnmanagedType.Bool)]
|
||||||
|
private static extern bool GetWindowRect(IntPtr hWnd, out RECT lpRect);
|
||||||
|
[DllImport("user32.dll", SetLastError = true)]
|
||||||
|
private static extern int GetWindowThreadProcessId(IntPtr hWnd, out int lpdwProcessId);
|
||||||
|
[DllImport("user32.dll")]
|
||||||
|
private static extern IntPtr GetForegroundWindow();
|
||||||
|
override public ScanTarget Target { get => ScanTarget.Windows; }
|
||||||
|
private bool GetWindowInfo(WindowInfo info, IntPtr hwnd)
|
||||||
|
{
|
||||||
|
int length = GetWindowTextLength(hwnd);
|
||||||
|
if (length > 0)
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder(length + 1);
|
||||||
|
GetWindowText(hwnd, sb, sb.Capacity);
|
||||||
|
info.Title = sb.ToString();
|
||||||
|
|
||||||
|
if (GetCursorPos(out RECT pt) && ScreenToClient(hwnd, ref pt))
|
||||||
|
{
|
||||||
|
info.CursorPosition = new GeometryPoint(pt.X, pt.Y);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetWindowRect(hwnd, out RECT wRect))
|
||||||
|
{
|
||||||
|
info.WindowGeometry = Geometry.FromLTRB(wRect.Left, wRect.Top, wRect.Right, wRect.Bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (GetClientRect(hwnd, out RECT cRect))
|
||||||
|
{
|
||||||
|
info.ContentGeometry = BaseGeometry.FromLTRB(cRect.Left, cRect.Top, cRect.Right, cRect.Bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
override internal Task<bool> ScanAsync(Dictionary<long, WindowInfo> data)
|
||||||
|
{
|
||||||
|
return Task.Run(() =>
|
||||||
|
{
|
||||||
|
var foreground = GetForegroundWindow();
|
||||||
|
|
||||||
|
EnumWindows(delegate (IntPtr hwnd, IntPtr lParam)
|
||||||
|
{
|
||||||
|
if (IsWindowVisible(hwnd))
|
||||||
|
{
|
||||||
|
WindowInfo? info;
|
||||||
|
|
||||||
|
if (!GetFromCacheOrNew(hwnd.ToInt64(), out info))
|
||||||
|
{
|
||||||
|
info.Hwnd = hwnd;
|
||||||
|
int threadId;
|
||||||
|
|
||||||
|
if ((threadId = GetWindowThreadProcessId(hwnd, out int pid)) != 0)
|
||||||
|
{
|
||||||
|
info.PID = pid;
|
||||||
|
info.ThreadId = threadId;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
info = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info != null && GetWindowInfo(info, hwnd))
|
||||||
|
{
|
||||||
|
info.IsActive = foreground == info.Hwnd;
|
||||||
|
data.Add(hwnd.ToInt64(), info);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}, IntPtr.Zero);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public WindowScanner(IScanProvider scanner, LazyConcurrentContainer<WindowInfo> container)
|
||||||
|
: base(scanner, container) { }
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,5 @@
|
|||||||
// File: Utils/ConcurrentObject.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
namespace WebmrAPI.Utils
|
namespace WebmrAPI.Utils
|
||||||
{
|
{
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Utils/JsonEnumConverter.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Utils/LazyConcurrentContainer.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
// File: Utils/WindowsProcess.cs
|
/* This software is licensed by the MIT License, see LICENSE file */
|
||||||
|
/* Copyright © 2024-2025 Gregory Lirent */
|
||||||
|
|
||||||
using System.Runtime.InteropServices;
|
using System.Runtime.InteropServices;
|
||||||
using System.Runtime.Versioning;
|
using System.Runtime.Versioning;
|
||||||
|
103
install.bat
Normal file
103
install.bat
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
@echo off
|
||||||
|
REM install.bat - Script for installing/uninstalling ProcessMonitoringService on the target machine.
|
||||||
|
REM
|
||||||
|
REM --- Run as Administrator ---
|
||||||
|
REM This script must be run with administrator privileges to create and manage Windows services.
|
||||||
|
REM If the script is not run as administrator, it will attempt to restart itself with elevated privileges.
|
||||||
|
|
||||||
|
setlocal
|
||||||
|
|
||||||
|
REM Check if the script is running with administrator privileges
|
||||||
|
NET SESSION >NUL 2>&1
|
||||||
|
IF %ERRORLEVEL% NEQ 0 (
|
||||||
|
echo.
|
||||||
|
echo This script must be run with administrator privileges.
|
||||||
|
echo Attempting to restart with elevated privileges...
|
||||||
|
echo.
|
||||||
|
powershell -Command "Start-Process -FilePath '%~dpnx0' -Verb RunAs"
|
||||||
|
exit /b
|
||||||
|
)
|
||||||
|
|
||||||
|
echo Script is running with administrator privileges.
|
||||||
|
|
||||||
|
REM --- Service Settings ---
|
||||||
|
SET "SERVICE_NAME=ProcessMonitoringService"
|
||||||
|
SET "SERVICE_DISPLAY_NAME=Process Monitoring Service"
|
||||||
|
SET "SERVICE_DESCRIPTION=A service for detailed monitoring processes and memory regions."
|
||||||
|
|
||||||
|
REM The service executable is in the same directory as install.bat
|
||||||
|
SET "SERVICE_EXE_PATH=%~dp0%SERVICE_NAME%.exe"
|
||||||
|
|
||||||
|
REM --- Options ---
|
||||||
|
SET "ACTION=%1"
|
||||||
|
IF "%ACTION%"=="" SET "ACTION=install"
|
||||||
|
|
||||||
|
echo.
|
||||||
|
echo --- Executing action: %ACTION% ---
|
||||||
|
echo.
|
||||||
|
|
||||||
|
IF /I "%ACTION%"=="install" GOTO :INSTALL
|
||||||
|
IF /I "%ACTION%"=="uninstall" GOTO :UNINSTALL
|
||||||
|
|
||||||
|
echo Unknown action: "%ACTION%".
|
||||||
|
echo Usage:
|
||||||
|
echo %~nx0 install - Creates and starts the service.
|
||||||
|
echo %~nx0 uninstall - Stops, deletes the service. After this, manually delete the folder.
|
||||||
|
GOTO :END
|
||||||
|
|
||||||
|
:INSTALL
|
||||||
|
echo --- Step 1: Create Windows Service ---
|
||||||
|
echo Creating service "%SERVICE_DISPLAY_NAME%" (%SERVICE_NAME%)...
|
||||||
|
IF NOT EXIST "%SERVICE_EXE_PATH%" (
|
||||||
|
echo ERROR: Service executable not found: "%SERVICE_EXE_PATH%".
|
||||||
|
GOTO :END
|
||||||
|
)
|
||||||
|
|
||||||
|
sc create "%SERVICE_NAME%" binPath="\"%SERVICE_EXE_PATH%\"" DisplayName="%SERVICE_DISPLAY_NAME%" start= auto
|
||||||
|
IF %ERRORLEVEL% NEQ 0 (
|
||||||
|
echo ERROR: Failed to create service. A service with this name might already exist or you lack permissions.
|
||||||
|
GOTO :END
|
||||||
|
)
|
||||||
|
echo Service created successfully.
|
||||||
|
|
||||||
|
echo --- Step 2: Set Service Description ---
|
||||||
|
sc description "%SERVICE_NAME%" "%SERVICE_DESCRIPTION%"
|
||||||
|
IF %ERRORLEVEL% NEQ 0 (
|
||||||
|
echo WARNING: Failed to set service description.
|
||||||
|
)
|
||||||
|
|
||||||
|
echo --- Step 3: Start Service ---
|
||||||
|
echo Starting service "%SERVICE_NAME%"...
|
||||||
|
sc start "%SERVICE_NAME%"
|
||||||
|
IF %ERRORLEVEL% NEQ 0 (
|
||||||
|
echo ERROR: Failed to start service. Check service logs.
|
||||||
|
GOTO :END
|
||||||
|
)
|
||||||
|
echo Service started successfully.
|
||||||
|
GOTO :END
|
||||||
|
|
||||||
|
:UNINSTALL
|
||||||
|
echo --- Stop Service ---
|
||||||
|
echo Stopping service "%SERVICE_NAME%"...
|
||||||
|
sc stop "%SERVICE_NAME%"
|
||||||
|
IF %ERRORLEVEL% NEQ 0 (
|
||||||
|
echo WARNING: Failed to stop service. The service might not be running or you lack permissions.
|
||||||
|
)
|
||||||
|
|
||||||
|
echo --- Delete Windows Service ---
|
||||||
|
echo Deleting service "%SERVICE_NAME%"...
|
||||||
|
sc delete "%SERVICE_NAME%"
|
||||||
|
IF %ERRORLEVEL% NEQ 0 (
|
||||||
|
echo ERROR: Failed to delete service. The service might not exist or you lack permissions.
|
||||||
|
GOTO :END
|
||||||
|
)
|
||||||
|
echo Service deleted successfully.
|
||||||
|
|
||||||
|
echo For complete removal, please manually delete the folder from which install.bat was run.
|
||||||
|
GOTO :END
|
||||||
|
|
||||||
|
:END
|
||||||
|
echo.
|
||||||
|
echo Operation completed.
|
||||||
|
endlocal
|
||||||
|
pause
|
@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
<AssemblyVersion>1.0.0.0</AssemblyVersion>
|
||||||
<FileVersion>1.0.0.0</FileVersion>
|
<FileVersion>1.0.0.0</FileVersion>
|
||||||
<Version>0.1.5</Version>
|
<Version>0.1.6</Version>
|
||||||
<Company>OpenSource</Company>
|
<Company>OpenSource</Company>
|
||||||
<Product>Process Monitoring Agent</Product>
|
<Product>Process Monitoring Agent</Product>
|
||||||
<Description>A service for detailed monitoring processes and memory regions.</Description>
|
<Description>A service for detailed monitoring processes and memory regions.</Description>
|
||||||
@ -26,5 +26,6 @@
|
|||||||
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.6" />
|
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.6" />
|
||||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||||
<PackageReference Include="System.Management" Version="8.0.0" />
|
<PackageReference Include="System.Management" Version="8.0.0" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting.WindowsServices" Version="8.0.0" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
</Project>
|
</Project>
|
||||||
|
Loading…
Reference in New Issue
Block a user