diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..1ff0c42 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,63 @@ +############################################################################### +# Set default behavior to automatically normalize line endings. +############################################################################### +* text=auto + +############################################################################### +# Set default behavior for command prompt diff. +# +# This is need for earlier builds of msysgit that does not have it on by +# default for csharp files. +# Note: This is only used by command line +############################################################################### +#*.cs diff=csharp + +############################################################################### +# Set the merge driver for project and solution files +# +# Merging from the command prompt will add diff markers to the files if there +# are conflicts (Merging from VS is not affected by the settings below, in VS +# the diff markers are never inserted). Diff markers may cause the following +# file extensions to fail to load in VS. An alternative would be to treat +# these files as binary and thus will always conflict and require user +# intervention with every merge. To do so, just uncomment the entries below +############################################################################### +#*.sln merge=binary +#*.csproj merge=binary +#*.vbproj merge=binary +#*.vcxproj merge=binary +#*.vcproj merge=binary +#*.dbproj merge=binary +#*.fsproj merge=binary +#*.lsproj merge=binary +#*.wixproj merge=binary +#*.modelproj merge=binary +#*.sqlproj merge=binary +#*.wwaproj merge=binary + +############################################################################### +# behavior for image files +# +# image files are treated as binary by default. +############################################################################### +#*.jpg binary +#*.png binary +#*.gif binary + +############################################################################### +# diff behavior for common document formats +# +# Convert binary document formats to text before diffing them. This feature +# is only available from the command line. Turn it on by uncommenting the +# entries below. +############################################################################### +#*.doc diff=astextplain +#*.DOC diff=astextplain +#*.docx diff=astextplain +#*.DOCX diff=astextplain +#*.dot diff=astextplain +#*.DOT diff=astextplain +#*.pdf diff=astextplain +#*.PDF diff=astextplain +#*.rtf diff=astextplain +#*.RTF diff=astextplain diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9491a2f --- /dev/null +++ b/.gitignore @@ -0,0 +1,363 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. +## +## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore + +# User-specific files +*.rsuser +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Mono auto generated files +mono_crash.* + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +[Ww][Ii][Nn]32/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +bld/ +[Bb]in/ +[Oo]bj/ +[Oo]ut/ +[Ll]og/ +[Ll]ogs/ + +# Visual Studio 2015/2017 cache/options directory +.vs/ +# Uncomment if you have tasks that create the project's static files in wwwroot +#wwwroot/ + +# Visual Studio 2017 auto generated files +Generated\ Files/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUnit +*.VisualState.xml +TestResult.xml +nunit-*.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +# Benchmark Results +BenchmarkDotNet.Artifacts/ + +# .NET Core +project.lock.json +project.fragment.lock.json +artifacts/ + +# ASP.NET Scaffolding +ScaffoldingReadMe.txt + +# StyleCop +StyleCopReport.xml + +# Files built by Visual Studio +*_i.c +*_p.c +*_h.h +*.ilk +*.meta +*.obj +*.iobj +*.pch +*.pdb +*.ipdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*_wpftmp.csproj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opendb +*.opensdf +*.sdf +*.cachefile +*.VC.db +*.VC.VC.opendb + +# Visual Studio profiler +*.psess +*.vsp +*.vspx +*.sap + +# Visual Studio Trace Files +*.e2e + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# AxoCover is a Code Coverage Tool +.axoCover/* +!.axoCover/settings.json + +# Coverlet is a free, cross platform Code Coverage Tool +coverage*.json +coverage*.xml +coverage*.info + +# Visual Studio code coverage results +*.coverage +*.coveragexml + +# NCrunch +_NCrunch_* +.*crunch*.local.xml +nCrunchTemp_* + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# Note: Comment the next line if you want to checkin your web deploy settings, +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# Microsoft Azure Web App publish settings. Comment the next line if you want to +# checkin your Azure Web App publish settings, but sensitive information contained +# in these scripts will be unencrypted +PublishScripts/ + +# NuGet Packages +*.nupkg +# NuGet Symbol Packages +*.snupkg +# The packages folder can be ignored because of Package Restore +**/[Pp]ackages/* +# except build/, which is used as an MSBuild target. +!**/[Pp]ackages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/[Pp]ackages/repositories.config +# NuGet v3's project.json files produces more ignorable files +*.nuget.props +*.nuget.targets + +# Microsoft Azure Build Output +csx/ +*.build.csdef + +# Microsoft Azure Emulator +ecf/ +rcf/ + +# Windows Store app package directories and files +AppPackages/ +BundleArtifacts/ +Package.StoreAssociation.xml +_pkginfo.txt +*.appx +*.appxbundle +*.appxupload + +# Visual Studio cache files +# files ending in .cache can be ignored +*.[Cc]ache +# but keep track of directories ending in .cache +!?*.[Cc]ache/ + +# Others +ClientBin/ +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.jfm +*.pfx +*.publishsettings +orleans.codegen.cs + +# Including strong name files can present a security risk +# (https://github.com/github/gitignore/pull/2483#issue-259490424) +#*.snk + +# Since there are multiple workflows, uncomment next line to ignore bower_components +# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) +#bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm +ServiceFabricBackup/ +*.rptproj.bak + +# SQL Server files +*.mdf +*.ldf +*.ndf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings +*.rptproj.rsuser +*- [Bb]ackup.rdl +*- [Bb]ackup ([0-9]).rdl +*- [Bb]ackup ([0-9][0-9]).rdl + +# Microsoft Fakes +FakesAssemblies/ + +# GhostDoc plugin setting file +*.GhostDoc.xml + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat +node_modules/ + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# Visual Studio 6 auto-generated workspace file (contains which files were open etc.) +*.vbw + +# Visual Studio LightSwitch build output +**/*.HTMLClient/GeneratedArtifacts +**/*.DesktopClient/GeneratedArtifacts +**/*.DesktopClient/ModelManifest.xml +**/*.Server/GeneratedArtifacts +**/*.Server/ModelManifest.xml +_Pvt_Extensions + +# Paket dependency manager +.paket/paket.exe +paket-files/ + +# FAKE - F# Make +.fake/ + +# CodeRush personal settings +.cr/personal + +# Python Tools for Visual Studio (PTVS) +__pycache__/ +*.pyc + +# Cake - Uncomment if you are using it +# tools/** +# !tools/packages.config + +# Tabs Studio +*.tss + +# Telerik's JustMock configuration file +*.jmconfig + +# BizTalk build output +*.btp.cs +*.btm.cs +*.odx.cs +*.xsd.cs + +# OpenCover UI analysis results +OpenCover/ + +# Azure Stream Analytics local run output +ASALocalRun/ + +# MSBuild Binary and Structured Log +*.binlog + +# NVidia Nsight GPU debugger configuration file +*.nvuser + +# MFractors (Xamarin productivity tool) working folder +.mfractor/ + +# Local History for Visual Studio +.localhistory/ + +# BeatPulse healthcheck temp database +healthchecksdb + +# Backup folder for Package Reference Convert tool in Visual Studio 2017 +MigrationBackup/ + +# Ionide (cross platform F# VS Code tools) working folder +.ionide/ + +# Fody - auto-generated XML schema +FodyWeavers.xsd \ No newline at end of file diff --git a/App.config b/App.config new file mode 100644 index 0000000..193aecc --- /dev/null +++ b/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Assets/screenshot.png b/Assets/screenshot.png new file mode 100644 index 0000000..161a0ea Binary files /dev/null and b/Assets/screenshot.png differ diff --git a/ErrorDict/FixLeagueClientWindowError.cs b/ErrorDict/FixLeagueClientWindowError.cs new file mode 100644 index 0000000..6397db3 --- /dev/null +++ b/ErrorDict/FixLeagueClientWindowError.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Fix_LCU_Window.ErrorDict +{ + internal class FixLeagueClientWindowError + { + public static Dictionary ErrorDict = new Dictionary + { + { 1, "成功恢复客户端窗口大小" }, + { 0, "未发现客户端窗口出现异常" }, + { -1, "未检测到客户端窗口,可能客户端正处于最最下化状态" }, + { -2, "未能获取到LCUAPI必要参数" }, + { -3, "未能获取到客户端缩放比例" } + }; + } +} diff --git a/Fix-LCU-Window.csproj b/Fix-LCU-Window.csproj new file mode 100644 index 0000000..7798590 --- /dev/null +++ b/Fix-LCU-Window.csproj @@ -0,0 +1,65 @@ + + + + + Debug + AnyCPU + {4E9F5409-2D0B-40CC-9C76-A33C7C68B8E8} + Exe + Fix_LCU_Window + Fix-LCU-Window + v4.8 + 512 + true + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + app.manifest + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Fix-LCU-Window.sln b/Fix-LCU-Window.sln new file mode 100644 index 0000000..0d7d83a --- /dev/null +++ b/Fix-LCU-Window.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.7.34031.279 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Fix-LCU-Window", "Fix-LCU-Window.csproj", "{4E9F5409-2D0B-40CC-9C76-A33C7C68B8E8}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {4E9F5409-2D0B-40CC-9C76-A33C7C68B8E8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4E9F5409-2D0B-40CC-9C76-A33C7C68B8E8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4E9F5409-2D0B-40CC-9C76-A33C7C68B8E8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4E9F5409-2D0B-40CC-9C76-A33C7C68B8E8}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {503D08E3-02D3-4E50-8F1B-9DE2B05DB7D2} + EndGlobalSection +EndGlobal diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..7b153b7 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2023 Coooookies + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Program.cs b/Program.cs new file mode 100644 index 0000000..e9b82af --- /dev/null +++ b/Program.cs @@ -0,0 +1,245 @@ +using System; +using System.Threading.Tasks; +using System.Windows.Forms; +using Fix_LCU_Window.Util; +using Fix_LCU_Window.ErrorDict; +using static Fix_LCU_Window.Util.ProcessControl; +using static Fix_LCU_Window.Util.LeagueClientAPI; + +namespace Fix_LCU_Window +{ + class Program + { + static LeagueClientAPI GetLCU() + { + var LeagueClientId = GetProcessId("LeagueClientUx"); + var LeagueClientUxCommandLine = GetCommandLineByProcessId(LeagueClientId); + var LeagueClientUxArgs = CommandLineParser(LeagueClientUxCommandLine); + + if (!LeagueClientUxArgs.Available) + { + return null; + } + + // Return a new instance of LeagueClientAPI + return new LeagueClientAPI(LeagueClientUxArgs.Port, LeagueClientUxArgs.Token); + } + + static (IntPtr LeagueClientWindowHWnd, IntPtr LeagueClientWindowCefHWnd) GetLeagueClientWindowHandle() + { + IntPtr LeagueClientWindowHWnd = FindWindow("RCLIENT", "League of Legends"); + IntPtr LeagueClientWindowCefHWnd = FindWindowEx(LeagueClientWindowHWnd, IntPtr.Zero, "CefBrowserWindow", null); + + // Return a tuple of the two window handles + return (LeagueClientWindowHWnd, LeagueClientWindowCefHWnd); + } + + static async Task FixLeagueClientWindow(bool forced = false) + { + + var LeagueClientWindowRect = new RECT(); + var LeagueClientWindow = GetLeagueClientWindowHandle(); + var LeagueClientAPIClient = GetLCU(); + + if ( + LeagueClientWindow.LeagueClientWindowHWnd == IntPtr.Zero || + LeagueClientWindow.LeagueClientWindowCefHWnd == IntPtr.Zero + ) + { + return -1; // Failed to get leagueclient window handle + } + + if (LeagueClientAPIClient == null) + { + return -2; // Failed to get leagueclient api instance + } + + GetWindowRect(LeagueClientWindow.LeagueClientWindowHWnd, ref LeagueClientWindowRect); + + var LeagueClientWindowWidth = LeagueClientWindowRect.Right - LeagueClientWindowRect.Left; + var LeagueClientWindowHeight = LeagueClientWindowRect.Bottom - LeagueClientWindowRect.Top; + var LeagueClientMinimized = IsMinimalized(LeagueClientWindow.LeagueClientWindowHWnd); + + if ((LeagueClientMinimized || LeagueClientWindowHeight / (double)LeagueClientWindowWidth == 0.5625) && !forced) + { + return 0; + } + + var LeagueClientZoom = await LeagueClientAPIClient.GetClientZoom(); + var PrimaryScreenWidth = Screen.PrimaryScreen.Bounds.Width; + var PrimaryScreenHeight = Screen.PrimaryScreen.Bounds.Height; + // var PrimaryScreenDpi = GetDpiForWindow(LeagueClientWindow.LeagueClientWindowHWnd) / 96.0; + + if (LeagueClientZoom == -1) + { + return -3; // Failed to get leagueclient zoom + } + + var TargetLeagueClientWindowWidth = (int)(1280 * LeagueClientZoom); + var TargetLeagueClientWindowHeight = (int)(720 * LeagueClientZoom); + + SetWindowPos( + LeagueClientWindow.LeagueClientWindowHWnd, + 0, + (PrimaryScreenWidth - TargetLeagueClientWindowWidth) / 2, + (PrimaryScreenHeight - TargetLeagueClientWindowHeight) / 2, + TargetLeagueClientWindowWidth, TargetLeagueClientWindowHeight, + 0x0040 + ); + + SetWindowPos( + LeagueClientWindow.LeagueClientWindowCefHWnd, + 0, + 0, + 0, + TargetLeagueClientWindowWidth, + TargetLeagueClientWindowHeight, + 0x0040 + ); + + return 1; + } + + static void Main(string[] args) + { + + if (args.Length >= 2 && args[0] == "--mode" && int.TryParse(args[1], out int mode)) + { + Run(true, mode).Wait(); + } + else + { + Run(false).Wait(); + Console.WriteLine("> 按任意键退出..."); + Console.ReadKey(); + } + } + + static void PrintMenu() + { + Console.WriteLine("------"); + Console.WriteLine("> 功能菜单: "); + Console.WriteLine("| [ 0 ]: 退出"); + Console.WriteLine("| [ 1 ]: 立即恢复客户端窗口到正常大小 "); + Console.WriteLine("| [ 2 ]: 自动恢复客户端窗口到正常大小 (常驻)"); + Console.WriteLine("| [ 3 ]: 直接跳过结算页面"); + Console.WriteLine("| [ 4 ]: 热重载客户端"); + Console.WriteLine("> "); + Console.Write("> 请输入功能序号并回车以执行 (1): "); + } + + static void PrintCopyright() + { + Console.WriteLine("------"); + Console.WriteLine("> [ 英雄联盟客户端疑难杂症解决方案 ]"); + Console.WriteLine("> Fix LCU Window -- " + GetVersion()); + Console.WriteLine("------"); + Console.WriteLine("> Github: https://github.com/LeagueTavern/fix-lcu-window"); + Console.WriteLine("> Bilibili: Butter_Cookies"); + Console.WriteLine("> Code by LeagueTavern"); + } + + static string GetVersion() + { + return Application.ProductVersion.Substring(0, Application.ProductVersion.LastIndexOf('.')); + } + + static int GetUserChoice() + { + var UserInput = 0; + var UserOriginalInput = Console.ReadLine(); + if (String.IsNullOrWhiteSpace(UserOriginalInput) || !int.TryParse(UserOriginalInput, out UserInput)) + { + UserInput = 1; // Default to 1 + } + + return UserInput; + } + + static async Task Plan1() + { + var Result = await FixLeagueClientWindow(true); + var ErrorMessage = FixLeagueClientWindowError.ErrorDict[Result]; + return ErrorMessage; + } + + static async Task Plan2() + { + int CurrentTriggerCount = 0; + + Console.WriteLine("> 已进入自动检测模式,您现在可以放心的去玩游戏了"); + Console.WriteLine("> 当客户端尺寸出现异常时,程序将会自动修复"); + Console.WriteLine("> 按下 [Ctrl] + [C] 或 直接关闭本窗口 即可关闭检测并结束本程序"); + Console.WriteLine("> ------"); + + while (true) + { + var CurrentResult = await FixLeagueClientWindow(); + if (CurrentResult == 1) + { + CurrentTriggerCount++; + Console.WriteLine("> 检测到窗口尺寸异常,已自动处理 (" + CurrentTriggerCount + ")"); + } + await Task.Delay(1500); + }; + } + + static async Task Plan3() + { + var LeagueClientAPIClient = GetLCU(); + var Result = await LeagueClientAPIClient.LobbyPlayAgain(); + return Result ? "成功向客户端发送指令" : "向客户端发送指令时出现问题"; + } + + static async Task Plan4() + { + var LeagueClientAPIClient = GetLCU(); + var Result = await LeagueClientAPIClient.RestartClientUx(); + return Result ? "成功向客户端发送指令" : "向客户端发送指令时出现问题"; + } + + static async Task Run(bool withArgs, int mode = 0) + { + if (!withArgs) + { + PrintCopyright(); + PrintMenu(); + } + + var UserChoice = withArgs ? mode : GetUserChoice(); + + if (withArgs) + { + Console.WriteLine("> 正在执行功能: " + mode); + } + else + { + Console.WriteLine("> ------"); + } + + + switch (UserChoice) + { + case 0: // Exit + return; + case 1: // Fix League Client Window + Console.WriteLine("> " + await Plan1()); + break; + case 2: // Fix League Client Window (Auto) + await Plan2(); + break; + case 3: // Skip Post Game + Console.WriteLine("> " + await Plan3()); + break; + case 4: // Restart Client + Console.WriteLine("> " + await Plan4()); + break; + default: // Unknown Choice + Console.WriteLine("> 未知的功能序号"); + break; + } + + return; + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..e0e4d92 --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// 有关程序集的一般信息由以下 +// 控制。更改这些特性值可修改 +// 与程序集关联的信息。 +[assembly: AssemblyTitle("Fix-LCU-Window")] +[assembly: AssemblyDescription("Fix League of Legends client window.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("LeagueTavern")] +[assembly: AssemblyProduct("fix-lcu-window")] +[assembly: AssemblyCopyright("Copyright © LeagueTavern 2023")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// 将 ComVisible 设置为 false 会使此程序集中的类型 +//对 COM 组件不可见。如果需要从 COM 访问此程序集中的类型 +//请将此类型的 ComVisible 特性设置为 true。 +[assembly: ComVisible(false)] + +// 如果此项目向 COM 公开,则下列 GUID 用于类型库的 ID +[assembly: Guid("4e9f5409-2d0b-40cc-9c76-a33c7c68b8e8")] + +// 程序集的版本信息由下列四个值组成: +// +// 主版本 +// 次版本 +// 生成号 +// 修订号 +// +//可以指定所有这些值,也可以使用“生成号”和“修订号”的默认值 +//通过使用 "*",如下所示: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.1.0.0")] +[assembly: AssemblyFileVersion("1.1.0.0")] diff --git a/Util/LeagueClientAPI.cs b/Util/LeagueClientAPI.cs new file mode 100644 index 0000000..26e79e8 --- /dev/null +++ b/Util/LeagueClientAPI.cs @@ -0,0 +1,80 @@ +using System; +using System.Text; +using System.Threading.Tasks; +using System.Net.Http; +using System.Text.RegularExpressions; + +namespace Fix_LCU_Window.Util +{ + public class LeagueClientAPI + { + private WebRequestHandler RequestHandler = new WebRequestHandler(); + private HttpClient Client; + + public LeagueClientAPI(int Port, string Token) + { + // ignore certificate errors + RequestHandler.ServerCertificateValidationCallback = delegate { return true; }; + + Client = new HttpClient(RequestHandler); + Client.BaseAddress = new Uri("https://127.0.0.1:" + Port); + Client.DefaultRequestHeaders.Add("Accept", "*/*"); + Client.DefaultRequestHeaders.Add("Authorization", "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes("riot:" + Token))); + } + + public async Task GetClientZoom() + { + try + { + HttpResponseMessage Response = await Client.GetAsync("/riotclient/zoom-scale"); + Response.EnsureSuccessStatusCode(); + return double.Parse(await Response.Content.ReadAsStringAsync()); + } + catch + { + return -1; + } + } + + public async Task RestartClientUx() + { + try + { + HttpResponseMessage response = await Client.PostAsync("/riotclient/kill-and-restart-ux", new StringContent("")); + response.EnsureSuccessStatusCode(); + return true; + } + catch + { + return false; + } + } + + public async Task LobbyPlayAgain() + { + try + { + HttpResponseMessage response = await Client.PostAsync("/lol-lobby/v2/play-again", new StringContent("")); + response.EnsureSuccessStatusCode(); + return true; + } + catch + { + return false; + } + } + + public static (bool Available, int Port, string Token, string Protocol) CommandLineParser(string command) + { + Regex installAuthToken = new Regex(@"""--remoting-auth-token=(.*?)"""); + Regex installAppPort = new Regex(@"""--app-port=(.*?)"""); + + var portMatch = installAppPort.Match(command); + var tokenMatch = installAuthToken.Match(command); + + return (portMatch.Success && tokenMatch.Success) + ? (true, int.Parse(portMatch.Groups[1].Value), tokenMatch.Groups[1].Value, "https") + : (false, 0, null, null); + } + } +} diff --git a/Util/ProcessControl.cs b/Util/ProcessControl.cs new file mode 100644 index 0000000..f3a6118 --- /dev/null +++ b/Util/ProcessControl.cs @@ -0,0 +1,111 @@ +using System; +using System.Runtime.InteropServices; +using System.Diagnostics; +using System.Linq; +using System.Management; +using System.ComponentModel; +using System.Drawing; + +namespace Fix_LCU_Window.Util +{ + internal class ProcessControl + { + [DllImport("User32.dll", EntryPoint = "FindWindow")] + public extern static IntPtr FindWindow(string lpClassName, string lpWindowName); + + [DllImport("User32.dll", EntryPoint = "FindWindowEx")] + public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName); + + [DllImport("user32.dll", EntryPoint = "SetWindowPos")] + public static extern int SetWindowPos(IntPtr hWnd, int hWndInsertAfter, int x, int y, int Width, int Height, int flags); + + [DllImport("user32.dll", EntryPoint = "GetDpiForWindow")] + public static extern uint GetDpiForWindow([In] IntPtr hmonitor); + + [DllImport("user32.dll", EntryPoint = "GetWindowRect")] + public static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); + + [DllImport("user32.dll", EntryPoint = "GetWindowPlacement")] + public static extern bool GetWindowPlacement(IntPtr hWnd, ref WINDOWPLACEMENT lpwndpl); + + [Serializable] + [StructLayout(LayoutKind.Sequential)] + public struct POINT + { + public int X; + public int Y; + } + + [StructLayout(LayoutKind.Sequential)] + public struct RECT + { + public int Left; + public int Top; + public int Right; + public int Bottom; + } + + [Serializable] + public struct WINDOWPLACEMENT + { + public int length; + public int flags; + public int showCmd; + public POINT ptMinPosition; + public POINT ptMaxPosition; + public RECT rcNormalPosition; + } + + public static string GetCommandLineByProcessId(uint processId) + { + try + { + return GetCommandLineArgsCore(); + } + catch (Win32Exception ex) when ((uint)ex.ErrorCode == 0x80004005) + { + return string.Empty; + } + catch (InvalidOperationException) + { + return string.Empty; + } + + string GetCommandLineArgsCore() + { + using (var searcher = new ManagementObjectSearcher( + "SELECT CommandLine FROM Win32_Process WHERE ProcessId = " + processId)) + using (var objects = searcher.Get()) + { + var @object = objects.Cast().SingleOrDefault(); + return @object?["CommandLine"]?.ToString() ?? ""; + } + } + } + + public static uint GetProcessId(String processName) + { + Process[] processes = Process.GetProcesses(); + uint iProcessId = 0; + + foreach (Process p in processes) + { + if (p.ProcessName == processName) + { + iProcessId = (uint)p.Id; + break; + } + } + + return iProcessId; + } + + public static bool IsMinimalized(IntPtr hWnd) + { + var placement = new WINDOWPLACEMENT(); + placement.length = Marshal.SizeOf(typeof(WINDOWPLACEMENT)); + GetWindowPlacement(hWnd, ref placement); + return placement.showCmd == 2; // SW_SHOWMINIMIZED + } + } +} diff --git a/app.manifest b/app.manifest new file mode 100644 index 0000000..a69415e --- /dev/null +++ b/app.manifest @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/readme.md b/readme.md new file mode 100644 index 0000000..c567b3c --- /dev/null +++ b/readme.md @@ -0,0 +1,40 @@ +# Fix-League-Client-Update-Window +英雄联盟客户端疑难杂症解决方案。 + +![screenshot](./Assets/screenshot.png) + +## 📝功能 +- [x] 修复客户端窗口大小异常 (可自动检测) +- [x] 跳过正在转圈的结算页面 +- [x] 热重载客户端 (不会触发排队) + +## 👋注意 +- **本工具从实现原理上没有封号的可能**。 +- 本工具依赖 `.NET Framework 4.8` (`Windows 10` 以上一般自带) + +## 🚀如何使用 +1. 从[这里](https://github.com/LeagueTavern/fiAx-lcu-window/releases)下载最新版本。 +2. 解压到任意目录下,桌面也好,总之你方便找到就行。 +3. 启动`Fix-LCU-Window.exe`,**程序可能会申请管理员权限,请务必点 [是] ,否则程序将无法运行**。 +4. 输入你想要执行的功能序号,按回车键执行。 + +## 😡为什么我会做这个工具 +- 有很多人说,将游戏设置成 `DX9` 问题即可解决窗口大小异常的问题,但我个人并不喜欢 `DX9` 模式 ,首先就是帧数低,差了**100帧**左右,其次就是切屏有黑屏时间,我本人电脑的配置较差,这些问题都是我不能忍受的,**所以请不要在B站视频评论、Issues、Pull requests里面刷一些本工具无用的言论,这个工具最初是我自己做给自己用的, `DX11` 模式下出现各种各样的专属BUG本身就是游戏发行商的问题,请不要在这方面洗,我会觉得很恶心。** +- 本人无边框掉帧严重。 +- 在客户端里使用 `Ctrl`+`↑`/`↓` 或许可以为你解决异常的问题,但此方法对我无效,所以我才做了这个工具。 +- 在客户端设置里,勾选 `低配机器适应模式` 可以解决问题,但是对我渣机而言加载时间过长,而且这种模式在游戏里是拿不到 `LCUAPI` 的 `port` 与 `token` 的(中国大陆服务器已禁用`lockfile`生成),如果关闭此选项,那我每次调试代码时都要关闭游戏,拿参数,然后再进入一遍游戏。 +- 为了防止频繁切屏导致客户端异常,我也尝试过减少切屏次数,比如[将重生时间同步到代码编辑器](https://github.com/Coooookies/vscode-league-respawn-timer),这样就不需要来回看了。 + +## 实现原理 +1. 修复客户端窗口大小异常 + - 获取目标窗口HWND + - 获取屏幕大小 + - 通过LCUAPI获取客户端缩放比,并根据比例对窗口尺寸进行计算 + - 设置窗口与CEF渲染区域的大小 +2. 跳过正在转圈的结算页面 + - 基于LCUAPI实现 +3. 热重载客户端 + - 基于LCUAPI实现 + +## 协议 +本项目遵循[MIT](https://github.com/LeagueTavern/fix-lcu-window/blob/master/LICENSE.txt) \ No newline at end of file