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;
}
}
}