- Agent Compare 기준으로 런처 빠른 실행 칩, 검색 히스토리 탐색, 선택 항목 미리보기 패널을 현재 런처에 이식 - 하단 위젯 바, QuickLook(F3), 화면 OCR(F4), 관련 서비스/partial 파일을 현재 LauncherWindow/LauncherViewModel 구조에 연결 - UsageRankingService 상위 항목 조회와 SearchHistoryService를 추가해 실행 상위 경로/검색 기록이 실제 런처 동작에 반영되도록 정리 - README.md, docs/DEVELOPMENT.md에 이식 범위와 검증 결과를 2026-04-05 11:58 (KST) 기준으로 기록 검증 결과 - dotnet build src/AxCopilot/AxCopilot.csproj -c Release -v minimal -p:OutputPath=bin\\verify\\ -p:IntermediateOutputPath=obj\\verify\\ 경고 0 / 오류 0
134 lines
3.9 KiB
C#
134 lines
3.9 KiB
C#
using System.IO;
|
|
using System.Runtime.InteropServices;
|
|
|
|
namespace AxCopilot.Services;
|
|
|
|
internal sealed class PerformanceMonitorService
|
|
{
|
|
public static readonly PerformanceMonitorService Instance = new();
|
|
|
|
[StructLayout(LayoutKind.Sequential)]
|
|
private struct FILETIME
|
|
{
|
|
public uint Low;
|
|
public uint High;
|
|
}
|
|
|
|
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)]
|
|
private struct MEMORYSTATUSEX
|
|
{
|
|
public uint dwLength;
|
|
public uint dwMemoryLoad;
|
|
public ulong ullTotalPhys;
|
|
public ulong ullAvailPhys;
|
|
public ulong ullTotalPageFile;
|
|
public ulong ullAvailPageFile;
|
|
public ulong ullTotalVirtual;
|
|
public ulong ullAvailVirtual;
|
|
public ulong ullAvailExtendedVirtual;
|
|
}
|
|
|
|
[DllImport("kernel32.dll", SetLastError = true)]
|
|
private static extern bool GetSystemTimes(out FILETIME idle, out FILETIME kernel, out FILETIME user);
|
|
|
|
[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
|
|
private static extern bool GlobalMemoryStatusEx(ref MEMORYSTATUSEX lpBuffer);
|
|
|
|
public double CpuPercent { get; private set; }
|
|
public double RamPercent { get; private set; }
|
|
public string RamText { get; private set; } = "";
|
|
public double DiskCPercent { get; private set; }
|
|
public string DiskCText { get; private set; } = "";
|
|
|
|
private System.Threading.Timer? _timer;
|
|
private FILETIME _prevIdle;
|
|
private FILETIME _prevKernel;
|
|
private FILETIME _prevUser;
|
|
private bool _hasPrev;
|
|
|
|
private PerformanceMonitorService() { }
|
|
|
|
public void StartPolling()
|
|
{
|
|
if (_timer != null)
|
|
return;
|
|
|
|
_timer = new System.Threading.Timer(_ => Sample(), null, 0, 2000);
|
|
}
|
|
|
|
public void StopPolling()
|
|
{
|
|
_timer?.Dispose();
|
|
_timer = null;
|
|
_hasPrev = false;
|
|
}
|
|
|
|
private void Sample()
|
|
{
|
|
SampleCpu();
|
|
SampleRam();
|
|
SampleDisk();
|
|
}
|
|
|
|
private void SampleCpu()
|
|
{
|
|
try
|
|
{
|
|
if (!GetSystemTimes(out var idle, out var kernel, out var user))
|
|
return;
|
|
|
|
if (_hasPrev)
|
|
{
|
|
var idleDelta = ToUlong(idle) - ToUlong(_prevIdle);
|
|
var kernelDelta = ToUlong(kernel) - ToUlong(_prevKernel);
|
|
var userDelta = ToUlong(user) - ToUlong(_prevUser);
|
|
var total = kernelDelta + userDelta;
|
|
var busy = total - idleDelta;
|
|
CpuPercent = total > 0 ? Math.Clamp(100.0 * busy / total, 0, 100) : 0;
|
|
}
|
|
|
|
_prevIdle = idle;
|
|
_prevKernel = kernel;
|
|
_prevUser = user;
|
|
_hasPrev = true;
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
private void SampleRam()
|
|
{
|
|
try
|
|
{
|
|
var mem = new MEMORYSTATUSEX { dwLength = (uint)Marshal.SizeOf<MEMORYSTATUSEX>() };
|
|
if (!GlobalMemoryStatusEx(ref mem))
|
|
return;
|
|
|
|
RamPercent = mem.dwMemoryLoad;
|
|
var usedGb = (mem.ullTotalPhys - mem.ullAvailPhys) / (1024.0 * 1024 * 1024);
|
|
var totalGb = mem.ullTotalPhys / (1024.0 * 1024 * 1024);
|
|
RamText = $"{usedGb:F1}/{totalGb:F0}GB";
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
private void SampleDisk()
|
|
{
|
|
try
|
|
{
|
|
var drive = DriveInfo.GetDrives()
|
|
.FirstOrDefault(d => d.IsReady && d.Name.StartsWith("C", StringComparison.OrdinalIgnoreCase));
|
|
if (drive == null)
|
|
return;
|
|
|
|
var usedBytes = drive.TotalSize - drive.AvailableFreeSpace;
|
|
DiskCPercent = 100.0 * usedBytes / drive.TotalSize;
|
|
var usedGb = usedBytes / (1024.0 * 1024 * 1024);
|
|
var totalGb = drive.TotalSize / (1024.0 * 1024 * 1024);
|
|
DiskCText = $"C:{usedGb:F0}/{totalGb:F0}GB";
|
|
}
|
|
catch { }
|
|
}
|
|
|
|
private static ulong ToUlong(FILETIME ft) => ((ulong)ft.High << 32) | ft.Low;
|
|
}
|