v0.1.6: Added windows
This commit is contained in:
parent
c0ccca98cf
commit
37d4f290e3
@ -23,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
|
||||||
{
|
{
|
||||||
@ -269,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.");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -10,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]
|
||||||
@ -34,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -10,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;
|
||||||
@ -49,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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -2,6 +2,7 @@
|
|||||||
/* Copyright © 2024-2025 Gregory Lirent */
|
/* 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;
|
||||||
@ -17,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)
|
||||||
{
|
{
|
||||||
@ -50,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)
|
||||||
{
|
{
|
||||||
@ -88,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)
|
||||||
|
@ -10,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
|
||||||
|
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) { }
|
||||||
|
}
|
||||||
|
}
|
@ -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>
|
||||||
|
Loading…
Reference in New Issue
Block a user