175 lines
6.7 KiB
C#
175 lines
6.7 KiB
C#
using System;
|
|
using System.Diagnostics;
|
|
using System.IO;
|
|
using System.Security.Principal;
|
|
using System.Text;
|
|
using System.Text.RegularExpressions;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace D2Multi
|
|
{
|
|
/// <summary>
|
|
/// Closes D2R "DiabloII Check For Other Instances" event handles using Sysinternals Handle64
|
|
/// (same approach as Scripts/KillInstanceChecks.ps1). Requires administrator rights for Handle64.
|
|
/// </summary>
|
|
static class D2RInstanceCheckKiller
|
|
{
|
|
static readonly Regex PidLine = new Regex(
|
|
@"^D2R\.exe\s+pid:\s*(?<pid>\d+)",
|
|
RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
|
|
|
static readonly Regex EventHandleLine = new Regex(
|
|
@"^\s*(?<handle>\S+)\s*:\s*Event.*DiabloII Check For Other Instances",
|
|
RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase);
|
|
|
|
public static bool IsRunningElevated()
|
|
{
|
|
using (var id = WindowsIdentity.GetCurrent())
|
|
{
|
|
var p = new WindowsPrincipal(id);
|
|
return p.IsInRole(WindowsBuiltInRole.Administrator);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Resolves Handle64 path: explicit config path if set and exists, else handle64.exe next to D2R folder.
|
|
/// </summary>
|
|
public static string ResolveHandle64Path(string d2rDirectory, string configuredPath)
|
|
{
|
|
if (!string.IsNullOrWhiteSpace(configuredPath))
|
|
{
|
|
var t = configuredPath.Trim();
|
|
if (File.Exists(t))
|
|
return t;
|
|
}
|
|
if (string.IsNullOrEmpty(d2rDirectory))
|
|
return null;
|
|
var nextToD2r = Path.Combine(d2rDirectory, "handle64.exe");
|
|
return File.Exists(nextToD2r) ? nextToD2r : null;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enumerates D2R.exe handles and closes matching instance-check events. Returns true if Handle64 ran and exited successfully (including when nothing needed closing).
|
|
/// </summary>
|
|
public static bool TryCloseInstanceCheckHandles(string handle64Path, string d2rDirectory, out string detailLog)
|
|
{
|
|
var log = new StringBuilder();
|
|
if (string.IsNullOrEmpty(handle64Path) || !File.Exists(handle64Path))
|
|
{
|
|
detailLog = "handle64.exe not found.";
|
|
return false;
|
|
}
|
|
|
|
string listOutput;
|
|
int listExit;
|
|
if (!RunHandle64(handle64Path, d2rDirectory, "-accepteula -a -p D2R.exe", out listOutput, out listExit, log))
|
|
{
|
|
detailLog = log.ToString();
|
|
return false;
|
|
}
|
|
if (listExit != 0)
|
|
{
|
|
log.AppendLine("List pass exit code: " + listExit);
|
|
detailLog = log.ToString();
|
|
if(listOutput.Contains("No matching handles found"))
|
|
{
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
var currentPid = "";
|
|
var closed = 0;
|
|
using (var reader = new StringReader(listOutput ?? string.Empty))
|
|
{
|
|
string line;
|
|
while ((line = reader.ReadLine()) != null)
|
|
{
|
|
var pm = PidLine.Match(line);
|
|
if (pm.Success)
|
|
{
|
|
currentPid = pm.Groups["pid"].Value;
|
|
continue;
|
|
}
|
|
|
|
var hm = EventHandleLine.Match(line);
|
|
if (!hm.Success)
|
|
continue;
|
|
|
|
var handleId = hm.Groups["handle"].Value.Trim();
|
|
if (string.IsNullOrEmpty(currentPid))
|
|
{
|
|
log.AppendLine("Skip handle " + handleId + " (no prior D2R.exe pid line).");
|
|
continue;
|
|
}
|
|
|
|
log.AppendLine("Closing pid=" + currentPid + " handle=" + handleId);
|
|
string closeOut;
|
|
int closeExit;
|
|
var closeArgs = "-accepteula -p " + currentPid + " -c " + handleId + " -y";
|
|
if (!RunHandle64(handle64Path, d2rDirectory, closeArgs, out closeOut, out closeExit, log))
|
|
{
|
|
detailLog = log.ToString();
|
|
return false;
|
|
}
|
|
if (closeExit != 0)
|
|
log.AppendLine("Close exit code: " + closeExit);
|
|
closed++;
|
|
}
|
|
}
|
|
|
|
if (closed == 0)
|
|
log.AppendLine("No 'DiabloII Check For Other Instances' event handles found for D2R.exe.");
|
|
detailLog = log.ToString();
|
|
return true;
|
|
}
|
|
|
|
static bool RunHandle64(string handle64Path, string workingDirectory, string arguments,
|
|
out string stdout, out int exitCode, StringBuilder log)
|
|
{
|
|
stdout = null;
|
|
exitCode = -1;
|
|
try
|
|
{
|
|
var psi = new ProcessStartInfo
|
|
{
|
|
FileName = handle64Path,
|
|
Arguments = arguments,
|
|
WorkingDirectory = string.IsNullOrEmpty(workingDirectory) ? Path.GetDirectoryName(handle64Path) : workingDirectory,
|
|
UseShellExecute = false,
|
|
RedirectStandardOutput = true,
|
|
RedirectStandardError = true,
|
|
CreateNoWindow = true,
|
|
};
|
|
using (var p = Process.Start(psi))
|
|
{
|
|
if (p == null)
|
|
{
|
|
log.AppendLine("Process.Start returned null.");
|
|
return false;
|
|
}
|
|
var outTask = Task.Run(() => p.StandardOutput.ReadToEnd());
|
|
var errTask = Task.Run(() => p.StandardError.ReadToEnd());
|
|
if (!p.WaitForExit(120000))
|
|
{
|
|
try { p.Kill(); } catch { /* ignore */ }
|
|
log.AppendLine("Handle64 timed out.");
|
|
return false;
|
|
}
|
|
stdout = outTask.GetAwaiter().GetResult();
|
|
var err = errTask.GetAwaiter().GetResult();
|
|
exitCode = p.ExitCode;
|
|
if (!string.IsNullOrEmpty(err))
|
|
log.AppendLine("stderr: " + err.Trim());
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
log.AppendLine(ex.Message);
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
}
|
|
}
|