mirror of
https://github.com/MrUnknownDE/VRCX.git
synced 2026-04-11 10:53:52 +02:00
* fix(.NET): Stop CheckGameRunning from force checking processes I'm not really sure what I was thinking when I did that the way I did; They are not supposed to force check if they're closed after the first time...That's what the monitor is for. * feat(.NET): Add optional child process culling, clean up applauncher Add an AppApi method to set applauncher settings Enabled/KillChildrenOnExit Refactor repeated applauncher event code, move into methods * refactor: Move the pug code for every tab into its own file * refactor: Add PoC mixins to settings.pug ^& add comments for navigation Some proof of concept replacements of the categories, switches and a radio group in the General settings seection. Also added comments for each header-separated section for marginally better navigation of the file. * refactor: Move the login page to its own file * fix(.NET): Correct wrong variable being set in SetAppLauncherSettings * fix(.NET): Remove redundant/exception causing process refresh in monitor * refactor(.NET): Allow launcher to be disabled; Disabled by default. * refactor: Change screenshot helper default to true * feat: Expose new app launcher settings, add new settings category Translation keys added/removed: + view.settings.advanced.advanced.app_launcher.header + view.settings.advanced.advanced.app_launcher.folder_tooltip + view.settings.advanced.advanced.app_launcher.enable + view.settings.advanced.advanced.app_launcher.auto_close - view.settings.advanced.advanced.auto_launch - view.settings.advanced.advanced.auto_launch_tooltip * Add GPU Fix and Udon Exception Logging options, unload favorites tab when not in use * Fix GPUFix typo * Fix GPUFix typo 1 * Add logging for AVPro streams without usharp videoplayer * Lint --------- Co-authored-by: Natsumi <cmcooper123@hotmail.com>
204 lines
7.1 KiB
C#
204 lines
7.1 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Timers;
|
|
|
|
namespace VRCX
|
|
{
|
|
// I don't think this applies to our use case, but I'm leaving it here for reference.
|
|
// https://stackoverflow.com/questions/2519673/process-hasexited-returns-true-even-though-process-is-running
|
|
// "When a process is started, it is assigned a PID. If the User is then prompted with the User Account Control dialog and selects 'Yes', the process is re-started and assigned a new PID."
|
|
// There's no docs for this, but Process.HasExited also seems to be checked every time the property is accessed, so it's not cached. Which means Process.Refresh() is not needed for our use case.
|
|
|
|
/// <summary>
|
|
/// A class that monitors given processes and raises events when they are started or exited.
|
|
/// Intended to be used to monitor VRChat and VRChat-related processes.
|
|
/// </summary>
|
|
internal class ProcessMonitor
|
|
{
|
|
private readonly Dictionary<string, MonitoredProcess> monitoredProcesses;
|
|
private readonly Timer monitorProcessTimer;
|
|
|
|
static ProcessMonitor()
|
|
{
|
|
Instance = new ProcessMonitor();
|
|
}
|
|
|
|
public ProcessMonitor()
|
|
{
|
|
monitoredProcesses = new Dictionary<string, MonitoredProcess>();
|
|
|
|
monitorProcessTimer = new Timer();
|
|
monitorProcessTimer.Interval = 1000;
|
|
monitorProcessTimer.Elapsed += MonitorProcessTimer_Elapsed;
|
|
}
|
|
|
|
public static ProcessMonitor Instance { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Raised when a monitored process is started.
|
|
/// </summary>
|
|
public event Action<MonitoredProcess> ProcessStarted;
|
|
|
|
/// <summary>
|
|
/// Raised when a monitored process is exited.
|
|
/// </summary>
|
|
public event Action<MonitoredProcess> ProcessExited;
|
|
|
|
public void Init()
|
|
{
|
|
AddProcess("vrchat");
|
|
AddProcess("vrserver");
|
|
monitorProcessTimer.Start();
|
|
}
|
|
|
|
public void Exit()
|
|
{
|
|
monitorProcessTimer.Stop();
|
|
monitoredProcesses.Values.ToList().ForEach(x => x.ProcessExited());
|
|
}
|
|
|
|
private void MonitorProcessTimer_Elapsed(object sender, ElapsedEventArgs e)
|
|
{
|
|
var processesNeedingUpdate = new List<MonitoredProcess>();
|
|
|
|
// Check if any of the monitored processes have been opened or closed.
|
|
foreach (var keyValuePair in monitoredProcesses)
|
|
{
|
|
var monitoredProcess = keyValuePair.Value;
|
|
var process = monitoredProcess.Process;
|
|
var name = monitoredProcess.ProcessName;
|
|
|
|
if (monitoredProcess.IsRunning)
|
|
{
|
|
if (monitoredProcess.Process == null || monitoredProcess.Process.HasExited)
|
|
{
|
|
monitoredProcess.ProcessExited();
|
|
ProcessExited?.Invoke(monitoredProcess);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
processesNeedingUpdate.Add(monitoredProcess);
|
|
}
|
|
}
|
|
|
|
// We do it this way so we're not constantly polling for processes if we don't actually need to (aka, all processes are already accounted for).
|
|
if (processesNeedingUpdate.Count == 0)
|
|
return;
|
|
|
|
var processes = Process.GetProcesses();
|
|
foreach (var monitoredProcess in processesNeedingUpdate)
|
|
{
|
|
var process = processes.FirstOrDefault(p => string.Equals(p.ProcessName, monitoredProcess.ProcessName, StringComparison.OrdinalIgnoreCase));
|
|
if (process != null)
|
|
{
|
|
monitoredProcess.ProcessStarted(process);
|
|
ProcessStarted?.Invoke(monitoredProcess);
|
|
}
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Checks if a process if currently being monitored and if it is running.
|
|
/// </summary>
|
|
/// <param name="processName">The name of the process to check for.</param>
|
|
/// <param name="ensureCheck">If true, will manually check if the given process is running should the the monitored process not be initialized yet.</param>
|
|
/// <returns>Whether the given process is monitored and currently running.</returns>
|
|
public bool IsProcessRunning(string processName, bool ensureCheck = false)
|
|
{
|
|
processName = processName.ToLower();
|
|
if (monitoredProcesses.ContainsKey(processName))
|
|
{
|
|
var process = monitoredProcesses[processName];
|
|
|
|
if (ensureCheck && process.Process == null)
|
|
return Process.GetProcessesByName(processName).FirstOrDefault() != null;
|
|
|
|
return process.IsRunning;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a process to be monitored.
|
|
/// </summary>
|
|
/// <param name="process"></param>
|
|
public void AddProcess(Process process)
|
|
{
|
|
if (monitoredProcesses.ContainsKey(process.ProcessName.ToLower()))
|
|
return;
|
|
|
|
monitoredProcesses.Add(process.ProcessName.ToLower(), new MonitoredProcess(process));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds a process to be monitored.
|
|
/// </summary>
|
|
/// <param name="processName"></param>
|
|
public void AddProcess(string processName)
|
|
{
|
|
if (monitoredProcesses.ContainsKey(processName.ToLower()))
|
|
{
|
|
return;
|
|
}
|
|
|
|
monitoredProcesses.Add(processName, new MonitoredProcess(processName));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes a process from being monitored.
|
|
/// </summary>
|
|
/// <param name="processName"></param>
|
|
public void RemoveProcess(string processName)
|
|
{
|
|
if (monitoredProcesses.ContainsKey(processName.ToLower()))
|
|
{
|
|
monitoredProcesses.Remove(processName);
|
|
}
|
|
}
|
|
}
|
|
|
|
internal class MonitoredProcess
|
|
{
|
|
public MonitoredProcess(Process process)
|
|
{
|
|
Process = process;
|
|
ProcessName = process.ProcessName.ToLower();
|
|
|
|
if (!process.HasExited)
|
|
IsRunning = true;
|
|
}
|
|
|
|
public MonitoredProcess(string processName)
|
|
{
|
|
ProcessName = processName;
|
|
IsRunning = false;
|
|
}
|
|
|
|
public Process Process { get; private set; }
|
|
public string ProcessName { get; private set; }
|
|
public bool IsRunning { get; private set; }
|
|
|
|
public bool HasName(string processName)
|
|
{
|
|
return ProcessName.Equals(processName, StringComparison.OrdinalIgnoreCase);
|
|
}
|
|
|
|
public void ProcessExited()
|
|
{
|
|
IsRunning = false;
|
|
Process?.Dispose();
|
|
Process = null;
|
|
}
|
|
|
|
public void ProcessStarted(Process process)
|
|
{
|
|
Process = process;
|
|
ProcessName = process.ProcessName.ToLower();
|
|
IsRunning = true;
|
|
}
|
|
}
|
|
} |