using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using XFEEDToolBox.Core.Session;
namespace XFEEDToolBox.Services;
public static class OwnedStartupCrashReporter
{
public static bool Matches(string game,string reporter,string commandLine,DateTimeOffset started,StartupCrashEvidence crash)
{
var installation=Directory.GetParent(Path.GetDirectoryName(Path.GetFullPath(game))!)?.Parent?.FullName;
if(installation is null||!string.Equals(Path.GetFullPath(reporter),Path.Combine(installation,"CrashReporter.exe"),StringComparison.OrdinalIgnoreCase)
||started<crash.At.AddSeconds(-5)||started>crash.At.AddMinutes(1))return false;
foreach(Match match in Regex.Matches(commandLine,"(?:\"(?<path>[^\"]+\\.dmp)\"|(?<path>\\S+\\.dmp))",RegexOptions.IgnoreCase))
try{if(string.Equals(Path.GetFullPath(match.Groups["path"].Value),Path.GetFullPath(crash.Path),StringComparison.OrdinalIgnoreCase))return true;}
catch(ArgumentException){}
return false;
}
public static async Task CloseAsync(string game,StartupCrashEvidence crash,CancellationToken token)
{
foreach(var reporter in Process.GetProcessesByName("CrashReporter"))
using(reporter)
{
token.ThrowIfCancellationRequested();string? path,line;DateTimeOffset started;
try{path=reporter.MainModule?.FileName;line=CommandLine(reporter);started=reporter.StartTime.ToUniversalTime();}
catch(Exception error)when(error is InvalidOperationException or System.ComponentModel.Win32Exception){continue;}
if(path is null||line is null||!Matches(game,path,line,started,crash))continue;
if(!reporter.CloseMainWindow())throw new IOException("已确认本次崩溃报告器,但它未接受关闭,未重复启动客户端。");
await reporter.WaitForExitAsync(token).WaitAsync(TimeSpan.FromSeconds(15),token).ConfigureAwait(false);
}
}
[DllImport("ntdll.dll")]private static extern int NtQueryInformationProcess(nint process,int information,nint buffer,uint length,out uint required);
private static string? CommandLine(Process process)
{
_=NtQueryInformationProcess(process.Handle,60,0,0,out var length);if(length is <16 or >65536)return null;
var buffer=Marshal.AllocHGlobal((int)length);
try
{
if(NtQueryInformationProcess(process.Handle,60,buffer,length,out _)!=0)return null;
var bytes=(ushort)Marshal.ReadInt16(buffer);var pointer=Marshal.ReadIntPtr(buffer,8);
if(bytes%2!=0||pointer<buffer||pointer>buffer+(int)length-bytes)return null;
return Marshal.PtrToStringUni(pointer,bytes/2);
}
finally{Marshal.FreeHGlobal(buffer);}
}
}
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
using XFEEDToolBox.Core.Session;
namespace XFEEDToolBox.Services;
public static class OwnedStartupCrashReporter
{
public static bool Matches(string game,string reporter,string commandLine,DateTimeOffset started,StartupCrashEvidence crash)
{
var installation=Directory.GetParent(Path.GetDirectoryName(Path.GetFullPath(game))!)?.Parent?.FullName;
if(installation is null||!string.Equals(Path.GetFullPath(reporter),Path.Combine(installation,"CrashReporter.exe"),StringComparison.OrdinalIgnoreCase)
||started<crash.At.AddSeconds(-5)||started>crash.At.AddMinutes(1))return false;
foreach(Match match in Regex.Matches(commandLine,"(?:\"(?<path>[^\"]+\\.dmp)\"|(?<path>\\S+\\.dmp))",RegexOptions.IgnoreCase))
try{if(string.Equals(Path.GetFullPath(match.Groups["path"].Value),Path.GetFullPath(crash.Path),StringComparison.OrdinalIgnoreCase))return true;}
catch(ArgumentException){}
return false;
}
public static async Task CloseAsync(string game,StartupCrashEvidence crash,CancellationToken token)
{
foreach(var reporter in Process.GetProcessesByName("CrashReporter"))
using(reporter)
{
token.ThrowIfCancellationRequested();string? path,line;DateTimeOffset started;
try{path=reporter.MainModule?.FileName;line=CommandLine(reporter);started=reporter.StartTime.ToUniversalTime();}
catch(Exception error)when(error is InvalidOperationException or System.ComponentModel.Win32Exception){continue;}
if(path is null||line is null||!Matches(game,path,line,started,crash))continue;
if(!reporter.CloseMainWindow())throw new IOException("已确认本次崩溃报告器,但它未接受关闭,未重复启动客户端。");
await reporter.WaitForExitAsync(token).WaitAsync(TimeSpan.FromSeconds(15),token).ConfigureAwait(false);
}
}
[DllImport("ntdll.dll")]private static extern int NtQueryInformationProcess(nint process,int information,nint buffer,uint length,out uint required);
private static string? CommandLine(Process process)
{
_=NtQueryInformationProcess(process.Handle,60,0,0,out var length);if(length is <16 or >65536)return null;
var buffer=Marshal.AllocHGlobal((int)length);
try
{
if(NtQueryInformationProcess(process.Handle,60,buffer,length,out _)!=0)return null;
var bytes=(ushort)Marshal.ReadInt16(buffer);var pointer=Marshal.ReadIntPtr(buffer,8);
if(bytes%2!=0||pointer<buffer||pointer>buffer+(int)length-bytes)return null;
return Marshal.PtrToStringUni(pointer,bytes/2);
}
finally{Marshal.FreeHGlobal(buffer);}
}
}