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 { /// /// Closes D2R "DiabloII Check For Other Instances" event handles using Sysinternals Handle64 /// (same approach as Scripts/KillInstanceChecks.ps1). Requires administrator rights for Handle64. /// static class D2RInstanceCheckKiller { static readonly Regex PidLine = new Regex( @"^D2R\.exe\s+pid:\s*(?\d+)", RegexOptions.Compiled | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase); static readonly Regex EventHandleLine = new Regex( @"^\s*(?\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); } } /// /// Resolves Handle64 path: explicit config path if set and exists, else handle64.exe next to D2R folder. /// 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; } /// /// Enumerates D2R.exe handles and closes matching instance-check events. Returns true if Handle64 ran and exited successfully (including when nothing needed closing). /// 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; } } }