XFE Git
XFE Studio Git
Git 首页 全局搜索
XFE 主站 文档 NuGet

XFEEDToolBox

【WinUI】XFE精英危险工具箱

公开
关注 0 Fork 0 Star 0
UTF-8
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using XFEEDToolBox.Core.Session;

namespace XFEEDToolBox.Services;

public sealed record StartupNoticeControl(int Id,string ClassName,string Text);

/// <summary>Dismisses only the observed informational translation notice of the exact bootstrap client.</summary>
public static class KnownTranslationStartupNotice
{
    private const string Notice="EliteDangerous汉化补丁已加载\n\n本补丁由TowaNoah及EliteDangerous中文汉化组制作免费发布\n\n如果你是通过付费渠道获取的本补丁,请举报退款,倒卖必究";
    private static string Compact(string text)=>string.Concat(text.Where(c=>!char.IsWhiteSpace(c)));
    public static bool Matches(string title,string className,IReadOnlyList<StartupNoticeControl> controls)
        =>title=="提示"&&className=="#32770"
        &&controls.Count(c=>c.ClassName=="Button")==1
        &&controls.Any(c=>c.ClassName=="Button"&&c.Id==2&&c.Text=="确定")
        &&controls.Count(c=>c.ClassName=="Static"&&c.Text.Length>0)==1
        &&controls.Any(c=>c.ClassName=="Static"&&Compact(c.Text)==Compact(Notice));

    public static bool TryDismiss(GameSessionTarget target,CancellationToken token)
    {
        token.ThrowIfCancellationRequested();
        using var process=Process.GetProcessById(target.ProcessId);
        if(process.HasExited||process.StartTime.ToUniversalTime().Ticks!=target.StartedUtcTicks
            ||!string.Equals(process.MainModule?.FileName,target.Executable,StringComparison.OrdinalIgnoreCase))return false;
        var dismissed=false;var timedOut=false;
        EnumWindows((window,parameter)=>
        {
            if(token.IsCancellationRequested)return false;
            _=GetWindowThreadProcessId(window,out var pid);
            if(pid!=target.ProcessId||!IsWindowVisible(window))return true;
            var controls=new List<StartupNoticeControl>();nint button=0;
            EnumChildWindows(window,(child,childParameter)=>
            {
                var kind=Text(child,true);var text=Text(child,false);var id=GetDlgCtrlID(child);
                controls.Add(new(id,kind,text));if(kind=="Button"&&id==2)button=child;return true;
            },0);
            if(button==0||!Matches(Text(window,false),Text(window,true),controls))return true;
            _=GetWindowThreadProcessId(button,out var buttonPid);
            if(buttonPid!=target.ProcessId||process.HasExited)return true;
            // WM_COMMAND follows this dialog's own confirmation handler without activating it or synthesizing keyboard input.
            timedOut=SendMessageTimeout(window,0x0111,2,button,0x0002,1000,out _)==0;
            dismissed=!timedOut;return false;
        },0);
        token.ThrowIfCancellationRequested();
        if(timedOut)throw new IOException("已识别汉化启动提示,但确认操作超时,已保留当前客户端。");
        return dismissed;
    }
    private static string Text(nint window,bool className)
    {
        var text=new StringBuilder(className?256:4096);
        if(className)_=GetClassName(window,text,text.Capacity);else _=GetWindowText(window,text,text.Capacity);
        return text.ToString();
    }
    private delegate bool WindowCallback(nint window,nint parameter);
    [DllImport("user32.dll")]private static extern bool EnumWindows(WindowCallback callback,nint parameter);
    [DllImport("user32.dll")]private static extern bool EnumChildWindows(nint window,WindowCallback callback,nint parameter);
    [DllImport("user32.dll")]private static extern uint GetWindowThreadProcessId(nint window,out uint processId);
    [DllImport("user32.dll")]private static extern bool IsWindowVisible(nint window);
    [DllImport("user32.dll")]private static extern int GetDlgCtrlID(nint window);
    [DllImport("user32.dll",CharSet=CharSet.Unicode)]private static extern int GetWindowText(nint window,StringBuilder text,int capacity);
    [DllImport("user32.dll",CharSet=CharSet.Unicode)]private static extern int GetClassName(nint window,StringBuilder text,int capacity);
    [DllImport("user32.dll",CharSet=CharSet.Unicode)]private static extern nint SendMessageTimeout(nint window,uint message,nuint wParam,nint lParam,uint flags,uint milliseconds,out nuint result);
}