1+ // 本段部分实现逻辑参考自 ProjBobcat 的 DeepJavaSearcher.cs
2+ // 仓库地址:https://github.com/Corona-Studio/ProjBobcat
3+
4+ using Microsoft . Win32 ;
5+ using System . Diagnostics ;
6+ using System . Runtime . CompilerServices ;
7+ using System . Runtime . Versioning ;
8+ using System . Text . RegularExpressions ;
9+ using OverrideLauncher . Core . Base . Entry . Info . Java ;
10+
11+ namespace OverrideLauncher . Core . Classes . Utilities ;
12+
13+ public static partial class JavaUtil {
14+ public static async Task < JavaInfo > GetJavaInfoAsync ( string javaPath , CancellationToken cancellationToken = default ) {
15+ if ( string . IsNullOrEmpty ( javaPath ) || ! File . Exists ( javaPath ) ) {
16+ return null ;
17+ }
18+
19+ using var process = Process . Start ( new ProcessStartInfo ( javaPath ) {
20+ Arguments = "-version" ,
21+ CreateNoWindow = true ,
22+ UseShellExecute = false ,
23+ RedirectStandardError = true ,
24+ RedirectStandardOutput = true
25+ } ) ;
26+
27+ string text = process . StandardError . ReadToEnd ( ) ;
28+ if ( string . IsNullOrEmpty ( text ) )
29+ ArgumentException . ThrowIfNullOrWhiteSpace ( text ) ;
30+
31+ bool is64bit = text . Contains ( "64-bit" , StringComparison . OrdinalIgnoreCase ) ;
32+ string javaVersion = JavaVersionRegex ( ) . Match ( text ) . Groups [ "version" ] . Value ;
33+
34+ string javaType = text . Contains ( "java(tm)" , StringComparison . OrdinalIgnoreCase )
35+ ? "Java"
36+ : text . Contains ( "zulu" )
37+ ? "ZuluJDK"
38+ : "OpenJDK" ;
39+
40+ await process . WaitForExitAsync ( cancellationToken ) ;
41+
42+ var versionParts = javaVersion . Split ( "." ) ;
43+ return new JavaInfo {
44+ Is64bit = is64bit ,
45+ JavaPath = javaPath ,
46+ JavaType = javaType ,
47+ JavaVersion = javaVersion ,
48+ MajorVersion = ( int . Parse ( versionParts [ 0 ] ) == 1 ) ? int . Parse ( versionParts [ 1 ] ) : int . Parse ( versionParts [ 0 ] ) ,
49+ } ;
50+ }
51+
52+ public static async Task < List < JavaInfo > > GetJavaListAsync ( CancellationToken cancellationToken = default ) {
53+ var javaList = new List < JavaInfo > ( ) ;
54+
55+ if ( OperatingSystem . IsWindows ( ) ) {
56+ foreach ( var java in GetJavasForWindows ( ) ) {
57+ if ( File . Exists ( java ) ) {
58+ var javaInfo = await GetJavaInfoAsync ( java , cancellationToken ) ;
59+ if ( javaInfo != null ) {
60+ javaList . Add ( javaInfo ) ;
61+ }
62+ }
63+ }
64+ return javaList ;
65+ }
66+
67+ using var process = Process . Start ( new ProcessStartInfo ( "whereis" ) {
68+ CreateNoWindow = true ,
69+ UseShellExecute = false ,
70+ RedirectStandardError = true ,
71+ RedirectStandardOutput = true ,
72+ ArgumentList = {
73+ "/b" ,
74+ "java"
75+ } ,
76+ } ) ;
77+
78+ if ( process == null )
79+ return javaList ;
80+
81+ do {
82+ cancellationToken . ThrowIfCancellationRequested ( ) ;
83+
84+ var line = process . StandardOutput . ReadLine ( ) ;
85+ if ( string . IsNullOrEmpty ( line ) || ! File . Exists ( line ) )
86+ continue ;
87+
88+ var javaInfo = await GetJavaInfoAsync ( line , cancellationToken ) ;
89+ if ( javaInfo != null ) {
90+ javaList . Add ( javaInfo ) ;
91+ }
92+ } while ( ! process . HasExited ) ;
93+
94+ await process . WaitForExitAsync ( cancellationToken ) ;
95+ var lastLine = await process . StandardOutput . ReadLineAsync ( cancellationToken ) ;
96+
97+ if ( ! string . IsNullOrEmpty ( lastLine ) && File . Exists ( lastLine ) ) {
98+ var javaInfo = await GetJavaInfoAsync ( lastLine , cancellationToken ) ;
99+ if ( javaInfo != null ) {
100+ javaList . Add ( javaInfo ) ;
101+ }
102+ }
103+
104+ return javaList ;
105+ }
106+
107+ #region Privates
108+ private static Regex JavaVersionRegex ( ) => new Regex ( "(java|openjdk) version \" \\ s*(?<version>\\ S+)\\ s*\" " ) ;
109+
110+ [ SupportedOSPlatform ( "Windows" ) ]
111+ private static IEnumerable < string > GetJavasForWindows ( ) {
112+ //Use by:https://github.com/Xcube-Studio/Natsurainko.FluentCore/blob/main/Natsurainko.FluentCore/Environment/JavaUtils.cs
113+ List < string > result = [ ] ;
114+
115+ #region Cmd: Find Java by running "where java" command in cmd.exe
116+
117+ using var process = new Process ( ) {
118+ StartInfo = new ProcessStartInfo ( ) {
119+ FileName = "cmd" ,
120+ UseShellExecute = false ,
121+ RedirectStandardOutput = true ,
122+ RedirectStandardError = true ,
123+ RedirectStandardInput = true ,
124+ CreateNoWindow = true
125+ } ,
126+ EnableRaisingEvents = true ,
127+ } ;
128+
129+ process . Start ( ) ;
130+ process . BeginErrorReadLine ( ) ;
131+ process . BeginOutputReadLine ( ) ;
132+
133+ var output = new List < string > ( ) ;
134+
135+ process . OutputDataReceived += ( sender , e ) => {
136+ if ( ! string . IsNullOrEmpty ( e . Data ) )
137+ output . Add ( e . Data ) ;
138+ } ;
139+ process . ErrorDataReceived += ( sender , e ) => {
140+ if ( ! string . IsNullOrEmpty ( e . Data ) )
141+ output . Add ( e . Data ) ;
142+ } ;
143+
144+ process . StandardInput . WriteLine ( "where java" ) ;
145+ process . StandardInput . WriteLine ( "exit" ) ;
146+ process . WaitForExit ( ) ;
147+
148+ IEnumerable < string > javaPaths = output . Where (
149+ x => ! string . IsNullOrEmpty ( x ) && x . EndsWith ( "java.exe" ) && File . Exists ( x )
150+ ) ! ; // null checked in the where clause
151+ result . AddRange ( javaPaths ) ;
152+
153+ #endregion
154+
155+ #region Registry: Find Java by searching the registry
156+
157+ var javaHomePaths = new List < string > ( ) ;
158+
159+ // Local function: recursively search for the keyName in the registry
160+ static List < string > ForRegistryKey ( RegistryKey registryKey , string keyName ) {
161+ var result = new List < string > ( ) ;
162+
163+ foreach ( string valueName in registryKey . GetValueNames ( ) ) {
164+ if ( valueName == keyName ) // Check that the valueName exists
165+ result . Add ( ( string ) registryKey . GetValue ( valueName ) ! ) ;
166+ }
167+
168+ foreach ( string registrySubKey in registryKey . GetSubKeyNames ( ) ) {
169+ using var subKey = registryKey . OpenSubKey ( registrySubKey ) ;
170+ if ( subKey is not null ) // Check that the registrySubKey exists
171+ result . AddRange ( ForRegistryKey ( subKey , keyName ) ) ;
172+ }
173+
174+ return result ;
175+ }
176+ ;
177+
178+ using var reg = Registry . LocalMachine . OpenSubKey ( "SOFTWARE" ) ;
179+
180+ if ( reg is not null && reg . GetSubKeyNames ( ) . Contains ( "JavaSoft" ) ) {
181+ using var registryKey = reg . OpenSubKey ( "JavaSoft" ) ;
182+ if ( registryKey is not null )
183+ javaHomePaths . AddRange ( ForRegistryKey ( registryKey , "JavaHome" ) ) ;
184+ }
185+
186+ if ( reg is not null && reg . GetSubKeyNames ( ) . Contains ( "WOW6432Node" ) ) {
187+ using var registryKey = reg . OpenSubKey ( "WOW6432Node" ) ;
188+ if ( registryKey is not null && registryKey . GetSubKeyNames ( ) . Contains ( "JavaSoft" ) ) {
189+ using var registrySubKey = reg . OpenSubKey ( "JavaSoft" ) ;
190+ if ( registrySubKey is not null )
191+ ForRegistryKey ( registrySubKey , "JavaHome" ) . ForEach ( x => javaHomePaths . Add ( x ) ) ;
192+ }
193+ }
194+
195+ foreach ( var item in javaHomePaths )
196+ if ( Directory . Exists ( item ) )
197+ result . AddRange ( GetFilesInDirectory ( item , "java.exe" ) ) ;
198+
199+ #endregion
200+
201+ #region Special Folders
202+
203+ List < string > folders = [ ] ;
204+
205+ // %APPDATA%\.minecraft\cache\java
206+ string appDataPath = Environment . GetEnvironmentVariable ( "APPDATA" ) ;
207+
208+ if ( ! string . IsNullOrEmpty ( appDataPath ) )
209+ folders . Add ( Path . Combine ( appDataPath , ".minecraft\\ cache\\ java" ) ) ;
210+
211+ // %APPDATA%\.minecraft\runtime\
212+ if ( ! string . IsNullOrEmpty ( appDataPath ) )
213+ folders . Add ( Path . Combine ( appDataPath , ".minecraft\\ runtime\\ " ) ) ;
214+
215+ // %JAVA_HOME%
216+ string javaHomePath = Environment . GetEnvironmentVariable ( "JAVA_HOME" ) ;
217+ if ( javaHomePath is not null )
218+ folders . Add ( javaHomePath ) ;
219+
220+ // Program Files\Java
221+ folders . Add ( Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFiles ) , "Java" ) ) ;
222+ folders . Add ( Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFilesX86 ) , "Java" ) ) ;
223+
224+ // Program Files\Zulu
225+ folders . Add ( Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFiles ) , "Zulu" ) ) ;
226+ folders . Add ( Path . Combine ( Environment . GetFolderPath ( Environment . SpecialFolder . ProgramFilesX86 ) , "Zulu" ) ) ;
227+
228+ // Check Java for each folder
229+ foreach ( var folder in folders )
230+ if ( Directory . Exists ( folder ) )
231+ result . AddRange ( GetFilesInDirectory ( folder , "java.exe" ) ) ;
232+
233+ #endregion
234+
235+ return result . Distinct ( ) ;
236+ }
237+
238+ private static IEnumerable < string > GetFilesInDirectory ( string directoryPath , string searchPattern ) {
239+ var files = new List < string > ( ) ;
240+
241+ try {
242+ // 使用 Directory.GetFiles 递归搜索文件
243+ files . AddRange ( Directory . GetFiles ( directoryPath , searchPattern , SearchOption . AllDirectories ) ) ;
244+ }
245+ catch ( UnauthorizedAccessException ) {
246+ // 忽略无权限访问的目录
247+ }
248+ catch ( Exception ) {
249+ // 忽略其他异常(如路径过长等)
250+ }
251+
252+ return files ;
253+ }
254+
255+ #endregion
256+ }
0 commit comments