├── .gitattributes ├── JavaInfo.rc ├── GetJavaInfo.rc ├── wsUtilEnv.pp ├── JavaInfoTest.pp ├── history.md ├── wsUtilArch.pp ├── wsUtilVer.pp ├── GetJavaInfo.md ├── wsUtilStr.pp ├── JavaInfoTest.ps1 ├── JavaInfo.pp ├── LICENSE ├── wsUtilFile.pp ├── wsUtilReg.pp ├── JavaInfoTest.iss ├── README.md ├── GetJavaInfo.pp └── wsJavaInfo.pp /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /JavaInfo.rc: -------------------------------------------------------------------------------- 1 | 1 VERSIONINFO 2 | FILEVERSION 1,6,0,0 3 | PRODUCTVERSION 1,6,0,0 4 | FILEOS 0x4 5 | FILETYPE 2 6 | { 7 | BLOCK "StringFileInfo" 8 | { 9 | BLOCK "040904E4" 10 | { 11 | VALUE "CompanyName", "" 12 | VALUE "FileDescription", "JavaInfo.dll" 13 | VALUE "FileVersion", "1.6.0.0" 14 | VALUE "InternalName", "JavaInfo.dll" 15 | VALUE "LegalCopyright", "Copyright 2020-2023 by Bill Stewart (bstewart at iname.com)" 16 | VALUE "OriginalFilename", "JavaInfo.dll" 17 | VALUE "ProductName", "" 18 | VALUE "ProductVersion", "1.6.0.0" 19 | } 20 | } 21 | 22 | BLOCK "VarFileInfo" 23 | { 24 | VALUE "Translation", 0x0409, 0x04E4 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /GetJavaInfo.rc: -------------------------------------------------------------------------------- 1 | 1 VERSIONINFO 2 | FILEVERSION 1,6,0,0 3 | PRODUCTVERSION 1,6,0,0 4 | FILEOS 0x4 5 | FILETYPE 1 6 | { 7 | BLOCK "StringFileInfo" 8 | { 9 | BLOCK "040904E4" 10 | { 11 | VALUE "CompanyName", "" 12 | VALUE "FileDescription", "GetJavaInfo.exe" 13 | VALUE "FileVersion", "1.6.0.0" 14 | VALUE "InternalName", "GetJavaInfo.exe" 15 | VALUE "LegalCopyright", "Copyright 2020-2023 by Bill Stewart (bstewart at iname.com)" 16 | VALUE "OriginalFilename", "GetJavaInfo.exe" 17 | VALUE "ProductName", "" 18 | VALUE "ProductVersion", "1.6.0.0" 19 | } 20 | } 21 | 22 | BLOCK "VarFileInfo" 23 | { 24 | VALUE "Translation", 0x0409, 0x04E4 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /wsUtilEnv.pp: -------------------------------------------------------------------------------- 1 | { Copyright (C) 2020-2021 by Bill Stewart (bstewart at iname.com) 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU Lesser General Public License as published by the Free 5 | Software Foundation; either version 3 of the License, or (at your option) any 6 | later version. 7 | 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Lesser Public License for more 11 | details. 12 | 13 | You should have received a copy of the GNU Lesser General Public License 14 | along with this program. If not, see https://www.gnu.org/licenses/. 15 | 16 | } 17 | 18 | {$MODE OBJFPC} 19 | {$MODESWITCH UNICODESTRINGS} 20 | 21 | unit wsUtilEnv; 22 | 23 | interface 24 | 25 | // Expands environment variable references (e.g., %varname%) in the named 26 | // string and returns the resulting string with expanded references 27 | function ExpandEnvStrings(const Name: string): string; 28 | 29 | // Gets the value of the named environment variable; returns an empty string 30 | // if the variable doesn't exist 31 | function GetEnvVar(const Name: string): string; 32 | 33 | implementation 34 | 35 | uses 36 | Windows; 37 | 38 | function ExpandEnvStrings(const Name: string): string; 39 | var 40 | NumChars, BufSize: DWORD; 41 | pBuffer: PChar; 42 | begin 43 | result := ''; 44 | // Get number of characters needed for buffer 45 | NumChars := ExpandEnvironmentStringsW(PChar(Name), // LPCWSTR lpSrc 46 | nil, // LPWSTR lpDst 47 | 0); // DWORD nSize 48 | if NumChars > 0 then 49 | begin 50 | BufSize := NumChars * SizeOf(Char); 51 | GetMem(pBuffer, BufSize); 52 | if ExpandEnvironmentStringsW(PChar(Name), // LPCWSTR lpSrc 53 | pBuffer, // LPWSTR lpDst 54 | NumChars) > 0 then // DWORD nSize 55 | begin 56 | result := pBuffer; 57 | end; 58 | FreeMem(pBuffer); 59 | end; 60 | end; 61 | 62 | function GetEnvVar(const Name: string): string; 63 | var 64 | NumChars, BufSize: DWORD; 65 | pBuffer: PChar; 66 | begin 67 | result := ''; 68 | // Get number of characters needed for buffer 69 | NumChars := GetEnvironmentVariableW(PChar(Name), // LPCWSTR lpName 70 | nil, // LPWSTR lpBuffer 71 | 0); // DWORD nSize 72 | if NumChars > 0 then 73 | begin 74 | BufSize := NumChars * SizeOf(Char); 75 | GetMem(pBuffer, BufSize); 76 | if GetEnvironmentVariableW(PChar(Name), // LPCWSTR lpName 77 | pBuffer, // LPWSTR lpBuffer 78 | NumChars) > 0 then // DWORD nSize 79 | begin 80 | result := pBuffer; 81 | end; 82 | FreeMem(pBuffer); 83 | end; 84 | end; 85 | 86 | begin 87 | end. 88 | -------------------------------------------------------------------------------- /JavaInfoTest.pp: -------------------------------------------------------------------------------- 1 | { Copyright (C) 2020-2023 by Bill Stewart (bstewart at iname.com) 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU Lesser General Public License as published by the Free 5 | Software Foundation; either version 3 of the License, or (at your option) any 6 | later version. 7 | 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Lesser Public License for more 11 | details. 12 | 13 | You should have received a copy of the GNU Lesser General Public License 14 | along with this program. If not, see https://www.gnu.org/licenses/. 15 | 16 | } 17 | 18 | {$MODE OBJFPC} 19 | {$MODESWITCH UNICODESTRINGS} 20 | {$APPTYPE GUI} 21 | 22 | program JavaInfoTest; 23 | 24 | uses 25 | Windows; 26 | 27 | type 28 | TGetDWORD = function(): DWORD; stdcall; 29 | TGetString = function(Buffer: PChar; NumChars: DWORD): DWORD; stdcall; 30 | TIsBinary64Bit = function(FileName: PChar; Is64Bit: PDWORD): DWORD; stdcall; 31 | 32 | var 33 | DLLHandle: HMODULE; 34 | IsJavaInstalled: TGetDWORD; 35 | GetJavaHome, GetJavaJVMPath, GetJavaVersion: TGetString; 36 | IsBinary64Bit: TIsBinary64Bit; 37 | JavaHome, JavaBinary, JavaJVMPath, JavaVersion, BinaryType, Msg: string; 38 | Is64Bit: DWORD; 39 | 40 | procedure MsgBox(const Msg: string; const MsgBoxType: UINT); 41 | begin 42 | MessageBoxW(0, PChar(Msg), 'JavaInfo.dll Test', MsgBoxType or MB_OK); 43 | end; 44 | 45 | function GetString(var Func: TGetString): string; 46 | var 47 | NumChars: DWORD; 48 | OutStr: string; 49 | begin 50 | result := ''; 51 | NumChars := Func(nil, 0); 52 | SetLength(OutStr, NumChars); 53 | if Func(PChar(OutStr), NumChars) > 0 then 54 | result := OutStr; 55 | end; 56 | 57 | begin 58 | DLLHandle := LoadLibraryW('JavaInfo.dll'); // LPCWSTR lpLibFileName 59 | if DLLHandle = 0 then 60 | ExitCode := GetLastError() 61 | else 62 | ExitCode := 0; 63 | if ExitCode <> 0 then 64 | begin 65 | MsgBox('Unable to load JavaInfo.dll.', MB_ICONERROR); 66 | exit(); 67 | end; 68 | GetJavaHome := TGetString(GetProcAddress(DLLHandle, // HMODULE hModule 69 | 'GetJavaHome')); // LPCSTR lpProcName 70 | GetJavaJVMPath := TGetString(GetProcAddress(DLLHandle, // HMODULE hModule 71 | 'GetJavaJVMPath')); // LPCSTR lpProcName 72 | GetJavaVersion := TGetString(GetProcAddress(DLLHandle, // HMODULE hModule 73 | 'GetJavaVersion')); // LPCSTR lpProcName 74 | IsBinary64Bit := TIsBinary64Bit(GetProcAddress(DLLHandle, // HMODULE hModule 75 | 'IsBinary64Bit')); // LPCSTR lpProcName 76 | IsJavaInstalled := TGetDWORD(GetProcAddress(DLLHandle, // HMODULE hModule 77 | 'IsJavaInstalled')); // LPCSTR lpProcName 78 | if IsJavaInstalled() = 0 then 79 | MsgBox('JavaInfo.dll did not detect a Java installation.', 0) 80 | else 81 | begin 82 | JavaHome := GetString(GetJavaHome); 83 | JavaBinary := JavaHome + '\bin\java.exe'; 84 | if IsBinary64Bit(PChar(JavaBinary), @Is64Bit) = 0 then 85 | begin 86 | if Is64Bit = 1 then 87 | BinaryType := '64-bit' 88 | else 89 | BinaryType := '32-bit'; 90 | end 91 | else 92 | BinaryType := 'unknown'; 93 | JavaJVMPath := GetString(GetJavaJVMPath); 94 | JavaVersion := GetString(GetJavaVersion); 95 | Msg := 'Java home: ' + JavaHome + #10 96 | + 'jvm.dll path: ' + JavaJVMPath + #10 97 | + 'Java file version: ' + JavaVersion + #10 98 | + 'Platform: ' + BinaryType; 99 | MsgBox(Msg, 0); 100 | end; 101 | FreeLibrary(DLLHandle); // HMODULE hLibModule 102 | end. 103 | -------------------------------------------------------------------------------- /history.md: -------------------------------------------------------------------------------- 1 | # JavaInfo.dll Version History 2 | 3 | ## 1.6.0.0 (2023-12-14) 4 | 5 | * Improved and simplified version checking code. 6 | 7 | * Corrected file type in DLL resource file. 8 | 9 | * Switched to FPC `UNICODESTRINGS` mode. 10 | 11 | ## 1.5.1.0 (2023-03-07) 12 | 13 | * Fixed bug in `jvm.dll` path detection. 14 | 15 | ## 1.5.0.0 (2023-02-27) 16 | 17 | * Added `GetJavaJVMPath` function. 18 | 19 | * Added `--javadll` (`-d`) option to GetJavaInfo utility. 20 | 21 | * Updated all sample code to add `GetJavaJVMPath` function. 22 | 23 | ## 1.4.0.0 (2023-01-12) 24 | 25 | * Added Microsoft JDK registry detection. 26 | 27 | * Minor tweaks. 28 | 29 | ## 1.3.1.0 (2021-11-16) 30 | 31 | * Added 'Eclipse Adoptium' registry detection. (For some reason, Adoptium changed the registry location, _again_). 32 | 33 | ## 1.3.0.0 (2021-10-05) 34 | 35 | * Added adoptium.net 'Eclipse Foundation' and 'Semeru' registry detection (adoptium.net is replacing adoptopenjdk.net). 36 | 37 | * Minor tweaks. 38 | 39 | ## 1.2.2.0 (2021-05-25) 40 | 41 | * Updated string-reading registry code to account for strings that might be missing a null terminator (to prevent a potential, but very low probability, buffer overflow). 42 | 43 | * Adjusted code formatting. 44 | 45 | * Compiled using FPC 3.2.2. 46 | 47 | * Wrote markdown documentation for GetJavaInfo.exe. 48 | 49 | * Minor tweaks. 50 | 51 | ## 1.2.1.0 (2021-01-19) 52 | 53 | * Minor tweaks and minor corrections to documentation. 54 | 55 | ## 1.2.0.0 (2021-01-15) 56 | 57 | * Added the `IsJavaMinimumVersion` function. 58 | 59 | * Added GetJavaInfo.exe that implements the JavaInfo.dll code as a console (command-line) utility. Run `GetJavaInfo -h` to display the help information. 60 | 61 | * Enhanced the `JavaInfo.iss` Inno Setup sample script to use the `IsJavaMinimumVersion` function. 62 | 63 | * Added sample PowerShell script to illustrate using .NET P/Invoke to call the JavaInfo.dll functions. 64 | 65 | ## 1.1.0.0 (2021-01-07) 66 | 67 | * Fixed: Registry search now returns the latest version across all registry searches instead of stopping after one subkey. 68 | 69 | * Fixed: Unhandled exception in edge case where no registry subkeys present to enumerate. 70 | 71 | ## 1.0.0.0 (2021-01-06) 72 | 73 | * Split registry searching code into separate functions to improve readability/maintenance. 74 | 75 | * Added AdoptOpenJDK registry key search. 76 | 77 | * Fixed buffer allocation/hang issue that could occur when searching the `Path` environment variable (regression bug from v0.0.0.2). 78 | 79 | ## 0.0.0.5 (2021-01-05) 80 | 81 | * Updated search order to search `Path` before registry. 82 | 83 | * Cleaned up registry search (removed redundant code and improved robustness and readability). 84 | 85 | * Updated/clarified documentation and fixed a couple of typos. 86 | 87 | ## 0.0.0.4 (2021-01-04) 88 | 89 | * Updated license to less restrictive LGPL. 90 | 91 | * Changed search order to use environment variables first. 92 | 93 | * Added registry searches for IBM and Azul JDKs. 94 | 95 | ## 0.0.0.3 (2020-12-31) 96 | 97 | * Changed `IsJava64Bit` function to `IsBinary64Bit` function. The reason for this change is that it is useful to determine whether a binary (i.e., a `.exe` or `.dll` file) is 64-bit even when Java is not detected. For example, if the `IsJavaInstalled` function returns 0 but an instance of Java is present, you can use the `IsBinary64Bit` function to determine whether the Java instance is 64-bit if you know its path. An added benefit is that the `IsBinary64Bit` function works on any Windows binary, not just Java binaries. 98 | 99 | * Included Inno Setup (https://www.jrsoftware.org/isinfo.php) sample script (`JavaInfo.iss`). 100 | 101 | ## 0.0.0.2 (2020-12-30) 102 | 103 | Misread Windows documentation on `SearchPathW` API function and allocated potentially insufficient buffer size. Fixed. 104 | 105 | ## 0.0.0.1 (2020-12-29) 106 | 107 | Initial version. 108 | -------------------------------------------------------------------------------- /wsUtilArch.pp: -------------------------------------------------------------------------------- 1 | { Copyright (C) 2020-2021 by Bill Stewart (bstewart at iname.com) 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU Lesser General Public License as published by the Free 5 | Software Foundation; either version 3 of the License, or (at your option) any 6 | later version. 7 | 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Lesser Public License for more 11 | details. 12 | 13 | You should have received a copy of the GNU Lesser General Public License 14 | along with this program. If not, see https://www.gnu.org/licenses/. 15 | 16 | } 17 | 18 | {$MODE OBJFPC} 19 | {$MODESWITCH UNICODESTRINGS} 20 | 21 | unit wsUtilArch; 22 | 23 | interface 24 | 25 | // Returns true if the current OS is 64-bit or false otherwise 26 | function IsWin64(): Boolean; 27 | 28 | // Returns true if the current process is WoW64 (i.e., 32-bit running on a 29 | // 64-bit OS), or false otherwise 30 | function IsProcessWoW64(): Boolean; 31 | 32 | implementation 33 | 34 | uses 35 | Windows; 36 | 37 | function IsProcessor64Bit(): Boolean; 38 | const 39 | PROCESSOR_ARCHITECTURE_INTEL = 0; 40 | PROCESSOR_ARCHITECTURE_ARM = 5; 41 | PROCESSOR_ARCHITECTURE_IA64 = 6; 42 | PROCESSOR_ARCHITECTURE_AMD64 = 9; 43 | PROCESSOR_ARCHITECTURE_ARM64 = 12; 44 | PROCESSOR_ARCHITECTURE_UNKNOWN = $FFFF; 45 | type 46 | TGetNativeSystemInfo = procedure(var lpSystemInfo: SYSTEM_INFO); stdcall; 47 | var 48 | Kernel32: HMODULE; 49 | GetNativeSystemInfo: TGetNativeSystemInfo; 50 | SystemInfo: SYSTEM_INFO; 51 | begin 52 | result := false; 53 | Kernel32 := GetModuleHandleW('kernel32'); // LPCWSTR lpModuleName 54 | GetNativeSystemInfo := TGetNativeSystemInfo(GetProcAddress(Kernel32, // HMODULE hModule 55 | 'GetNativeSystemInfo')); // LPCSTR lpProcName 56 | if Assigned(GetNativeSystemInfo) then 57 | begin 58 | GetNativeSystemInfo(SystemInfo); // LPSYSTEM_INFO lpSystemInfo 59 | with SystemInfo do 60 | result := (wProcessorArchitecture = PROCESSOR_ARCHITECTURE_IA64) or 61 | (wProcessorArchitecture = PROCESSOR_ARCHITECTURE_AMD64) or 62 | (wProcessorArchitecture = PROCESSOR_ARCHITECTURE_ARM64); 63 | end; 64 | end; 65 | 66 | function IsProcessWoW64(): Boolean; 67 | type 68 | TIsWow64Process = function(hProcess: HANDLE; var Wow64Process: BOOL): BOOL; stdcall; 69 | var 70 | Kernel32: HMODULE; 71 | IsWow64Process: TIsWow64Process; 72 | ProcessHandle: HANDLE; 73 | IsWoW64: BOOL; 74 | begin 75 | result := false; 76 | Kernel32 := GetModuleHandleW('kernel32'); // LPCWSTR lpModuleName 77 | IsWow64Process := TIsWow64Process(GetProcAddress(Kernel32, // HMODULE hModule 78 | 'IsWow64Process')); // LPCSTR lpProcName 79 | if Assigned(IsWow64Process) then 80 | begin 81 | ProcessHandle := OpenProcess(PROCESS_QUERY_INFORMATION, // DWORD dwDesiredAccess 82 | true, // BOOL bInheritHandle 83 | GetCurrentProcessId()); // DWORD dwProcessId 84 | if ProcessHandle <> 0 then 85 | begin 86 | if IsWow64Process(ProcessHandle, // HANDLE hProcess 87 | IsWoW64) then // PBOOL Wow64Process 88 | begin 89 | result := IsWoW64; 90 | end; 91 | CloseHandle(ProcessHandle); // HANDLE hObject 92 | end; 93 | end; 94 | end; 95 | 96 | function IsWin64(): Boolean; 97 | begin 98 | {$IFDEF WIN64} 99 | // compiled on x64 100 | result := true; 101 | {$ELSE} 102 | // true if processor is 64-bit and current process is WoW64 103 | result := IsProcessor64Bit() and IsProcessWoW64(); 104 | {$ENDIF} 105 | end; 106 | 107 | begin 108 | end. 109 | -------------------------------------------------------------------------------- /wsUtilVer.pp: -------------------------------------------------------------------------------- 1 | { Copyright (C) 2020-2023 by Bill Stewart (bstewart at iname.com) 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU Lesser General Public License as published by the Free 5 | Software Foundation; either version 3 of the License, or (at your option) any 6 | later version. 7 | 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Lesser Public License for more 11 | details. 12 | 13 | You should have received a copy of the GNU Lesser General Public License 14 | along with this program. If not, see https://www.gnu.org/licenses/. 15 | 16 | } 17 | 18 | {$MODE OBJFPC} 19 | {$MODESWITCH UNICODESTRINGS} 20 | 21 | unit wsUtilVer; 22 | 23 | interface 24 | 25 | // Returns true if the specified version string is valid, or false otherwise 26 | function TestVersionString(const S: string): Boolean; 27 | 28 | // Compares two version strings 'a[.b[.c[.d]]]' 29 | // Returns: 30 | // < 0 if V1 < V2 31 | // 0 if V1 = V2 32 | // > 0 if V1 > V2 33 | function CompareVersionStrings(const V1, V2: string): Integer; 34 | 35 | // Expands a version string into a full 4-part version string; missing parts 36 | // are returned as zeroes; e.g., '10' returns '10.0.0.0', '11.1' returns 37 | // '11.1.0.0', and '12.1.2' returns '12.1.2.0'; returns an empty string if 38 | // no version number or an invalid version number is supplied; each part must 39 | // be in the range 0..65535 (16-bit unsigned - word) 40 | function ExpandVersionString(const S: string): string; 41 | 42 | implementation 43 | 44 | type 45 | TStringArray = array of string; 46 | TVersionArray = array[0..3] of Word; 47 | 48 | // Returns the number of times Substring appears in S 49 | function CountSubstring(const Substring, S: string): Integer; 50 | var 51 | P: Integer; 52 | begin 53 | result := 0; 54 | P := Pos(Substring, S, 1); 55 | while P <> 0 do 56 | begin 57 | Inc(result); 58 | P := Pos(Substring, S, P + Length(Substring)); 59 | end; 60 | end; 61 | 62 | // Splits S into the Dest array using Delim as a delimiter 63 | procedure StrSplit(S, Delim: string; var Dest: TStringArray); 64 | var 65 | I, P: Integer; 66 | begin 67 | I := CountSubstring(Delim, S); 68 | // If no delimiters, Dest is a single-element array 69 | if I = 0 then 70 | begin 71 | SetLength(Dest, 1); 72 | Dest[0] := S; 73 | exit; 74 | end; 75 | SetLength(Dest, I + 1); 76 | for I := 0 to Length(Dest) - 1 do 77 | begin 78 | P := Pos(Delim, S); 79 | if P > 0 then 80 | begin 81 | Dest[I] := Copy(S, 1, P - 1); 82 | Delete(S, 1, P + Length(Delim) - 1); 83 | end 84 | else 85 | Dest[I] := S; 86 | end; 87 | end; 88 | 89 | function IntToStr(const I: Integer): string; 90 | begin 91 | Str(I, result); 92 | end; 93 | 94 | function StrToInt(const S: string; var I: Integer): Boolean; 95 | var 96 | Code: Word; 97 | begin 98 | Val(S, I, Code); 99 | result := Code = 0; 100 | end; 101 | 102 | function StrToWord(const S: string; var W: Word): Boolean; 103 | var 104 | Code: Word; 105 | begin 106 | Val(S, W, Code); 107 | result := Code = 0; 108 | end; 109 | 110 | function GetVersionArray(const S: string; var Version: TVersionArray): Boolean; 111 | var 112 | A: TStringArray; 113 | ALen, I, Part: Integer; 114 | begin 115 | result := false; 116 | StrSplit(S, '.', A); 117 | ALen := Length(A); 118 | if ALen > 4 then 119 | exit; 120 | if ALen < 4 then 121 | begin 122 | SetLength(A, 4); 123 | for I := ALen to 3 do 124 | A[I] := '0'; 125 | end; 126 | for I := 0 to Length(A) - 1 do 127 | begin 128 | result := StrToInt(A[I], Part); 129 | if not result then 130 | exit; 131 | result := (Part >= 0) and (Part <= $FFFF); 132 | if not result then 133 | exit; 134 | end; 135 | for I := 0 to 3 do 136 | begin 137 | result := StrToWord(A[I], Version[I]); 138 | if not result then 139 | exit; 140 | end; 141 | end; 142 | 143 | function TestVersionString(const S: string): Boolean; 144 | var 145 | Version: TVersionArray; 146 | begin 147 | result := GetVersionArray(S, Version); 148 | end; 149 | 150 | function CompareVersionStrings(const V1, V2: string): Integer; 151 | var 152 | Ver1, Ver2: TVersionArray; 153 | I: Integer; 154 | Word1, Word2: Word; 155 | begin 156 | result := 0; 157 | if not GetVersionArray(V1, Ver1) then 158 | exit; 159 | if not GetVersionArray(V2, Ver2) then 160 | exit; 161 | for I := 0 to 3 do 162 | begin 163 | Word1 := Ver1[I]; 164 | Word2 := Ver2[I]; 165 | if Word1 > Word2 then 166 | begin 167 | result := 1; 168 | exit; 169 | end 170 | else if Word1 < Word2 then 171 | begin 172 | result := -1; 173 | exit; 174 | end; 175 | end; 176 | end; 177 | 178 | function ExpandVersionString(const S: string): string; 179 | var 180 | Version: TVersionArray; 181 | I: Integer; 182 | begin 183 | result := ''; 184 | if not GetVersionArray(S, Version) then 185 | exit; 186 | result := IntToStr(Version[0]); 187 | for I := 1 to 3 do 188 | result := result + '.' + IntToStr(Version[I]); 189 | end; 190 | 191 | begin 192 | end. 193 | -------------------------------------------------------------------------------- /GetJavaInfo.md: -------------------------------------------------------------------------------- 1 | # GetJavaInfo 2 | 3 | GetJavaInfo is a console mode (command line) Windows utility that provides information about Java installations. 4 | 5 | ## Author 6 | 7 | Bill Stewart - bstewart at iname dot com 8 | 9 | ## License 10 | 11 | GetJavaInfo is covered by the GNU Lesser License (LPGL). See the file `LICENSE` for details. 12 | 13 | ## Download 14 | 15 | https://github.com/Bill-Stewart/JavaInfo/releases/ 16 | 17 | ## Description 18 | 19 | GetJavaInfo is a command line utility that uses the same code as JavaInfo.dll to detect Java installation details. GetJavaInfo does not require JavaInfo.dll. 20 | 21 | ## Usage 22 | 23 | `GetJavaInfo` [`--javainstalled` | `--javahome` | `--javaversion` | `--javais64bit`] [`--quiet`] 24 | 25 | or: 26 | 27 | `GetJavaInfo` `--javaminversion` _version_ [`--quiet`] 28 | 29 | Without command-line parameters, GetJavaInfo outputs Java installation details. The following table describes the command line parameters: 30 | 31 | | Parameter | Abbreviation | Description 32 | | ------------------ | ------------ | ------------------------------------- 33 | | `--javainstalled` | `-i` | Tests if Java is installed 34 | | `--javahome` | `-H` | Outputs Java home directory 35 | | `--javadll` | `-d` | Outputs jvm.dll path 36 | | `--javaversion` | `-V` | Outputs Java version 37 | | `--javais64bit` | `-b` | Tests if Java is 64-bit 38 | | `--version` | `-v` | Outputs this program's version number 39 | | `--javaminversion` | `-m` | Tests for a minimum version of Java 40 | | `--quiet` | `-q` | Suppresses output from other options 41 | | `--help` | `-h` | Outputs help information 42 | 43 | Parameters can be spelled out (e.g., `--javahome`) or used in abbreviated form (e.g., `-H`). Parameters are case-sensitive. 44 | 45 | General exit codes: 46 | 47 | * 0 = no errors/Java is installed 48 | * 2 = Java is not installed 49 | * 87 = invalid parameter on command line 50 | 51 | Exit codes with `--javais64bit` (`-b`) parameter: 52 | 53 | * 0 = Java is not 64-bit 54 | * 1 = Java is 64-bit 55 | 56 | Exit codes with `--javaminversion` (`-m`) parameter: 57 | 58 | * 0 = Java version is < specified version 59 | * 1 = Java version is >= specified version 60 | 61 | When specifying the `--javaminversion` (`-m`) parameter, specify a version number after the parameter in on the command line in the following format: 62 | 63 | _n_[._n_[._n_[._n_]]] 64 | 65 | That is, up to 4 numbers (in the range 0 through 65535, inclusive) separated by dots (`.`). Values not specified are assumed to be zero. Examples: 66 | 67 | | Version | Value 68 | | ------- | -------- 69 | | 11 | 11.0.0.0 70 | | 8.1 | 8.1.0.0 71 | | 7.5.1 | 7.5.1.0 72 | 73 | If the version number does not follow the above format, GetJavaInfo will exit with error code 87 (87 is the Windows error code `ERROR_INVALID_PARAMETER`). 74 | 75 | For example: 76 | 77 | GetJavaInfo --javaminversion 11.0.9.1 78 | 79 | In this example, GetJavaInfo will return one of the following exit codes: 80 | 81 | * 0 - Java is installed, and is at least version 11.0.9.1 82 | * 1 - Java is installed, but is less than version 11.0.9.1 83 | * 2 - Java is not installed 84 | 85 | ## Examples 86 | 87 | 1. `GetJavaInfo --javainstalled --quiet` 88 | 89 | Returns an exit code 0 if Java is installed or an exit code of 2 if Java is not installed. The program produces no output. 90 | 91 | 2. Sample batch file (cmd.exe shell script) for getting the Java home directory: 92 | 93 | @echo off 94 | setlocal enableextensions 95 | set _JHOME= 96 | for /f "delims=" %%a in ('GetJavaInfo --javahome') do set _JHOME=%%a 97 | if "%_JHOME%"=="" goto :NOJAVA 98 | echo Java home: %_JHOME% 99 | goto :END 100 | :NOJAVA 101 | echo Java is not installed 102 | :END 103 | endlocal 104 | 105 | This script sets the `_JHOME` environment variable within the script to the Java home directory if Java is installed. The variable will not be set if Java is not instaled. 106 | 107 | 3. Sample batch file (cmd.exe shell script) for checking whether Java is installed and is at least a minimum version: 108 | 109 | @echo off 110 | setlocal enableextensions 111 | set _JMINVER=11 112 | GetJavaInfo --javaminversion %_JMINVER% --quiet 113 | if errorlevel 2 goto :NOJAVA 114 | if errorlevel 1 goto :VER_OK 115 | echo Java is installed, but it is less than version %_JMINVER% 116 | goto :END 117 | :VER_OK 118 | echo At least Java %_JMINVER% is installed 119 | goto :END 120 | :NOJAVA 121 | echo Java is not installed 122 | :END 123 | endlocal 124 | 125 | 4. Sample batch file (cmd.exe shell script) for getting the path and filename of jvm.dll in an environment variable: 126 | 127 | @echo off 128 | setlocal enableextensions 129 | GetJavaInfo -i -q 130 | if errorlevel 2 goto :NOJAVA 131 | set _JVMPATH= 132 | for /f "delims=" %%a in ('GetJavaInfo --javadll') do set _JVMPATH=%%a 133 | echo jvm.dll path: %_JVMPATH% 134 | goto :END 135 | :NOJAVA 136 | echo Java is not installed 137 | :END 138 | endlocal 139 | -------------------------------------------------------------------------------- /wsUtilStr.pp: -------------------------------------------------------------------------------- 1 | { Copyright (C) 2020-2023 by Bill Stewart (bstewart at iname.com) 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU Lesser General Public License as published by the Free 5 | Software Foundation; either version 3 of the License, or (at your option) any 6 | later version. 7 | 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Lesser Public License for more 11 | details. 12 | 13 | You should have received a copy of the GNU Lesser General Public License 14 | along with this program. If not, see https://www.gnu.org/licenses/. 15 | 16 | } 17 | 18 | {$MODE OBJFPC} 19 | {$MODESWITCH UNICODESTRINGS} 20 | 21 | unit wsUtilStr; 22 | 23 | interface 24 | 25 | uses 26 | Windows; 27 | 28 | // Scans a string for digit characters and returns found digits in OutputStr; 29 | // returns false if InputStr contains no digits 30 | function GetDigitsInString(const InputStr: string; var OutputStr: string): Boolean; 31 | 32 | // Returns I as a string 33 | function IntToStr(const I: Integer): string; 34 | 35 | // Removes trailing '\' from string unless string is 3 characters long and 36 | // second character is ':' 37 | function RemoveBackslashUnlessRoot(S: string): string; 38 | 39 | // Converts the specified string to a Unicode string 40 | function AnsiToUnicodeString(const S: RawByteString; const CodePage: UINT): string; 41 | 42 | // Converts the specified Unicode string to a regular (multi-byte) string and 43 | // returns the resulting multi-byte string 44 | function UnicodeStringToAnsi(const S: string; const CodePage: UINT): RawByteString; 45 | 46 | implementation 47 | 48 | function GetDigitsInString(const InputStr: string; var OutputStr: string): Boolean; 49 | const 50 | Digits: string = '0123456789'; 51 | var 52 | DigitStr: string; 53 | I: Integer; 54 | begin 55 | DigitStr := ''; 56 | for I := 1 to Length(InputStr) do 57 | if Pos(InputStr[I], Digits) > 0 then 58 | DigitStr := DigitStr + InputStr[I]; 59 | result := DigitStr <> ''; 60 | if result then 61 | OutputStr := DigitStr; 62 | end; 63 | 64 | function IntToStr(const I: Integer): string; 65 | begin 66 | Str(I, result); 67 | end; 68 | 69 | function RemoveBackslashUnlessRoot(S: string): string; 70 | begin 71 | result := ''; 72 | if s <> '' then 73 | begin 74 | if (Length(S) = 3) and (S[2] = ':') and (S[3] = '\') then 75 | exit(S); 76 | while S[Length(S)] = '\' do 77 | SetLength(S, Length(S) - 1); 78 | result := S; 79 | end; 80 | end; 81 | 82 | function AnsiToUnicodeString(const S: RawByteString; const CodePage: UINT): string; 83 | var 84 | NumChars, BufSize: DWORD; 85 | pBuffer: PChar; 86 | begin 87 | result := ''; 88 | // Get number of characters needed for buffer 89 | NumChars := MultiByteToWideChar(CodePage, // UINT CodePage 90 | 0, // DWORD dwFlags 91 | PAnsiChar(S), // LPCCH lpMultiByteStr 92 | -1, // int cbMultiByte 93 | nil, // LPWSTR lpWideCharStr 94 | 0); // int cchWideChar 95 | if NumChars > 0 then 96 | begin 97 | BufSize := NumChars * SizeOf(Char); 98 | GetMem(pBuffer, BufSize); 99 | if MultiByteToWideChar(CodePage, // UINT CodePage 100 | 0, // DWORD dwFlags 101 | PAnsiChar(S), // LPCCH lpMultiByteStr 102 | -1, // int cbMultiByte 103 | pBuffer, // LPWSTR lpWideCharStr 104 | NumChars) > 0 then // int cchWideChar 105 | begin 106 | result := pBuffer; 107 | end; 108 | FreeMem(pBuffer); 109 | end; 110 | end; 111 | 112 | function UnicodeStringToAnsi(const S: string; const CodePage: UINT): RawByteString; 113 | var 114 | NumChars, BufSize: DWORD; 115 | pBuffer: PAnsiChar; 116 | begin 117 | result := ''; 118 | // Get number of characters needed for buffer 119 | NumChars := WideCharToMultiByte(CodePage, // UINT CodePage 120 | 0, // DWORD dwFlags 121 | PChar(S), // LPCWCH lpWideCharStr 122 | -1, // int cchWideChar 123 | nil, // LPSTR lpMultiByteStr 124 | 0, // int cbMultiByte 125 | nil, // LPCCH lpDefaultChar 126 | nil); // LPBOOL lpUsedDefaultChar 127 | if NumChars > 0 then 128 | begin 129 | BufSize := NumChars * SizeOf(AnsiChar); 130 | GetMem(pBuffer, BufSize); 131 | if WideCharToMultiByte(CodePage, // UINT CodePage 132 | 0, // DWORD dwFlags 133 | PChar(S), // LPCWCH lpWideCharStr 134 | -1, // int cchWideChar 135 | pBuffer, // LPSTR lpMultiByteStr 136 | NumChars, // int cbMultiByte 137 | nil, // LPCCH lpDefaultChar 138 | nil) > 0 then // LPBOOL lpUsedDefaultChar 139 | begin 140 | result := pBuffer; 141 | end; 142 | FreeMem(pBuffer); 143 | end; 144 | end; 145 | 146 | begin 147 | end. 148 | -------------------------------------------------------------------------------- /JavaInfoTest.ps1: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020-2023 by Bill Stewart (bstewart at iname.com) 2 | # 3 | # This program is free software; you can redistribute it and/or modify it under 4 | # the terms of the GNU Lesser General Public License as published by the Free 5 | # Software Foundation; either version 3 of the License, or (at your option) any 6 | # later version. 7 | # 8 | # This program is distributed in the hope that it will be useful, but WITHOUT 9 | # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | # FOR A PARTICULAR PURPOSE. See the GNU General Lesser Public License for more 11 | # details. 12 | # 13 | # You should have received a copy of the GNU Lesser General Public License 14 | # along with this program. If not, see https://www.gnu.org/licenses/. 15 | 16 | #requires -version 2 17 | 18 | # Prerequisites: 19 | # * 64-bit JavaInfo.dll v1.2.0.0 or later in x86_64 directory 20 | # * 32-bit JavaInfo.dll v1.2.0.0 or later in i386 directory 21 | 22 | # If you use -MinimumVersion parameter, specify a version number string as 23 | # a[.b[.c[.d]]]; e.g. "-MinimumVersion 8" would test for at least Java 8 24 | param( 25 | [String] 26 | $MinimumVersion 27 | ) 28 | 29 | function Get-Platform { 30 | if ( [IntPtr]::Size -eq 8 ) { 31 | "x86_64" 32 | } 33 | else { 34 | "i386" 35 | } 36 | } 37 | 38 | $APIDefs = @" 39 | [DllImport("{0}\\JavaInfo.dll", CharSet = CharSet.Unicode, SetLastError = true)] 40 | public static extern uint IsBinary64Bit( 41 | string FileName, 42 | out uint Is64bit 43 | ); 44 | 45 | [DllImport("{0}\\JavaInfo.dll", CharSet = CharSet.Unicode, SetLastError = true)] 46 | public static extern uint IsJavaInstalled(); 47 | 48 | [DllImport("{0}\\JavaInfo.dll", CharSet = CharSet.Unicode, SetLastError = true)] 49 | public static extern uint IsJavaMinimumVersion( 50 | string Version, 51 | out uint VersionOK 52 | ); 53 | 54 | [DllImport("{0}\\JavaInfo.dll", CharSet = CharSet.Unicode, SetLastError = true)] 55 | public static extern uint GetJavaHome( 56 | StringBuilder PathName, 57 | uint NumChars 58 | ); 59 | 60 | [DllImport("{0}\\JavaInfo.dll", CharSet = CharSet.Unicode, SetLastError = true)] 61 | public static extern uint GetJavaJVMPath( 62 | StringBuilder PathName, 63 | uint NumChars 64 | ); 65 | 66 | [DllImport("{0}\\JavaInfo.dll", CharSet = CharSet.Unicode, SetLastError = true)] 67 | public static extern uint GetJavaVersion( 68 | StringBuilder Version, 69 | uint NumChars 70 | ); 71 | "@ -f (Get-Platform) 72 | 73 | $JavaInfo = Add-Type -Name JavaInfo ` 74 | -MemberDefinition $APIDefs ` 75 | -Namespace "D8BE189018594F8F841FDF1359E47C6C" ` 76 | -UsingNamespace "System.Text" ` 77 | -PassThru ` 78 | -ErrorAction Stop 79 | 80 | # DLL function wrapper to retrieve strings 81 | function Get-DLLString { 82 | param( 83 | [Management.Automation.PSMethod] 84 | $dllFunction 85 | ) 86 | $result = "" 87 | # Create a StringBuilder with 0 capacity to get string length 88 | $stringBuilder = New-Object Text.StringBuilder(0) 89 | # Invoke function with 0 for 2nd parameter to get length as return value 90 | $numChars = $dllFunction.Invoke($stringBuilder,0) 91 | if ( $numChars -gt 0 ) { 92 | # Specify size of string and call function again 93 | $stringBuilder.Capacity = $numChars 94 | if ( $dllFunction.Invoke($stringBuilder,$numChars) -gt 0 ) { 95 | $result = $stringBuilder.ToString() 96 | } 97 | } 98 | $result 99 | } 100 | 101 | # Wrapper for GetJavaHome() DLL function 102 | function Get-JavaHome { 103 | Get-DLLString ($JavaInfo::GetJavaHome) 104 | } 105 | 106 | function Get-JavaJVMPath { 107 | Get-DLLString ($JavaInfo::GetJavaJVMPath) 108 | } 109 | 110 | # Wrapper for GetJavaVersion() DLL function 111 | function Get-JavaVersion { 112 | Get-DLLString ($JavaInfo::GetJavaVersion) 113 | } 114 | 115 | # Wrapper for IsBinary64Bit() DLL function 116 | function Get-Binary64Bit { 117 | param( 118 | [String] 119 | $fileName 120 | ) 121 | $result = "Unknown" 122 | if ( $fileName ) { 123 | # Variable must exist before calling as [Ref] parameter 124 | $is64Bit = $null 125 | # Function returns 0 if successful 126 | if ( $JavaInfo::IsBinary64Bit($fileName,[Ref] $is64Bit) -eq 0 ) { 127 | if ( $is64Bit -eq 1 ) { 128 | $result = "Yes" 129 | } 130 | else { 131 | $result = "No" 132 | } 133 | } 134 | } 135 | $result 136 | } 137 | 138 | # Wrapper for IsJavaMinimumVersion() DLL function 139 | function Get-JavaMinimumVersion { 140 | param( 141 | [String] 142 | $version 143 | ) 144 | $result = "" 145 | # Variable must exist before calling as [Ref] parameter 146 | $versionOK = $null 147 | if ( $JavaInfo::IsJavaMinimumVersion($version,[Ref] $versionOK) -eq 0 ) { 148 | if ( $versionOK -eq 1 ) { 149 | $result = "Yes" 150 | } 151 | else { 152 | $result = "No" 153 | } 154 | } 155 | $result 156 | } 157 | 158 | # Main body of script follows... 159 | 160 | try { 161 | $IsJavaInstalled = $JavaInfo::IsJavaInstalled() -eq 1 162 | } 163 | catch { 164 | # End script with error message if DLL function failed 165 | Write-Error $_ 166 | return 167 | } 168 | 169 | if ( $IsJavaInstalled ) { 170 | $JavaHome = Get-JavaHome 171 | $JavaJVMPath = Get-JavaJVMPath 172 | $JavaBinary = Join-Path $JavaHome "bin\java.exe" 173 | "Java home:`t{0}" -f $JavaHome 174 | "jvm.dll path:`t{0}" -f $JavaJVMPath 175 | "Java version:`t{0}" -f (Get-JavaVersion) 176 | "Java is 64-bit:`t{0}" -f (Get-Binary64Bit $JavaBinary) 177 | if ( $MinimumVersion ) { 178 | $TestVersion = Get-JavaMinimumVersion $MinimumVersion 179 | if ( $TestVersion -ne "" ) { 180 | "At least version {0}:`t{1}" -f $MinimumVersion,$TestVersion 181 | } 182 | else { 183 | Write-Warning "Invalid version specified with -MinimumVersion parameter." 184 | } 185 | } 186 | } 187 | else { 188 | "JavaInfo.dll did not detect a Java installation." 189 | } 190 | -------------------------------------------------------------------------------- /JavaInfo.pp: -------------------------------------------------------------------------------- 1 | { Copyright (C) 2020-2023 by Bill Stewart (bstewart at iname.com) 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU Lesser General Public License as published by the Free 5 | Software Foundation; either version 3 of the License, or (at your option) any 6 | later version. 7 | 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Lesser Public License for more 11 | details. 12 | 13 | You should have received a copy of the GNU Lesser General Public License 14 | along with this program. If not, see https://www.gnu.org/licenses/. 15 | 16 | } 17 | 18 | { 19 | Note Regarding the GetJavaHome(), GetJavaJVMPath(), and GetJavaVersion() 20 | Functions 21 | ======================================================================== 22 | You must allocate a buffer to get the result strings from these functions. To 23 | determine the required buffer size, call the function with a null pointer for 24 | the first parameter and 0 for the second parameter. The function will return 25 | the number of characters required for the buffer, not including the 26 | terminating null character. Allocate the buffer (don't forget to account for 27 | the terminating null character), then call the function again. 28 | 29 | // Example using WideChar pointer 30 | function GetJavaHomeDir(): UnicodeString; 31 | var 32 | NumChars, BufSize: DWORD; 33 | OutStr: PWideChar; 34 | begin 35 | result := ''; 36 | // First call: Get number of characters needed 37 | NumChars := GetJavaHome(nil, 0); 38 | // Specify buffer size (+ space for null terminator character) 39 | BufSize := NumChars * SizeOf(WideChar) + SizeOf(WideChar); 40 | // Allocate the buffer 41 | GetMem(OutStr, BufSize); 42 | // Second call: Get the string 43 | if GetJavaHome(OutStr, NumChars) > 0 then 44 | result := OutStr; 45 | FreeMem(OutStr); 46 | end; 47 | 48 | // Example using UnicodeString 49 | function GetJavaHomeDir(): UnicodeString; 50 | var 51 | NumChars: DWORD; 52 | OutStr: UnicodeString; 53 | begin 54 | result := ''; 55 | // First call: Get number of characters needed 56 | NumChars := GetJavaHome(nil, 0); 57 | // Allocate space for the string (accounts for null terminator) 58 | SetLength(OutStr, NumChars); 59 | // Second call: Get the string 60 | if GetJavaHome(PWideChar(OutStr), NumChars) > 0 then 61 | result := OutStr; 62 | end; 63 | 64 | } 65 | 66 | {$MODE OBJFPC} 67 | {$MODESWITCH UNICODESTRINGS} 68 | {$R *.res} 69 | 70 | library JavaInfo; 71 | 72 | uses 73 | Windows, 74 | wsJavaInfo; 75 | 76 | type 77 | TStringFunction = function(): string; 78 | 79 | // Copies Source to Dest. 80 | procedure CopyString(const Source: string; Dest: PChar); 81 | var 82 | NumChars: DWORD; 83 | begin 84 | NumChars := Length(Source); 85 | Move(Source[1], Dest^, NumChars * SizeOf(Char)); 86 | Dest[NumChars] := #0; 87 | end; 88 | 89 | // First parameter is address of string function you want to call. Returns 90 | // number of characters needed for output buffer, not including the terminating 91 | // null character. 92 | function GetString(var StringFunction: TStringFunction; Buffer: PChar; const NumChars: DWORD): DWORD; 93 | var 94 | OutStr: string; 95 | begin 96 | OutStr := StringFunction(); 97 | if (Length(OutStr) > 0) and Assigned(Buffer) and (NumChars >= Length(OutStr)) then 98 | CopyString(OutStr, Buffer); 99 | result := Length(OutStr); 100 | end; 101 | 102 | // Gets Java home directory into string buffer pointed to by PathName. 103 | function GetJavaHome(PathName: PChar; NumChars: DWORD): DWORD; stdcall; 104 | var 105 | StringFunction: TStringFunction; 106 | begin 107 | StringFunction := @wsGetJavaHome; 108 | result := GetString(StringFunction, PathName, NumChars); 109 | end; 110 | 111 | // Gets path of jvm.dll into string buffer pointed to by PathName. 112 | function GetJavaJVMPath(PathName: PChar; NumChars: DWORD): DWORD; stdcall; 113 | var 114 | StringFunction: TStringFunction; 115 | begin 116 | StringFunction := @wsGetJavaJVMPath; 117 | result := GetString(StringFunction, PathName, NumChars); 118 | end; 119 | 120 | // Gets Java version string (a.b.c.d) into string buffer pointed to by Version. 121 | function GetJavaVersion(Version: PChar; NumChars: DWORD): DWORD; stdcall; 122 | var 123 | StringFunction: TStringFunction; 124 | begin 125 | StringFunction := @wsGetJavaVersion; 126 | result := GetString(StringFunction, Version, NumChars); 127 | end; 128 | 129 | // Retrieves whether a specified binary is 64-bit or not to the Is64Bit 130 | // parameter. Returns 0 for success, or non-zero for failure. If successful, 131 | // value pointed to by Is64Bit will be 0 if not 64-bit, or 1 otherwise. 132 | function IsBinary64Bit(FileName: PChar; Is64Bit: PDWORD): DWORD; stdcall; 133 | var 134 | Is64: Boolean; 135 | begin 136 | result := wsIsBinary64Bit(FileName, Is64); 137 | if result = 0 then 138 | begin 139 | if Is64 then 140 | Is64Bit^ := 1 141 | else 142 | Is64Bit^ := 0; 143 | end; 144 | end; 145 | 146 | // Returns 1 if Java installation detected or 0 otherwise. 147 | function IsJavaInstalled(): DWORD; stdcall; 148 | begin 149 | if wsIsJavaInstalled() then 150 | result := 1 151 | else 152 | result := 0; 153 | end; 154 | 155 | // Retrieves whether the installed Java version is at least the specified 156 | // version to the VersionOK parameter. Returns 0 for success, or non-zero for 157 | // failure. If successful, the value pointed to by VersionOK will be 1 if the 158 | // installed Java version is at least the specified version, or 0 otherwise. 159 | function IsJavaMinimumVersion(Version: PChar; VersionOK: PDWORD): DWORD; stdcall; 160 | var 161 | IsOK: Boolean; 162 | begin 163 | if wsIsJavaMinimumVersion(Version, IsOK) then 164 | begin 165 | result := ERROR_SUCCESS; 166 | if IsOK then 167 | VersionOK^ := 1 168 | else 169 | VersionOK^ := 0; 170 | end 171 | else 172 | result := ERROR_INVALID_PARAMETER; 173 | end; 174 | 175 | exports 176 | GetJavaHome, 177 | GetJavaJVMPath, 178 | GetJavaVersion, 179 | IsBinary64Bit, 180 | IsJavaInstalled, 181 | IsJavaMinimumVersion; 182 | 183 | end. 184 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU LESSER GENERAL PUBLIC LICENSE 2 | 3 | Version 3, 29 June 2007 4 | 5 | Copyright (C) 2007 Free Software Foundation, Inc. https://fsf.org/ 6 | 7 | Everyone is permitted to copy and distribute verbatim copies of this license 8 | document, but changing it is not allowed. 9 | 10 | This version of the GNU Lesser General Public License incorporates the terms 11 | and conditions of version 3 of the GNU General Public License, supplemented by 12 | the additional permissions listed below. 13 | 14 | 0. Additional Definitions. 15 | 16 | As used herein, "this License" refers to version 3 of the GNU Lesser General 17 | Public License, and the "GNU GPL" refers to version 3 of the GNU General Public 18 | License. 19 | 20 | "The Library" refers to a covered work governed by this License, other than an 21 | Application or a Combined Work as defined below. 22 | 23 | An "Application" is any work that makes use of an interface provided by the 24 | Library, but which is not otherwise based on the Library. Defining a subclass 25 | of a class defined by the Library is deemed a mode of using an interface 26 | provided by the Library. 27 | 28 | A "Combined Work" is a work produced by combining or linking an Application 29 | with the Library. The particular version of the Library with which the Combined 30 | Work was made is also called the "Linked Version". 31 | 32 | The "Minimal Corresponding Source" for a Combined Work means the Corresponding 33 | Source for the Combined Work, excluding any source code for portions of the 34 | Combined Work that, considered in isolation, are based on the Application, and 35 | not on the Linked Version. 36 | 37 | The "Corresponding Application Code" for a Combined Work means the object code 38 | and/or source code for the Application, including any data and utility programs 39 | needed for reproducing the Combined Work from the Application, but excluding 40 | the System Libraries of the Combined Work. 41 | 42 | 1. Exception to Section 3 of the GNU GPL. 43 | 44 | You may convey a covered work under sections 3 and 4 of this License without 45 | being bound by section 3 of the GNU GPL. 46 | 47 | 2. Conveying Modified Versions. 48 | 49 | If you modify a copy of the Library, and, in your modifications, a facility 50 | refers to a function or data to be supplied by an Application that uses the 51 | facility (other than as an argument passed when the facility is invoked), then 52 | you may convey a copy of the modified version: 53 | 54 | a) under this License, provided that you make a good faith effort to ensure 55 | that, in the event an Application does not supply the function or data, the 56 | facility still operates, and performs whatever part of its purpose remains 57 | meaningful, or 58 | 59 | b) under the GNU GPL, with none of the additional permissions of this License 60 | applicable to that copy. 61 | 62 | 3. Object Code Incorporating Material from Library Header Files. 63 | 64 | The object code form of an Application may incorporate material from a header 65 | file that is part of the Library. You may convey such object code under terms 66 | of your choice, provided that, if the incorporated material is not limited to 67 | numerical parameters, data structure layouts and accessors, or small macros, 68 | inline functions and templates (ten or fewer lines in length), you do both of 69 | the following: 70 | 71 | a) Give prominent notice with each copy of the object code that the Library is 72 | used in it and that the Library and its use are covered by this License. 73 | 74 | b) Accompany the object code with a copy of the GNU GPL and this license 75 | document. 76 | 77 | 4. Combined Works. 78 | 79 | You may convey a Combined Work under terms of your choice that, taken together, 80 | effectively do not restrict modification of the portions of the Library 81 | contained in the Combined Work and reverse engineering for debugging such 82 | modifications, if you also do each of the following: 83 | 84 | a) Give prominent notice with each copy of the Combined Work that the Library 85 | is used in it and that the Library and its use are covered by this License. 86 | 87 | b) Accompany the Combined Work with a copy of the GNU GPL and this license 88 | document. 89 | 90 | c) For a Combined Work that displays copyright notices during execution, 91 | include the copyright notice for the Library among these notices, as well as a 92 | reference directing the user to the copies of the GNU GPL and this license 93 | document. 94 | 95 | d) Do one of the following: 96 | 97 | 0) Convey the Minimal Corresponding Source under the terms of this License, 98 | and the Corresponding Application Code in a form suitable for, and under 99 | terms that permit, the user to recombine or relink the Application with a 100 | modified version of the Linked Version to produce a modified Combined Work, 101 | in the manner specified by section 6 of the GNU GPL for conveying 102 | Corresponding Source. 103 | 104 | 1) Use a suitable shared library mechanism for linking with the Library. A 105 | suitable mechanism is one that (a) uses at run time a copy of the Library 106 | already present on the user's computer system, and (b) will operate properly 107 | with a modified version of the Library that is interface-compatible with the 108 | Linked Version. 109 | 110 | e) Provide Installation Information, but only if you would otherwise be 111 | required to provide such information under section 6 of the GNU GPL, and only 112 | to the extent that such information is necessary to install and execute a 113 | modified version of the Combined Work produced by recombining or relinking the 114 | Application with a modified version of the Linked Version. (If you use option 115 | 4d0, the Installation Information must accompany the Minimal Corresponding 116 | Source and Corresponding Application Code. If you use option 4d1, you must 117 | provide the Installation Information in the manner specified by section 6 of 118 | the GNU GPL for conveying Corresponding Source.) 119 | 120 | 5. Combined Libraries. 121 | 122 | You may place library facilities that are a work based on the Library side by 123 | side in a single library together with other library facilities that are not 124 | Applications and are not covered by this License, and convey such a combined 125 | library under terms of your choice, if you do both of the following: 126 | 127 | a) Accompany the combined library with a copy of the same work based on the 128 | Library, uncombined with any other library facilities, conveyed under the terms 129 | of this License. 130 | 131 | b) Give prominent notice with the combined library that part of it is a work 132 | based on the Library, and explaining where to find the accompanying uncombined 133 | form of the same work. 134 | 135 | 6. Revised Versions of the GNU Lesser General Public License. 136 | 137 | The Free Software Foundation may publish revised and/or new versions of the GNU 138 | Lesser General Public License from time to time. Such new versions will be 139 | similar in spirit to the present version, but may differ in detail to address 140 | new problems or concerns. 141 | 142 | Each version is given a distinguishing version number. If the Library as you 143 | received it specifies that a certain numbered version of the GNU Lesser General 144 | Public License "or any later version" applies to it, you have the option of 145 | following the terms and conditions either of that published version or of any 146 | later version published by the Free Software Foundation. If the Library as you 147 | received it does not specify a version number of the GNU Lesser General Public 148 | License, you may choose any version of the GNU Lesser General Public License 149 | ever published by the Free Software Foundation. 150 | 151 | If the Library as you received it specifies that a proxy can decide whether 152 | future versions of the GNU Lesser General Public License shall apply, that 153 | proxy's public statement of acceptance of any version is permanent 154 | authorization for you to choose that version for the Library. -------------------------------------------------------------------------------- /wsUtilFile.pp: -------------------------------------------------------------------------------- 1 | { Copyright (C) 2020-2023 by Bill Stewart (bstewart at iname.com) 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU Lesser General Public License as published by the Free 5 | Software Foundation; either version 3 of the License, or (at your option) any 6 | later version. 7 | 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Lesser Public License for more 11 | details. 12 | 13 | You should have received a copy of the GNU Lesser General Public License 14 | along with this program. If not, see https://www.gnu.org/licenses/. 15 | 16 | } 17 | 18 | 19 | {$MODE OBJFPC} 20 | {$MODESWITCH UNICODESTRINGS} 21 | 22 | unit wsUtilFile; 23 | 24 | interface 25 | 26 | // Returns true if the named directorty exists, or false if not 27 | function DirExists(const DirName: string): Boolean; 28 | 29 | // Returns directory name from a path without trailing separator 30 | function ExtractFileDir(const FileName: string): string; 31 | 32 | // Returns true if the named file exists, or false if not 33 | function FileExists(const FileName: string): Boolean; 34 | 35 | // Searches for a named file in semicolon-delimited list of directory names; 36 | // returns an empty string if nothing found 37 | function FileSearch(const Name, DirList: string): string; 38 | 39 | // Gets the specified file's binary type to the BinaryType parameter; returns 40 | // 0 for success, or non-zero for failure 41 | function GetBinaryType(const FileName: string; var BinaryType: Word): DWORD; 42 | 43 | // Returns a version number string (a.b.c.d) for the named file; returns an 44 | // empty string if the function failed (e.g., no version information found) 45 | function GetFileVersion(const FileName: string): string; 46 | 47 | // Concatenates Path1 to Path2 with only a single path separator between 48 | function JoinPath(Path1, Path2: string): string; 49 | 50 | implementation 51 | 52 | uses 53 | imagehlp, 54 | Windows, 55 | wsUtilArch, 56 | wsUtilStr; 57 | 58 | const 59 | INVALID_FILE_ATTRIBUTES = DWORD(-1); 60 | 61 | var 62 | PerformWow64FsRedirection: Boolean; 63 | Wow64FsRedirectionOldValue: Pointer; 64 | 65 | procedure ToggleWow64FsRedirection(); 66 | begin 67 | if PerformWow64FsRedirection then 68 | begin 69 | if not Assigned(Wow64FsRedirectionOldValue) then 70 | begin 71 | if not Wow64DisableWow64FsRedirection(@Wow64FsRedirectionOldValue) then // PVOID *OldValue 72 | Wow64FsRedirectionOldValue := nil; 73 | end 74 | else 75 | begin 76 | if Wow64RevertWow64FsRedirection(Wow64FsRedirectionOldValue) then // PVOID OlValue 77 | Wow64FsRedirectionOldValue := nil; 78 | end; 79 | end; 80 | end; 81 | 82 | function DirExists(const DirName: string): Boolean; 83 | var 84 | Attrs: DWORD; 85 | begin 86 | ToggleWow64FsRedirection(); 87 | Attrs := GetFileAttributesW(PChar(DirName)); // LPCWSTR lpFileName 88 | ToggleWow64FsRedirection(); 89 | result := (Attrs <> INVALID_FILE_ATTRIBUTES) and 90 | ((Attrs and FILE_ATTRIBUTE_DIRECTORY) <> 0); 91 | end; 92 | 93 | function ExtractFileDir(const FileName: string): string; 94 | const 95 | Separators: string = ':\'; 96 | var 97 | I: Integer; 98 | begin 99 | I := Length(FileName); 100 | while (I > 0) and (Pos(FileName[I], Separators) > 0) do 101 | Dec(I); 102 | if (I > 1) and (FileName[I] = '\') and (Pos(FileName[I - 1], Separators) > 0) then 103 | Dec(I); 104 | result := Copy(FileName, 1, I); 105 | end; 106 | 107 | function FileExists(const FileName: string): Boolean; 108 | var 109 | Attrs: DWORD; 110 | begin 111 | ToggleWow64FsRedirection(); 112 | Attrs := GetFileAttributesW(PChar(FileName)); // LPCWSTR lpFileName 113 | ToggleWow64FsRedirection(); 114 | result := (Attrs <> INVALID_FILE_ATTRIBUTES) and 115 | ((Attrs and FILE_ATTRIBUTE_DIRECTORY) = 0); 116 | end; 117 | 118 | function FileSearch(const Name, DirList: string): string; 119 | var 120 | NumChars, BufSize: DWORD; 121 | pBuffer: PChar; 122 | begin 123 | result := ''; 124 | // Get number of characters needed for buffer 125 | ToggleWow64FsRedirection(); 126 | NumChars := SearchPathW(PChar(DirList), // LPCSTR lpPath 127 | PChar(Name), // LPCSTR lpFilename 128 | nil, // LPCSTR lpExtension 129 | 0, // DWORD nBufferLength 130 | nil, // LPSTR lpBuffer 131 | nil); // LPSTR lpFilePart 132 | if NumChars > 0 then 133 | begin 134 | BufSize := NumChars * SizeOf(Char); 135 | GetMem(pBuffer, BufSize); 136 | if SearchPathW(PChar(DirList), // LPCSTR lpPath 137 | PChar(Name), // LPCSTR lpFilename 138 | nil, // LPCSTR lpExtension 139 | NumChars, // DWORD nBufferLength 140 | pBuffer, // LPSTR lpBuffer 141 | nil) > 0 then // LPSTR lpFilePart 142 | begin 143 | result := pBuffer; 144 | end; 145 | FreeMem(pBuffer); 146 | end; 147 | ToggleWow64FsRedirection(); 148 | end; 149 | 150 | function GetBinaryType(const FileName: string; var BinaryType: Word): DWORD; 151 | var 152 | ImagePath: RawByteString; 153 | pLoadedImage: PLOADED_IMAGE; 154 | begin 155 | ImagePath := UnicodeStringToAnsi(FileName, CP_ACP); 156 | if Length(ImagePath) = 0 then 157 | exit(ERROR_INVALID_DATA); 158 | ToggleWow64FsRedirection(); 159 | pLoadedImage := ImageLoad(PAnsiChar(ImagePath), // PCSTR DllName 160 | ''); // PCSTR DllPath 161 | ToggleWow64FsRedirection(); 162 | if Assigned(pLoadedImage) then 163 | result := 0 164 | else 165 | result := GetLastError(); 166 | if result = 0 then 167 | begin 168 | BinaryType := pLoadedImage^.Fileheader^.FileHeader.Machine; 169 | ImageUnload(pLoadedImage); 170 | end; 171 | end; 172 | 173 | function GetFileVersion(const FileName: string): string; 174 | var 175 | VerInfoSize, Handle: DWORD; 176 | pBuffer: Pointer; 177 | pFileInfo: ^VS_FIXEDFILEINFO; 178 | Len: UINT; 179 | begin 180 | result := ''; 181 | ToggleWow64FsRedirection(); 182 | VerInfoSize := GetFileVersionInfoSizeW(PChar(FileName), // LPCWSTR lptstrFilename 183 | Handle); // LPDWORD lpdwHandle 184 | if VerInfoSize > 0 then 185 | begin 186 | GetMem(pBuffer, VerInfoSize); 187 | if GetFileVersionInfoW(PChar(FileName), // LPCWSTR lptstrFilename 188 | Handle, // DWORD dwHandle 189 | VerInfoSize, // DWORD dwLen 190 | pBuffer) then // LPVOID lpData 191 | begin 192 | if VerQueryValueW(pBuffer, // LPCVOID pBlock 193 | '\', // LPCWSTR lpSubBlock 194 | pFileInfo, // LPVOID *lplpBuffer 195 | Len) then // PUINT puLen 196 | begin 197 | with pFileInfo^ do 198 | begin 199 | result := IntToStr(HiWord(dwFileVersionMS)) + '.' + 200 | IntToStr(LoWord(dwFileVersionMS)) + '.' + 201 | IntToStr(HiWord(dwFileVersionLS)) + '.' + 202 | IntToStr(LoWord(dwFileVersionLS)); 203 | end; 204 | end; 205 | end; 206 | FreeMem(pBuffer); 207 | end; 208 | ToggleWow64FsRedirection(); 209 | end; 210 | 211 | function JoinPath(Path1, Path2: string): string; 212 | begin 213 | if (Length(Path1) > 0) and (Length(Path2) > 0) then 214 | begin 215 | while Path1[Length(Path1)] = '\' do 216 | Path1 := Copy(Path1, 1, Length(Path1) - 1); 217 | while Path2[1] = '\' do 218 | Path2 := Copy(Path2, 2, Length(Path2) - 1); 219 | result := Path1 + '\' + Path2; 220 | end 221 | else 222 | result := ''; 223 | end; 224 | 225 | procedure InitializeUnit(); 226 | begin 227 | PerformWow64FsRedirection := IsProcessWoW64(); 228 | Wow64FsRedirectionOldValue := nil; 229 | end; 230 | 231 | initialization 232 | InitializeUnit(); 233 | 234 | end. 235 | -------------------------------------------------------------------------------- /wsUtilReg.pp: -------------------------------------------------------------------------------- 1 | { Copyright (C) 2020-2021 by Bill Stewart (bstewart at iname.com) 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU Lesser General Public License as published by the Free 5 | Software Foundation; either version 3 of the License, or (at your option) any 6 | later version. 7 | 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Lesser Public License for more 11 | details. 12 | 13 | You should have received a copy of the GNU Lesser General Public License 14 | along with this program. If not, see https://www.gnu.org/licenses/. 15 | 16 | } 17 | 18 | {$MODE OBJFPC} 19 | {$MODESWITCH UNICODESTRINGS} 20 | 21 | unit wsUtilReg; 22 | 23 | interface 24 | 25 | uses 26 | Windows; 27 | 28 | type 29 | TStringArray = array of string; 30 | 31 | const 32 | HKEY_LOCAL_MACHINE_64 = $80000102; 33 | HKEY_LOCAL_MACHINE_32 = $80000202; 34 | 35 | // NOTE: The RootKey parameter in the below functions can also be 36 | // HKEY_LOCAL_MACHINE_64 (works from 32-bit process) or HKEY_LOCAL_MACHINE_32 37 | 38 | // Gets registry subkey names into Names array; returns true for success 39 | // or false for failure 40 | function RegGetSubKeyNames(RootKey: HKEY; const SubKeyName: string; 41 | var Names: TStringArray): Boolean; 42 | 43 | // Returns true if the specified registry subkey exists or false otherwise 44 | function RegKeyExists(RootKey: HKEY; const SubKeyName: string): Boolean; 45 | 46 | // Returns the ValueName value from the specified key and subkey into 47 | // ResultStr; returns true for success or false for failure 48 | function RegQueryStringValue(RootKey: HKEY; const SubKeyName, ValueName: string; 49 | var ResultStr: string): Boolean; 50 | 51 | implementation 52 | 53 | // Updates RootKey and AccessFlags appropriately if using _32 or _64 RootKey 54 | procedure UpdateRootKeyAndFlags(var RootKey: HKEY; var AccessFlags: REGSAM); 55 | begin 56 | if (RootKey and KEY_WOW64_32KEY) <> 0 then 57 | begin 58 | RootKey := RootKey and (not KEY_WOW64_32KEY); 59 | AccessFlags := AccessFlags or KEY_WOW64_32KEY; 60 | end 61 | else if (RootKey and KEY_WOW64_64KEY) <> 0 then 62 | begin 63 | RootKey := RootKey and (not KEY_WOW64_64KEY); 64 | AccessFlags := AccessFlags or KEY_WOW64_64KEY; 65 | end; 66 | end; 67 | 68 | function RegGetSubKeyNames(RootKey: HKEY; const SubKeyName: string; 69 | var Names: TStringArray): Boolean; 70 | var 71 | AccessFlags: REGSAM; 72 | hkHandle: HANDLE; 73 | NumSubKeys: DWORD; 74 | MaxSubKeyNameLen: DWORD; 75 | NumValues: DWORD; 76 | MaxValueNameLen: DWORD; 77 | MaxValueLen: DWORD; 78 | Name: PChar; 79 | I: DWORD; 80 | MaxSubKeyNameLen2: DWORD; 81 | begin 82 | AccessFlags := KEY_READ; 83 | UpdateRootKeyAndFlags(RootKey, AccessFlags); 84 | result := RegOpenKeyExW(RootKey, // HKEY hKey 85 | PChar(SubKeyName), // LPCSTR lpSubKey 86 | 0, // DWORD ulOptions 87 | AccessFlags, // REGSAM samdesired 88 | hkHandle) = 0; // PHKEY phkResult 89 | if result then 90 | begin 91 | result := RegQueryInfoKeyW(hkHandle, // HKEY hKey 92 | nil, // LPSTR lpClass 93 | nil, // LPDWORD lpcchClass 94 | nil, // LPDWORD lpReserved 95 | @NumSubKeys, // LPDWORD lpcSubKeys 96 | @MaxSubKeyNameLen, // LPDWORD lpcbMaxSubKeyLen 97 | nil, // LPDWORD lpcbMaxClassLen 98 | @NumValues, // LPDWORD lpcValues 99 | @MaxValueNameLen, // LPDWORD lpcbMaxValueNameLen 100 | @MaxValueLen, // LPDWORD lpcbMaxValueLen 101 | nil, // LPDWORD lpcbSecurityDescriptor 102 | nil) = 0; // PFILETIME lpftLastWriteTime 103 | // Set dynamic array size 104 | SetLength(Names, NumSubKeys); 105 | if NumSubKeys > 0 then 106 | begin 107 | // lpcbMaxValueNameLen doesn't include terminating null 108 | MaxSubKeyNameLen := (MaxSubKeyNameLen + 1) * SizeOf(Char); 109 | // Each call to RegEnumKeyEx will use this buffer 110 | GetMem(Name, MaxSubKeyNameLen); 111 | // Enumerate subkey names 112 | for I := 0 to NumSubKeys - 1 do 113 | begin 114 | // Reset max buffer size on each call because RegEnumKeyExW updates it 115 | MaxSubKeyNameLen2 := MaxSubKeyNameLen; 116 | if RegEnumKeyExW(hkHandle, // HKEY hKey 117 | I, // DWORD dwIndex 118 | Name, // LPSTR lpName 119 | @MaxSubKeyNameLen2, // LPDWORD lpcchName 120 | nil, // LPDWORD lpReserved 121 | nil, // LPSTR lpClass 122 | nil, // LPDWORD lpcchClass 123 | nil) = 0 then // PFILETIME lpftLastWriteTime 124 | Names[I] := Name 125 | else 126 | Names[I] := ''; 127 | end; 128 | FreeMem(Name); 129 | end; 130 | RegCloseKey(hkHandle); 131 | end; 132 | end; 133 | 134 | function RegKeyExists(RootKey: HKEY; const SubKeyName: string): Boolean; 135 | var 136 | AccessFlags: REGSAM; 137 | hkHandle: HANDLE; 138 | begin 139 | AccessFlags := KEY_READ; 140 | UpdateRootKeyAndFlags(RootKey, AccessFlags); 141 | result := RegOpenKeyExW(RootKey, // HKEY hKey 142 | PChar(SubKeyName), // LPCSTR lpSubKey 143 | 0, // DWORD ulOptions 144 | AccessFlags, // REGSAM samDesired 145 | hkHandle) = 0; // PHKEY phkResult 146 | if result then 147 | RegCloseKey(hkHandle); 148 | end; 149 | 150 | function RegQueryStringValue(RootKey: HKEY; const SubKeyName, ValueName: string; 151 | var ResultStr: string): Boolean; 152 | var 153 | AccessFlags: REGSAM; 154 | hkHandle: HKEY; 155 | ValueType, ValueSize, BufSize: DWORD; 156 | pData, pBuf: Pointer; 157 | begin 158 | AccessFlags := KEY_READ; 159 | UpdateRootKeyAndFlags(RootKey, AccessFlags); 160 | result := RegOpenKeyExW(RootKey, // HKEY hKey 161 | PChar(SubKeyName), // LPCSTR lpSubKey 162 | 0, // DWORD ulOptions 163 | AccessFlags, // REGSAM samDesired 164 | hkHandle) = 0; // PHKEY phkResult 165 | if result then 166 | begin 167 | // First call: Get value size 168 | result := RegQueryValueExW(hkHandle, // HKEY hKey 169 | PChar(ValueName), // LPCSTR lpValueName 170 | nil, // LPDWORD lpReserved 171 | @ValueType, // LPDWORD lpType 172 | nil, // LPBYTE lpData 173 | @ValueSize) = 0; // LPDWORD lpcbData 174 | if result then 175 | begin 176 | // Must be REG_SZ or REG_EXPAND_SZ 177 | if (ValueType = REG_SZ) or (ValueType = REG_EXPAND_SZ) then 178 | begin 179 | GetMem(pData, ValueSize); 180 | // Second call: Get value data 181 | result := RegQueryValueExW(hkHandle, // HKEY hKey 182 | PChar(ValueName), // LPCSTR lpValueName 183 | nil, // LPDWORD lpReserved 184 | @ValueType, // LPDWORD lpType 185 | pData, // LPBYTE lpData 186 | @ValueSize) = 0; // LPDWORD lpcbData 187 | if result then 188 | // Last char is null 189 | if PChar(pData)[(ValueSize div SizeOf(Char)) - 1] = #0 then 190 | ResultStr := PChar(pData) 191 | else 192 | begin 193 | // Last char not null: Return as null-terminated string 194 | BufSize := ValueSize + SizeOf(Char); 195 | GetMem(pBuf, BufSize); 196 | FillChar(pBuf^, BufSize, 0); 197 | Move(pData^, pBuf^, ValueSize); 198 | ResultStr := PChar(pBuf); 199 | FreeMem(pBuf); 200 | end; 201 | FreeMem(pData); 202 | end 203 | else 204 | result := false; 205 | end; 206 | RegCloseKey(hkHandle); 207 | end; 208 | end; 209 | 210 | begin 211 | end. 212 | -------------------------------------------------------------------------------- /JavaInfoTest.iss: -------------------------------------------------------------------------------- 1 | ; Copyright (C) 2020-2023 by Bill Stewart (bstewart at iname.com) 2 | ; 3 | ; This program is free software; you can redistribute it and/or modify it under 4 | ; the terms of the GNU Lesser General Public License as published by the Free 5 | ; Software Foundation; either version 3 of the License, or (at your option) any 6 | ; later version. 7 | ; 8 | ; This program is distributed in the hope that it will be useful, but WITHOUT 9 | ; ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | ; FOR A PARTICULAR PURPOSE. See the GNU General Lesser Public License for more 11 | ; details. 12 | ; 13 | ; You should have received a copy of the GNU Lesser General Public License 14 | ; along with this program. If not, see https://www.gnu.org/licenses/. 15 | 16 | ; JavaInfoTest.iss 17 | ; Sample Inno Setup (https://www.jrsoftware.org/isinfo.php) script 18 | ; demonstrating use of JavaInfo.dll; doesn't install anything 19 | 20 | ; Only works with Unicode versions of IS 5.6 or later, or IS 6.x or later (6.x 21 | ; and later versions are all Unicode) 22 | #ifndef UNICODE 23 | #error This script requires Unicode Inno Setup 24 | #endif 25 | 26 | [Setup] 27 | AppName=JavaInfoTest 28 | AppVersion=1.6.0.0 29 | UsePreviousAppDir=false 30 | DefaultDirName={autopf}\JavaInfoTest 31 | Uninstallable=false 32 | OutputDir=. 33 | OutputBaseFilename=JavaInfoTest 34 | PrivilegesRequired=none 35 | 36 | [Files] 37 | Source: "i386\JavaInfo.dll"; Flags: dontcopy 38 | 39 | [Messages] 40 | ButtonNext=&Test 41 | ButtonCancel=&Close 42 | SetupAppTitle=JavaInfo 43 | SetupWindowTitle=JavaInfo.dll Test 44 | 45 | [Code] 46 | 47 | var 48 | InputPage: TInputQueryWizardPage; 49 | 50 | // Import functions from DLL - named with 'DLL' prefix so we can write our own 51 | // 'wrapper' functions for ease of use 52 | function DLLIsBinary64Bit(FileName: string; var Is64Bit: DWORD): DWORD; 53 | external 'IsBinary64Bit@files:JavaInfo.dll stdcall setuponly'; 54 | function DLLIsJavaInstalled(): DWORD; 55 | external 'IsJavaInstalled@files:JavaInfo.dll stdcall setuponly'; 56 | function DLLIsJavaMinimumVersion(Version: string; var VersionOK: DWORD): DWORD; 57 | external 'IsJavaMinimumVersion@files:JavaInfo.dll stdcall setuponly'; 58 | function DLLGetJavaHome(PathName: string; NumChars: DWORD): DWORD; 59 | external 'GetJavaHome@files:JavaInfo.dll stdcall setuponly'; 60 | function DLLGetJavaJVMPath(PathName: string; NumChars: DWORD): DWORD; 61 | external 'GetJavaJVMPath@files:JavaInfo.dll stdcall setuponly'; 62 | function DLLGetJavaVersion(Version: string; NumChars: DWORD): DWORD; 63 | external 'GetJavaVersion@files:JavaInfo.dll stdcall setuponly'; 64 | // Note that 'var' parameters above are pointers 65 | 66 | // Wrapper for IsBinary64Bit() DLL function 67 | function IsBinary64Bit(const FileName: string): Boolean; 68 | var 69 | Is64Bit: DWORD; 70 | begin 71 | result := false; 72 | if DLLIsBinary64Bit(FileName, Is64Bit) = 0 then 73 | result := Is64Bit = 1; 74 | end; 75 | 76 | // Wrapper for IsJavaInstalled() DLL function 77 | function IsJavaInstalled(): Boolean; 78 | begin 79 | result := DLLIsJavaInstalled() = 1; 80 | end; 81 | 82 | // Wrapper for IsJavaMinimumVersion() DLL function 83 | function IsJavaMinimumVersion(const Version: string): string; 84 | var 85 | VersionOK: DWORD; 86 | begin 87 | result := ''; 88 | if DLLIsJavaMinimumVersion(Version, VersionOK) = 0 then 89 | if VersionOK = 1 then 90 | result := 'Yes' 91 | else 92 | result := 'No'; 93 | end; 94 | 95 | // Wrapper for GetJavaHome() DLL function; note that we call the DLL function 96 | // twice: The first call gets the required number of characters, and the second 97 | // call gets the output string from the DLL 98 | function GetJavaHome(): string; 99 | var 100 | NumChars: DWORD; 101 | OutStr: string; 102 | begin 103 | result := ''; 104 | NumChars := DLLGetJavaHome('', 0); 105 | SetLength(OutStr, NumChars); 106 | if DLLGetJavaHome(OutStr, NumChars) > 0 then 107 | result := OutStr; 108 | end; 109 | 110 | // Wrapper for GetJavaJVMPath() DLL function (same note as above) 111 | function GetJavaJVMPath(): string; 112 | var 113 | NumChars: DWORD; 114 | OutStr: string; 115 | begin 116 | result := ''; 117 | NumChars := DLLGetJavaJVMPath('', 0); 118 | SetLength(OutStr, NumChars); 119 | if DLLGetJavaJVMPath(OutStr, NumChars) > 0 then 120 | result := OutStr; 121 | end; 122 | 123 | // Wrapper for GetJavaVersion() DLL function (same note as above) 124 | function GetJavaVersion(): string; 125 | var 126 | NumChars: DWORD; 127 | OutStr: string; 128 | begin 129 | result := ''; 130 | NumChars := DLLGetJavaVersion('', 0); 131 | SetLength(OutStr, NumChars); 132 | if DLLGetJavaVersion(OutStr, NumChars) > 0 then 133 | result := OutStr; 134 | end; 135 | 136 | function BoolToStr(const B: Boolean): string; 137 | begin 138 | if B then 139 | result := 'Yes' 140 | else 141 | result := 'No'; 142 | end; 143 | 144 | // Show content of a string array in a message box 145 | procedure ShowStringArray(var A: TArrayOfString); 146 | var 147 | Msg: string; 148 | I: LongInt; 149 | begin 150 | if GetArrayLength(A) = 0 then 151 | MsgBox('Array length is 0', mbInformation, MB_OK) 152 | else 153 | begin 154 | Msg := '[0] ' + A[0]; 155 | for I := 1 to GetArrayLength(A) - 1 do 156 | Msg := Msg + #10 + '[' + IntToStr(I) + '] ' + A[I]; 157 | MsgBox(Msg, mbInformation, MB_OK); 158 | end; 159 | end; 160 | 161 | function InitializeSetup(): Boolean; 162 | begin 163 | result := IsJavaInstalled(); 164 | // Exits the wizard if Java is not detected 165 | if not result then 166 | MsgBox('JavaInfo.dll did not detect a Java installation.', mbInformation, MB_OK); 167 | end; 168 | 169 | procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean); 170 | begin 171 | // Don't show the confirmation prompt when exiting 172 | Confirm := false; 173 | end; 174 | 175 | procedure InitializeWizard(); 176 | begin 177 | // Create input query page and text field for entering the Java version 178 | InputPage := CreateInputQueryPage(wpWelcome, 179 | 'Minimum Java Version', 180 | 'What minimum version of Java to you want to check for?', 181 | 'Specify the minimum version of Java to check for, then click Test.'); 182 | InputPage.Add('&Minimum Java version:', false); 183 | end; 184 | 185 | // Splits S into the array Dest using Delim as the delimiter 186 | procedure StrSplit(S, Delim: string; var Dest: TArrayOfString); 187 | var 188 | Temp: string; 189 | I, P: Integer; 190 | begin 191 | Temp := S; 192 | I := StringChangeEx(Temp, Delim, '', true); 193 | SetArrayLength(Dest, I + 1); 194 | for I := 0 to GetArrayLength(Dest) - 1 do 195 | begin 196 | P := Pos(Delim, S); 197 | if P > 0 then 198 | begin 199 | Dest[I] := Copy(S, 1, P - 1); 200 | Delete(S, 1, P + Length(Delim) - 1); 201 | end 202 | else 203 | Dest[I] := S; 204 | end; 205 | end; 206 | 207 | // Returns a packed version number from an input string 208 | function StrToPackedVersion(const Version: string): Int64; 209 | var 210 | Parts: TArrayOfString; 211 | PartCount, I, Part: LongInt; 212 | begin 213 | result := 0; 214 | StrSplit(Version, '.', Parts); 215 | PartCount := GetArrayLength(Parts); 216 | if Parts[0] = '' then 217 | exit; 218 | if PartCount < 4 then 219 | begin 220 | SetArrayLength(Parts, 4); 221 | // Missing parts are '0' 222 | for I := PartCount to 3 do 223 | Parts[I] := '0'; 224 | end; 225 | // Return 0 if any part not numeric or out of range 226 | for I := 0 to 3 do 227 | begin 228 | Part := StrToIntDef(Parts[I], -1); 229 | if (Part < 0) or (Part > 65535) then 230 | exit; 231 | end; 232 | result := PackVersionComponents(StrToIntDef(Parts[0], 0), 233 | StrToIntDef(Parts[1], 0), 234 | StrToIntDef(Parts[2], 0), 235 | StrToIntDef(Parts[3], 0)); 236 | end; 237 | 238 | function NextButtonClick(CurPageID: Integer): Boolean; 239 | var 240 | MinVersion, JavaHome, JavaJVMPath, Message: string; 241 | begin 242 | result := true; 243 | // Check if current page is the custom Java version page 244 | if CurPageID = InputPage.ID then 245 | begin 246 | // Disable 'Next' button (relabeled as 'Test') 247 | result := false; 248 | MinVersion := Trim(InputPage.Values[0]); 249 | if StrToPackedVersion(MinVersion) = 0 then 250 | begin 251 | MsgBox('Please specify a minimum Java version number.', mbError, MB_OK); 252 | WizardForm.ActiveControl := InputPage.Edits[0]; 253 | InputPage.Edits[0].SelectAll(); 254 | end 255 | else 256 | begin 257 | JavaHome := GetJavaHome(); 258 | Message := 'Java home: ' + JavaHome + #10 259 | + 'Java JVM path: ' + GetJavaJVMPath + #10 260 | + 'Java version: ' + GetJavaVersion() + #10 261 | + 'Java is 64-bit: ' + BoolToStr(IsBinary64Bit(JavaHome + '\bin\java.exe')) + #10 262 | + 'At least version ' + MinVersion + ': ' + IsJavaMinimumVersion(MinVersion); 263 | MsgBox(Message, mbInformation, MB_OK); 264 | end; 265 | end; 266 | end; 267 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # JavaInfo.dll 3 | 4 | JavaInfo.dll is a Windows DLL (dynamically linked library) that provides information about Java installations. 5 | 6 | - [Author](#author) 7 | - [License](#license) 8 | - [Download](#download) 9 | - [Background](#background) 10 | - [Registry Searches](#registry-searches) 11 | - [Functions](#functions) 12 | - [IsBinary64Bit](#isbinary64bit) 13 | - [Syntax](#syntax) 14 | - [Parameters](#parameters) 15 | - [Return Value](#return-value) 16 | - [IsJavaInstalled](#isjavainstalled) 17 | - [Syntax](#syntax-1) 18 | - [Return Value](#return-value-1) 19 | - [IsJavaMinimumVersion](#isjavaminimumversion) 20 | - [Syntax](#syntax-2) 21 | - [Parameters](#parameters-1) 22 | - [Return Value](#return-value-2) 23 | - [GetJavaHome](#getjavahome) 24 | - [Syntax](#syntax-3) 25 | - [Parameters](#parameters-2) 26 | - [Return Value](#return-value-3) 27 | - [GetJavaJVMPath](#getjavajvmpath) 28 | - [Syntax](#syntax-4) 29 | - [Parameters](#parameters-3) 30 | - [Return Value](#return-value-4) 31 | - [GetJavaVersion](#getjavaversion) 32 | - [Syntax](#syntax-5) 33 | - [Parameters](#parameters-4) 34 | - [Return Value](#return-value-5) 35 | 36 | ## Author 37 | 38 | Bill Stewart - bstewart at iname dot com 39 | 40 | ## License 41 | 42 | JavaInfo.dll is covered by the GNU Lesser Public License (LPGL). See the file `LICENSE` for details. 43 | 44 | ## Download 45 | 46 | https://github.com/Bill-Stewart/JavaInfo/releases/ 47 | 48 | ## Background 49 | 50 | A Java Development Kit (JDK) or Java Runtime Environment (JRE) is required to run Java applications, but there's not a standard way to detect whether Java is installed and details about it. JavaInfo.dll provides this information. For example, you can use JavaInfo.dll in an installer or a Java application launcher executable to detect if Java is installed. 51 | 52 | JavaInfo.dll searches for Java in the following ways: 53 | 54 | 1. It checks for the presence of the `JAVA_HOME`, `JDK_HOME`, and `JRE_HOME` environment variables (in that order). The value of the environment variable is the Java home directory. 55 | 56 | 2. If the environment variables noted above are not defined, JavaInfo.dll searches the directories named in the `Path` environment variable for `java.exe`. The home directory is the parent directory of the directory where `java.exe` is found. For example, if `C:\Program Files\Eclipse Adoptium\JRE11\bin` is in the path (and `java.exe` is in that directory), the Java home directory is `C:\Program Files\Eclipse Adoptium\JRE11`. 57 | 58 | 3. If `java.exe` is not found in the `Path`, JavaInfo.dll searches in the registry for the home directory of the latest Java version installed. (See [Registry Searches](#registry-searches), below, for details on the registry searches.) 59 | 60 | > NOTE: On 64-bit platforms, JavaInfo.dll does not search the registry for 32-bit versions of Java if it finds any 64-bit versions in the registry, even if there is a newer 32-bit version installed. This only applies to the registry searches; if one of the environment variables points to a 32-bit Java installation, or if JavaInfo.dll finds a 32-bit copy of `java.exe` in the `Path`, JavaInfo.dll doesn't search the registry. 61 | 62 | If JavaInfo.dll succeeds in finding the Java home directory using any of the above techniques, it then looks for the file _javahome_`\bin\java.exe` (where _javahome_ is the Java home directory). If the file exists, it retrieves the file's version information. If the file exists and JavaInfo.dll is successful at retrieving the file's version information, then it considers Java to be installed. 63 | 64 | If JavaInfo.dll finds a Java installation, you can use the following paths to find Java binaries (where _javahome_ is the Java home directory): 65 | 66 | * _javahome_`\bin\java.exe` - Console-based Java executable 67 | * _javahome_`\bin\javaw.exe` - GUI-based Java executable 68 | 69 | The 32-bit (x86) DLL works on both 32-bit and 64-bit versions of Windows. Use the x64 DLL with x64 executables on x64 versions of Windows. 70 | 71 | > NOTE: When you use the the 32-bit DLL on 64-bit Windows, it correctly handles 32-bit registry and file system redirection. (That is, the 32-bit DLL can correctly detect 64-bit Java installations and return the correct path.) 72 | 73 | ## Registry Searches 74 | 75 | JavaInfo.dll searches in the following registry locations for the location of the Java home directory: 76 | 77 | `HKEY_LOCAL_MACHINE\SOFTWARE\JavaSoft` 78 | `HKEY_LOCAL_MACHINE\SOFTWARE\IBM` 79 | `HKEY_LOCAL_MACHINE\SOFTWARE\AdoptOpenJDK` 80 | `HKEY_LOCAL_MACHINE\SOFTWARE\Eclipse Adoptium` 81 | `HKEY_LOCAL_MACHINE\SOFTWARE\Eclipse Foundation` 82 | `HKEY_LOCAL_MACHINE\SOFTWARE\Semeru` 83 | `HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\JDK` 84 | `HKEY_LOCAL_MACHINE\SOFTWARE\Azul Systems\Zulu` 85 | 86 | If other versions of Java are available that JavaInfo.dll does not detect in the registry, please contact the author so the registry detection can be improved. 87 | 88 | ## Functions 89 | 90 | This section documents the functions exported by JavaInfo.dll. 91 | 92 | --- 93 | 94 | ### IsBinary64Bit 95 | 96 | The `IsBinary64Bit` function detects whether a Windows binary file is 32-bit or 64-bit. 97 | 98 | #### Syntax 99 | 100 | C/C++: 101 | ``` 102 | DWORD IsBinary64Bit(LPWSTR FileName, PDWORD Is64Bit); 103 | ``` 104 | 105 | Pascal: 106 | ``` 107 | function IsBinary64Bit(FileName: PWideChar; Is64Bit: PDWORD): DWORD; 108 | ``` 109 | 110 | #### Parameters 111 | 112 | `FileName` 113 | 114 | A Unicode string containing the name of a binary file (exe or dll). 115 | 116 | `Is64Bit` 117 | 118 | A pointer to a variable that gets set to 1 if the specified binary file is 64-bit, or 0 otherwise. The value of this variable is not defined if the `IsBinary64Bit` function fails. 119 | 120 | #### Return Value 121 | 122 | The `IsBinary64Bit` function returns 0 for success, or non-zero for failure. 123 | 124 | --- 125 | 126 | ### IsJavaInstalled 127 | 128 | The `IsJavaInstalled` function detects whether Java is installed. 129 | 130 | #### Syntax 131 | 132 | C/C++: 133 | ``` 134 | DWORD IsJavaInstalled; 135 | ``` 136 | 137 | Pascal: 138 | ``` 139 | function IsJavaInstalled: DWORD; 140 | ``` 141 | 142 | #### Return Value 143 | 144 | The `IsJavaInstalled` function returns zero if no Java installations were detected, or non-zero otherwise. 145 | 146 | --- 147 | 148 | ### IsJavaMinimumVersion 149 | 150 | The `IsJavaMinimumVersion` function checks whether the installed Java version is at least a specified version. 151 | 152 | #### Syntax 153 | 154 | C/C++: 155 | ``` 156 | DWORD IsJavaMinimumVersion(LPWSTR Version, PDWORD VersionOK); 157 | ``` 158 | 159 | Pascal: 160 | ``` 161 | function IsJavaMinimumVersion(Version: PWideChar; VersionOK: PDWORD): DWORD; 162 | ``` 163 | 164 | #### Parameters 165 | 166 | `Version` 167 | 168 | A Unicode string containing a version number. The string can contain from 1 to 4 numbers in the range 0 through 65535 separated by `.` characters. 169 | 170 | `VersionOK` 171 | 172 | A pointer to a variable that gets set to 1 if the installed Java version is at least the version specified in the `Version` parameter, or 0 otherwise. The value of this variable is not defined if the `IsJavaMinimumVersion` function fails. 173 | 174 | #### Return Value 175 | 176 | The `IsJavaMinimumVersion` function returns 0 for success, or non-zero for failure. If the version specified in the `Version` parameter is not a valid version number string, the function will return error code 87 (`ERROR_INVALID_PARAMETER`). 177 | 178 | --- 179 | 180 | ### GetJavaHome 181 | 182 | The `GetJavaHome` function gets the Java home directory. 183 | 184 | #### Syntax 185 | 186 | C/C++: 187 | ``` 188 | DWORD GetJavaHome(LPWSTR PathName, DWORD NumChars); 189 | ```` 190 | 191 | Pascal: 192 | ``` 193 | function GetJavaHome(PathName: PWideChar; NumChars: DWORD): DWORD; 194 | ``` 195 | 196 | #### Parameters 197 | 198 | `PathName` 199 | 200 | A pointer to a variable that receives a Unicode string that contains the Java home directory. 201 | 202 | `NumChars` 203 | 204 | Specifies the number of characters needed to store the home directory string, not including the terminating null character. To get the required number of characters needed, call the function twice. In the first call to the function, specify a null pointer for the `PathName` parameter and `0` for the `NumChars` parameter. The function will return with the number of characters required for the buffer. Allocate a buffer of sufficient size (don't forget to include the terminating null character), then call the function a second time to retrieve the string. 205 | 206 | #### Return Value 207 | 208 | The `GetJavaHome` function returns zero if it failed, or non-zero if it succeeded. 209 | 210 | --- 211 | 212 | ### GetJavaJVMPath 213 | 214 | The `GetJavaJVMPath` function gets the path and filename of jvm.dll. 215 | 216 | #### Syntax 217 | 218 | C/C++: 219 | ``` 220 | DWORD GetJavaJVMPath(LPWSTR PathName, DWORD NumChars); 221 | ```` 222 | 223 | Pascal: 224 | ``` 225 | function GetJavaJVMPath(PathName: PWideChar; NumChars: DWORD): DWORD; 226 | ``` 227 | 228 | #### Parameters 229 | 230 | `PathName` 231 | 232 | A pointer to a variable that receives a Unicode string that contains the path and filename of jvm.dll. 233 | 234 | `NumChars` 235 | 236 | Specifies the number of characters needed to store the path string, not including the terminating null character. To get the required number of characters needed, call the function twice. In the first call to the function, specify a null pointer for the `PathName` parameter and `0` for the `NumChars` parameter. The function will return with the number of characters required for the buffer. Allocate a buffer of sufficient size (don't forget to include the terminating null character), then call the function a second time to retrieve the string. 237 | 238 | #### Return Value 239 | 240 | The `GetJavaJVMPath` function returns zero if it failed, or non-zero if it succeeded. 241 | 242 | --- 243 | 244 | ### GetJavaVersion 245 | 246 | The `GetJavaVersion` function gets the version of Java as a string in the following format: _n_`.`_n_`.`_n_`.`_n_ (where _n_ is a value between 0 and 65535, inclusive). 247 | 248 | #### Syntax 249 | 250 | C/C++: 251 | ``` 252 | DWORD GetJavaVersion(LPWSTR Version, DWORD NumChars); 253 | ```` 254 | 255 | Pascal: 256 | ``` 257 | function GetJavaVersion(Version: PWideChar; NumChars: DWORD): DWORD; 258 | ``` 259 | 260 | #### Parameters 261 | 262 | `Version` 263 | 264 | A pointer to a variable that receives a Unicode string that contains the Java version string. 265 | 266 | `NumChars` 267 | 268 | Specifies the number of characters needed to store the version number string, not including the terminating null character. To get the required number of characters needed, call the function twice. In the first call to the function, specify a null pointer for the `Version` parameter and `0` for the `NumChars` parameter. The function will return with the number of characters required for the buffer. Allocate a buffer of sufficient size (don't forget to include the terminating null character), then call the function a second time to retrieve the string. 269 | 270 | #### Return Value 271 | 272 | The `GetJavaVersion` function returns zero if it failed, or non-zero if it succeeded. 273 | -------------------------------------------------------------------------------- /GetJavaInfo.pp: -------------------------------------------------------------------------------- 1 | { Copyright (C) 2020-2023 by Bill Stewart (bstewart at iname.com) 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU Lesser General Public License as published by the Free 5 | Software Foundation; either version 3 of the License, or (at your option) any 6 | later version. 7 | 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Lesser Public License for more 11 | details. 12 | 13 | You should have received a copy of the GNU Lesser General Public License 14 | along with this program. If not, see https://www.gnu.org/licenses/. 15 | 16 | } 17 | 18 | {$MODE OBJFPC} 19 | {$MODESWITCH UNICODESTRINGS} 20 | {$R *.res} 21 | {$APPTYPE CONSOLE} 22 | 23 | program GetJavaInfo; 24 | 25 | // wargcv/wgetopts - https://github.com/Bill-Stewart/wargcv 26 | uses 27 | Windows, 28 | wargcv, 29 | wgetopts, 30 | wsJavaInfo, 31 | wsUtilFile, 32 | wsUtilStr; 33 | 34 | type 35 | TCommandLine = object 36 | ErrorCode: Word; 37 | ErrorMessage: string; 38 | ArgHelp: Boolean; // --help/-h 39 | ArgQuiet: Boolean; // --quiet/-q 40 | ArgVersion: Boolean; // --version/v 41 | ArgJavaIs64Bit: Boolean; // --javais64bit/-b 42 | ArgJavaHome: Boolean; // --javahome/-H 43 | ArgJavaDLL: Boolean; // --javadll/-d 44 | ArgJavaInstalled: Boolean; // --javainstalled/-i 45 | ArgJavaMinVersion: string; // --javaminversion/-m 46 | ArgJavaVersion: Boolean; // --javaversion/-V 47 | procedure Parse(); 48 | end; 49 | 50 | var 51 | CommandLine: TCommandLine; 52 | JavaDetected, JavaIs64Bit, VersionOK: Boolean; 53 | JavaHome, JavaJVMPath, JavaVersion, OutputStr: string; 54 | 55 | procedure Usage(); 56 | var 57 | UsageText: string; 58 | begin 59 | UsageText := 'GetJavaInfo - Copyright 2020-2023 by Bill Stewart (bstewart at iname.com)' + sLineBreak 60 | + 'This is free software and comes with ABSOLUTELY NO WARRANTY.' + sLineBreak 61 | + sLineBreak 62 | + 'Usage: GetJavaInfo [--javainstalled | --javahome | --javaversion |' + sLineBreak 63 | + ' --javais64bit] [--quiet]' + sLineBreak 64 | + 'or: GetJavaInfo --javaminversion [--quiet]' + sLineBreak 65 | + sLineBreak 66 | + 'Without parameters, outputs Java installation details.' + sLineBreak 67 | + sLineBreak 68 | + 'Parameter Abbrev. Description' + sLineBreak 69 | + '---------------- ------- -------------------------------------' + sLineBreak 70 | + '--javainstalled -i Tests if Java is installed' + sLineBreak 71 | + '--javahome -H Outputs Java home directory' + sLineBreak 72 | + '--javadll -d Outputs jvm.dll path' + sLineBreak 73 | + '--javaversion -V Outputs Java version' + sLineBreak 74 | + '--javais64bit -b Tests if Java is 64-bit' + sLineBreak 75 | + '--version -v Outputs this program''s version number' + sLineBreak 76 | + '--javaminversion -m Tests for a minimum version of Java' + sLineBreak 77 | + '--quiet -q Suppresses output from other options' + sLineBreak 78 | + '--help -h Outputs this help information' + sLineBreak 79 | + sLineBreak 80 | + 'Parameters can be spelled out (e.g., --javahome) or specified in abbreviated' + sLineBreak 81 | + 'form (e.g., -H). Parameters are case-sensitive.' + sLineBreak 82 | + sLineBreak 83 | + 'General exit codes:' + sLineBreak 84 | + '* 0 = no error/Java is installed' + sLineBreak 85 | + '* 2 = Java is not installed' + sLineBreak 86 | + '* 87 = invalid parameter on command line' + sLineBreak 87 | + sLineBreak 88 | + 'Exit codes with --javais64bit (-b) parameter:' + sLineBreak 89 | + '* 0 = Java is not 64-bit' + sLineBreak 90 | + '* 1 = Java is 64-bit' + sLineBreak 91 | + sLineBreak 92 | + 'Exit codes with --javaminversion (-m) parameter:' + sLineBreak 93 | + '* 0 = Java version is < specified version' + sLineBreak 94 | + '* 1 = Java version is >= specified version' + sLineBreak 95 | + sLineBreak 96 | + 'Version number for --javaminversion (-m) parameter uses the following format:' + sLineBreak 97 | + 'n[.n[.n[.n]]]' + sLineBreak 98 | + '(i.e., up to 4 numbers separated by dots)'; 99 | WriteLn(UsageText); 100 | end; 101 | 102 | procedure TCommandLine.Parse(); 103 | var 104 | LongOpts: array[1..10] of TOption; 105 | Opt: Char; 106 | I: Integer; 107 | begin 108 | // Set up array of options; requires final option with empty name; 109 | // set Value member to specify short option match for GetLongOps 110 | with LongOpts[1] do 111 | begin 112 | Name := 'javais64bit'; 113 | Has_arg := No_Argument; 114 | Flag := nil; 115 | Value := 'b'; 116 | end; 117 | with LongOpts[2] do 118 | begin 119 | Name := 'javadll'; 120 | Has_arg := No_Argument; 121 | flag := nil; 122 | Value := 'd'; 123 | end; 124 | with LongOpts[3] do 125 | begin 126 | Name := 'javahome'; 127 | Has_arg := No_Argument; 128 | Flag := nil; 129 | Value := 'H'; 130 | end; 131 | with LongOpts[4] do 132 | begin 133 | Name := 'help'; 134 | Has_arg := No_Argument; 135 | Flag := nil; 136 | Value := 'h'; 137 | end; 138 | with LongOpts[5] do 139 | begin 140 | Name := 'javainstalled'; 141 | Has_arg := No_Argument; 142 | Flag := nil; 143 | Value := 'i'; 144 | end; 145 | with LongOpts[6] do 146 | begin 147 | Name := 'javaminversion'; 148 | Has_arg := Required_Argument; 149 | Flag := nil; 150 | Value := 'm'; 151 | end; 152 | with LongOpts[7] do 153 | begin 154 | Name := 'quiet'; 155 | Has_arg := No_Argument; 156 | Flag := nil; 157 | Value := 'q'; 158 | end; 159 | with LongOpts[8] do 160 | begin 161 | Name := 'javaversion'; 162 | Has_arg := No_Argument; 163 | Flag := nil; 164 | Value := 'V'; 165 | end; 166 | with LongOpts[9] do 167 | begin 168 | Name := 'version'; 169 | Has_arg := No_Argument; 170 | Flag := nil; 171 | Value := 'v'; 172 | end; 173 | with LongOpts[10] do 174 | begin 175 | Name := ''; 176 | Has_arg := No_Argument; 177 | Flag := nil; 178 | Value := #0; 179 | end; 180 | // Initialize defaults 181 | ErrorCode := 0; 182 | ErrorMessage := ''; 183 | ArgHelp := false; // --help/-h 184 | ArgQuiet := false; // --quiet/-q 185 | ArgVersion := false; // --version/v 186 | ArgJavaIs64Bit := false; // --javais64bit/-b 187 | ArgJavaHome := false; // --javahome/-H 188 | ArgJavaDLL := false; // --javadll/-d 189 | ArgJavaInstalled := false; // --javainstalled/-i 190 | ArgJavaMinVersion := ''; // --javaminversion/-m 191 | ArgJavaVersion := false; // --javaversion/-V 192 | OptErr := false; // no error outputs from wgetopts 193 | repeat 194 | Opt := GetLongOpts('bdHhim:qVv', @LongOpts, I); 195 | case Opt of 196 | 'b': ArgJavaIs64Bit := true; 197 | 'd': ArgJavaDLL := true; 198 | 'H': ArgJavaHome := true; 199 | 'h': ArgHelp := true; 200 | 'i': ArgJavaInstalled := true; 201 | 'm': ArgJavaMinVersion := OptArg; 202 | 'q': ArgQuiet := true; 203 | 'V': ArgJavaVersion := true; 204 | 'v': ArgVersion := true; 205 | '?': 206 | begin 207 | ErrorCode := ERROR_INVALID_PARAMETER; 208 | ErrorMessage := 'Invalid parameter(s). Use --help (-h) for usage information.'; 209 | end; 210 | end; //case Opt 211 | until Opt = EndOfOptions; 212 | end; 213 | 214 | function BoolToStr(const B: Boolean): string; 215 | begin 216 | if B then 217 | result := 'Yes' 218 | else 219 | result := 'No'; 220 | end; 221 | 222 | begin 223 | // Special case - show help if first parameter is '/?' 224 | if ParamStr(1) = '/?' then 225 | begin 226 | Usage(); 227 | exit(); 228 | end; 229 | 230 | // Parse the command line using getopts library 231 | CommandLine.Parse(); 232 | 233 | // --help/-h 234 | if CommandLine.ArgHelp then 235 | begin 236 | Usage(); 237 | exit(); 238 | end; 239 | 240 | // --version/-v 241 | if CommandLine.ArgVersion then 242 | begin 243 | WriteLn(GetFileVersion(ParamStr(0))); 244 | exit(); 245 | end; 246 | 247 | // Exit code is non-zero if error on command line 248 | ExitCode := CommandLine.ErrorCode; 249 | if ExitCode <> 0 then 250 | begin 251 | WriteLn(CommandLine.ErrorMessage); 252 | exit(); 253 | end; 254 | 255 | // Initialize variables 256 | JavaDetected := wsIsJavaInstalled(); 257 | JavaHome := ''; 258 | JavaJVMPath := ''; 259 | JavaVersion := ''; 260 | JavaIs64Bit := false; 261 | 262 | // Get details if Java was detected 263 | if JavaDetected then 264 | begin 265 | JavaHome := wsGetJavaHome(); 266 | JavaJVMPath := wsGetJavaJVMPath(); 267 | JavaVersion := wsGetJavaVersion(); 268 | if wsIsBinary64Bit(JavaHome + '\bin\java.exe', JavaIs64Bit) <> 0 then 269 | JavaIs64Bit := false; 270 | end; 271 | 272 | // --javainstalled/-i 273 | if CommandLine.ArgJavaInstalled then 274 | begin 275 | if not JavaDetected then 276 | ExitCode := ERROR_FILE_NOT_FOUND; 277 | if not CommandLine.ArgQuiet then 278 | if JavaDetected then 279 | WriteLn('Java detected.') 280 | else 281 | WriteLn('Java not detected.'); 282 | exit(); 283 | end; 284 | 285 | // Exit if Java not detected 286 | if not JavaDetected then 287 | begin 288 | ExitCode := ERROR_FILE_NOT_FOUND; 289 | if not CommandLine.ArgQuiet then 290 | WriteLn('Java not detected.'); 291 | exit(); 292 | end; 293 | 294 | // --javais64bit/-b 295 | if CommandLine.ArgJavaIs64Bit then 296 | begin 297 | if JavaIs64Bit then 298 | ExitCode := 1 299 | else 300 | ExitCode := 0; 301 | if not CommandLine.ArgQuiet then 302 | if JavaIs64Bit then 303 | WriteLn('Java is 64-bit.') 304 | else 305 | WriteLn('Java is not 64-bit.'); 306 | exit(); 307 | end; 308 | 309 | // --javaminversion/-m 310 | if CommandLine.ArgJavaMinVersion <> '' then 311 | begin 312 | if wsIsJavaMinimumVersion(CommandLine.ArgJavaMinVersion, VersionOK) then 313 | begin 314 | if VersionOK then 315 | ExitCode := 1 316 | else 317 | ExitCode := 0; 318 | if not CommandLine.ArgQuiet then 319 | if VersionOK then 320 | WriteLn('Java version is greater than or equal to the specified version.') 321 | else 322 | WriteLn('Java version is less than the specified version.'); 323 | exit(); 324 | end 325 | else 326 | begin 327 | ExitCode := ERROR_INVALID_PARAMETER; 328 | if not CommandLine.ArgQuiet then 329 | WriteLn('--javaminversion (-m) parameter requires a valid version number as an argument.'); 330 | exit(); 331 | end; 332 | end; 333 | 334 | if not (CommandLine.ArgJavaHome or CommandLine.ArgJavaDLL or CommandLine.ArgJavaVersion) then 335 | begin 336 | OutputStr := 'Java home:' + #9 + JavaHome + sLineBreak 337 | + 'jvm.dll path:' + #9 + JavaJVMPath + sLineBreak 338 | + 'Java version:' + #9 + JavaVersion + sLineBreak 339 | + 'Java is 64-bit:' + #9 + BoolToStr(JavaIs64Bit); 340 | WriteLn(OutputStr); 341 | exit(); 342 | end; 343 | 344 | // --javahome/-H or --javadll/-d or --javaversion/-V 345 | if CommandLine.ArgJavaHome then 346 | WriteLn(JavaHome) 347 | else if CommandLine.ArgJavaDLL then 348 | WriteLn(JavaJVMPath) 349 | else if CommandLine.ArgJavaVersion then 350 | WriteLn(JavaVersion); 351 | end. 352 | -------------------------------------------------------------------------------- /wsJavaInfo.pp: -------------------------------------------------------------------------------- 1 | { Copyright (C) 2020-2023 by Bill Stewart (bstewart at iname.com) 2 | 3 | This program is free software; you can redistribute it and/or modify it under 4 | the terms of the GNU Lesser General Public License as published by the Free 5 | Software Foundation; either version 3 of the License, or (at your option) any 6 | later version. 7 | 8 | This program is distributed in the hope that it will be useful, but WITHOUT 9 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 10 | FOR A PARTICULAR PURPOSE. See the GNU General Lesser Public License for more 11 | details. 12 | 13 | You should have received a copy of the GNU Lesser General Public License 14 | along with this program. If not, see https://www.gnu.org/licenses/. 15 | 16 | } 17 | 18 | {$MODE OBJFPC} 19 | {$MODESWITCH UNICODESTRINGS} 20 | 21 | unit wsJavaInfo; 22 | 23 | interface 24 | 25 | type 26 | TJavaDetectionType = (JDNone, JDEnvironment, JDPath, JDJavaSoft, JDIBM, 27 | JDAdoptium, JDMicrosoft, JDZulu); 28 | 29 | // Gets whether specified binary is 64-bit or not in Is64Bit parameter; returns 30 | // 0 for success, or non-zero for failure 31 | function wsIsBinary64Bit(FileName: string; var Is64Bit: Boolean): DWORD; 32 | 33 | // If Java installation found, returns path of Java home directory 34 | function wsGetJavaHome(): string; 35 | 36 | // If Java installation found, returns path for jvm.dll 37 | function wsGetJavaJVMPath(): string; 38 | 39 | // If Java installation found, returns version number of java.exe as a string 40 | // (a.b.c.d) 41 | function wsGetJavaVersion(): string; 42 | 43 | // Returns true if a Java installation was found, or false otherwise 44 | function wsIsJavaInstalled(): Boolean; 45 | 46 | // Gets whether the installed Java version is at least the specified version 47 | // in the VersionOK parameter; returns true for success, or false otherwise 48 | function wsIsJavaMinimumVersion(Version: string; var VersionOK: Boolean): Boolean; 49 | 50 | // Diagnostic: returns Java detection type 51 | function wsGetJavaDetectionType(): TJavaDetectionType; 52 | 53 | implementation 54 | 55 | uses 56 | Windows, 57 | wsUtilArch, 58 | wsUtilEnv, 59 | wsUtilFile, 60 | wsUtilReg, 61 | wsUtilStr, 62 | wsUtilVer; 63 | 64 | type 65 | TStringArray = array of string; 66 | 67 | var 68 | JavaHome, JavaJVMPath, JavaVersion: string; 69 | JavaDetectionType: TJavaDetectionType; 70 | 71 | function wsIsBinary64Bit(FileName: string; var Is64Bit: Boolean): DWORD; 72 | var 73 | BinaryType: Word; 74 | begin 75 | result := GetBinaryType(FileName, BinaryType); 76 | if result = 0 then 77 | Is64Bit := BinaryType <> IMAGE_FILE_MACHINE_I386; 78 | end; 79 | 80 | // Find Java home from environment variables 81 | function FindJavaHomeEnvironment(): string; 82 | var 83 | VarList: TStringArray; 84 | I: Integer; 85 | begin 86 | result := ''; 87 | SetLength(VarList, 3); 88 | VarList[0] := 'JAVA_HOME'; 89 | VarList[1] := 'JDK_HOME'; 90 | VarList[2] := 'JRE_HOME'; 91 | for I := 0 to Length(VarList) - 1 do 92 | begin 93 | result := GetEnvVar(VarList[I]); 94 | if result <> '' then 95 | break; 96 | end; 97 | end; 98 | 99 | // Find Java home by searching the Path 100 | function FindJavaHomePath(): string; 101 | begin 102 | result := FileSearch('java.exe', GetEnvVar('Path')); 103 | if result <> '' then 104 | result := ExtractFileDir(ExtractFileDir(result)); 105 | end; 106 | 107 | // Find latest Java home using JavaSoft-style registry subkeys 108 | // Subkey: SOFTWARE\\\ 109 | // String value: 'JavaHome' 110 | // Where 111 | // is usually 'JavaSoft' or 'IBM' 112 | // is JDK or JRE (or spelled out versions of those) 113 | // is the Java version string 114 | // StartingSubKey is subkey where to start search (e.g., 'SOFTWARE\JavaSoft') 115 | // If starting subkey is 'SOFTWARE\JavaSoft', this function should detect: 116 | // * Oracle JDK/JRE 117 | // * Adoptium if 'JavaSoft (Oracle) registry keys' component selected 118 | // * Amazon Corretto JDK if 'Setup Registry Keys' component selected 119 | // * Azul Systems Zulu JDK 120 | // If starting subkey is 'SOFTWARE\IBM', this function should detect IBM JDK 121 | // (hopefully? I have no way to test, as I don't have a way to get an IBM 122 | // JRE/JDK on Windows) 123 | function FindJavaHomeRegistryJavaSoft(const StartingSubKey: string): string; 124 | var 125 | LatestSubKeyName, LatestVersion, ResultStr: string; 126 | RootKey: HKEY; 127 | I, J: Integer; 128 | StartingSubKeys, SubKeyNames: TStringArray; 129 | SubKeyExists: Boolean; 130 | begin 131 | result := ''; 132 | LatestSubKeyName := ''; 133 | LatestVersion := '0'; 134 | RootKey := 0; 135 | SetLength(StartingSubKeys, 4); 136 | StartingSubKeys[0] := JoinPath(StartingSubKey, 'Java Development Kit'); 137 | StartingSubKeys[1] := JoinPath(StartingSubKey, 'JDK'); 138 | StartingSubKeys[2] := JoinPath(StartingSubKey, 'Java Runtime Environment'); 139 | StartingSubKeys[3] := JoinPath(StartingSubKey, 'JRE'); 140 | for I := 0 to Length(StartingSubKeys) - 1 do 141 | begin 142 | SubKeyExists := false; 143 | if IsWin64() then 144 | begin 145 | SubKeyExists := RegKeyExists(HKEY_LOCAL_MACHINE_64, StartingSubKeys[I]); 146 | if SubKeyExists then 147 | RootKey := HKEY_LOCAL_MACHINE_64 148 | else 149 | begin 150 | SubKeyExists := RegKeyExists(HKEY_LOCAL_MACHINE_32, StartingSubKeys[I]); 151 | if SubKeyExists then 152 | RootKey := HKEY_LOCAL_MACHINE_32; 153 | end; 154 | end 155 | else 156 | begin 157 | SubKeyExists := RegKeyExists(HKEY_LOCAL_MACHINE, StartingSubKeys[I]); 158 | if SubKeyExists then 159 | RootKey := HKEY_LOCAL_MACHINE; 160 | end; 161 | if SubKeyExists then 162 | begin 163 | if RegGetSubKeyNames(RootKey, StartingSubKeys[I], SubKeyNames) and (Length(SubKeyNames) > 0) then 164 | begin 165 | for J := 0 to Length(SubKeyNames) - 1 do 166 | begin 167 | if CompareVersionStrings(SubKeyNames[J], LatestVersion) > 0 then 168 | begin 169 | LatestSubKeyName := StartingSubKeys[I]; 170 | LatestVersion := SubKeyNames[J]; 171 | end; 172 | end; 173 | end; 174 | end; 175 | end; 176 | if (LatestVersion <> '0') and (RegQueryStringValue(RootKey, LatestSubKeyName + '\' + LatestVersion, 177 | 'JavaHome', ResultStr)) and (ResultStr <> '') then 178 | result := ResultStr; 179 | end; 180 | 181 | // Find latest Java home using Adoptium-style registry subkeys 182 | // Subkey: SOFTWARE\\\\\MSI 183 | // String value: Path 184 | // Where 185 | // is one of: 'AdoptOpenJDK', 'Eclipse Adoptium', 'Eclipse Foundation', or 'Semeru' 186 | // is JDK or JRE 187 | // is the Java version string 188 | // is usually 'hotspot' or 'openj9' 189 | function FindJavaHomeRegistryAdoptium(): string; 190 | var 191 | LatestSubKeyName, LatestVersion, ResultStr: string; 192 | RootKey: HKEY; 193 | I, J, K: Integer; 194 | StartingSubKeys, SubKeyNames, SubSubKeyNames: TStringArray; 195 | SubKeyExists: Boolean; 196 | begin 197 | result := ''; 198 | LatestSubKeyName := ''; 199 | LatestVersion := '0'; 200 | RootKey := 0; 201 | SetLength(StartingSubKeys, 8); 202 | StartingSubKeys[0] := 'SOFTWARE\AdoptOpenJDK\JDK'; 203 | StartingSubKeys[1] := 'SOFTWARE\AdoptOpenJDK\JRE'; 204 | StartingSubKeys[2] := 'SOFTWARE\Eclipse Adoptium\JDK'; 205 | StartingSubKeys[3] := 'SOFTWARE\Eclipse Adoptium\JRE'; 206 | StartingSubKeys[4] := 'SOFTWARE\Eclipse Foundation\JDK'; 207 | StartingSubKeys[5] := 'SOFTWARE\Eclipse Foundation\JRE'; 208 | StartingSubKeys[6] := 'SOFTWARE\Semeru\JDK'; 209 | StartingSubKeys[7] := 'SOFTWARE\Semeru\JRE'; 210 | for I := 0 to Length(StartingSubKeys) - 1 do 211 | begin 212 | SubKeyExists := false; 213 | if IsWin64() then 214 | begin 215 | SubKeyExists := RegKeyExists(HKEY_LOCAL_MACHINE_64, StartingSubKeys[I]); 216 | if SubKeyExists then 217 | RootKey := HKEY_LOCAL_MACHINE_64 218 | else 219 | begin 220 | SubKeyExists := RegKeyExists(HKEY_LOCAL_MACHINE_32, StartingSubKeys[I]); 221 | if SubKeyExists then 222 | RootKey := HKEY_LOCAL_MACHINE_32; 223 | end; 224 | end 225 | else 226 | begin 227 | SubKeyExists := RegKeyExists(HKEY_LOCAL_MACHINE, StartingSubKeys[I]); 228 | if SubKeyExists then 229 | RootKey := HKEY_LOCAL_MACHINE; 230 | end; 231 | if SubKeyExists then 232 | begin 233 | if RegGetSubKeyNames(RootKey, StartingSubKeys[I], SubKeyNames) and (Length(SubKeyNames) > 0) then 234 | begin 235 | for J := 0 to Length(SubKeyNames) - 1 do 236 | begin 237 | if CompareVersionStrings(SubKeyNames[J], LatestVersion) > 0 then 238 | begin 239 | if RegGetSubKeyNames(RootKey, StartingSubKeys[I] + '\' + SubKeyNames[J], SubSubKeyNames) and 240 | (Length(SubSubKeyNames) > 0) then 241 | begin 242 | for K := 0 to Length(SubSubKeyNames) - 1 do 243 | begin 244 | LatestSubKeyName := StartingSubKeys[I] + '\' + SubKeyNames[J] + '\' + SubSubKeyNames[K] + '\MSI'; 245 | LatestVersion := SubKeyNames[J]; 246 | end; 247 | end; 248 | end; 249 | end; 250 | end; 251 | end; 252 | end; 253 | if (LatestVersion <> '0') and (RegQueryStringValue(RootKey, LatestSubKeyName, 'Path', ResultStr)) and (ResultStr <> '') then 254 | result := ResultStr; 255 | end; 256 | 257 | // Find latest Java home using Microsoft registry subkeys 258 | // Subkey: SOFTWARE\Microsoft\\\\MSI 259 | // String value: Path 260 | // Where 261 | // is JDK 262 | // is the Java version string 263 | // is 'hotspot' 264 | function FindJavaHomeRegistryMicrosoft(): string; 265 | var 266 | LatestSubKeyName, LatestVersion, ResultStr: string; 267 | RootKey: HKEY; 268 | I, J, K: Integer; 269 | StartingSubKeys, SubKeyNames, SubSubKeyNames: TStringArray; 270 | SubKeyExists: Boolean; 271 | begin 272 | result := ''; 273 | LatestSubKeyName := ''; 274 | LatestVersion := '0'; 275 | RootKey := 0; 276 | SetLength(StartingSubKeys, 1); 277 | StartingSubKeys[0] := 'SOFTWARE\Microsoft\JDK'; 278 | for I := 0 to Length(StartingSubKeys) - 1 do 279 | begin 280 | SubKeyExists := false; 281 | if IsWin64() then 282 | begin 283 | SubKeyExists := RegKeyExists(HKEY_LOCAL_MACHINE_64, StartingSubKeys[I]); 284 | if SubKeyExists then 285 | RootKey := HKEY_LOCAL_MACHINE_64 286 | else 287 | begin 288 | SubKeyExists := RegKeyExists(HKEY_LOCAL_MACHINE_32, StartingSubKeys[I]); 289 | if SubKeyExists then 290 | RootKey := HKEY_LOCAL_MACHINE_32; 291 | end; 292 | end 293 | else 294 | begin 295 | SubKeyExists := RegKeyExists(HKEY_LOCAL_MACHINE, StartingSubKeys[I]); 296 | if SubKeyExists then 297 | RootKey := HKEY_LOCAL_MACHINE; 298 | end; 299 | if SubKeyExists then 300 | begin 301 | if RegGetSubKeyNames(RootKey, StartingSubKeys[I], SubKeyNames) and (Length(SubKeyNames) > 0) then 302 | begin 303 | for J := 0 to Length(SubKeyNames) - 1 do 304 | begin 305 | if CompareVersionStrings(SubKeyNames[J], LatestVersion) > 0 then 306 | begin 307 | if RegGetSubKeyNames(RootKey, StartingSubKeys[I] + '\' + SubKeyNames[J], SubSubKeyNames) and 308 | (Length(SubSubKeyNames) > 0) then 309 | begin 310 | for K := 0 to Length(SubSubKeyNames) - 1 do 311 | begin 312 | LatestSubKeyName := StartingSubKeys[I] + '\' + SubKeyNames[J] + '\' + SubSubKeyNames[K] + '\MSI'; 313 | LatestVersion := SubKeyNames[J]; 314 | end; 315 | end; 316 | end; 317 | end; 318 | end; 319 | end; 320 | end; 321 | if (LatestVersion <> '0') and (RegQueryStringValue(RootKey, LatestSubKeyName, 'Path', ResultStr)) and (ResultStr <> '') then 322 | result := ResultStr; 323 | end; 324 | 325 | // Find latest Java home using Zulu-style registry subkeys 326 | // Subkey: SOFTWARE\Azul Systems\Zulu\zulu- 327 | // String value: InstallationPath 328 | // is the Java version 329 | // (This search would only occur if the Zulu installer doesn't update the 330 | // JavaSoft registry subkeys for some reason) 331 | function FindJavaHomeRegistryZulu(): string; 332 | var 333 | LatestSubKeyName, LatestVersion, SubKeyName, CurrentVersion, ResultStr: string; 334 | RootKey: HKEY; 335 | SubKeyExists: Boolean; 336 | SubKeyNames: TStringArray; 337 | I: Integer; 338 | begin 339 | result := ''; 340 | LatestSubKeyName := ''; 341 | LatestVersion := '0'; 342 | RootKey := 0; 343 | SubKeyExists := false; 344 | SubKeyName := 'SOFTWARE\Azul Systems\Zulu'; 345 | if IsWin64() then 346 | begin 347 | SubKeyExists := RegKeyExists(HKEY_LOCAL_MACHINE_64, SubKeyName); 348 | if SubKeyExists then 349 | RootKey := HKEY_LOCAL_MACHINE_64 350 | else 351 | begin 352 | SubKeyExists := RegKeyExists(HKEY_LOCAL_MACHINE_32, SubKeyName); 353 | if SubKeyExists then 354 | RootKey := HKEY_LOCAL_MACHINE_32; 355 | end; 356 | end 357 | else 358 | begin 359 | SubKeyExists := RegKeyExists(HKEY_LOCAL_MACHINE, SubKeyName); 360 | if SubKeyExists then 361 | RootKey := HKEY_LOCAL_MACHINE; 362 | end; 363 | if SubKeyExists then 364 | begin 365 | if RegGetSubKeyNames(RootKey, SubKeyName, SubKeyNames) and (Length(SubKeyNames) > 0) then 366 | begin 367 | for I := 0 to Length(SubKeyNames) - 1 do 368 | begin 369 | if (Length(SubKeyNames[I]) > 5) and (LowerCase(Copy(SubKeyNames[I], 1, 5)) = 'zulu-') then 370 | begin 371 | if GetDigitsInString(SubKeyNames[I], CurrentVersion) then 372 | begin 373 | if CompareVersionStrings(CurrentVersion, LatestVersion) > 0 then 374 | begin 375 | LatestSubKeyName := SubKeyName + '\' + SubKeyNames[I]; 376 | LatestVersion := CurrentVersion; 377 | end; 378 | end; 379 | end; 380 | end; 381 | end; 382 | end; 383 | if (LatestVersion <> '0') and (RegQueryStringValue(RootKey, LatestSubKeyName, 'InstallationPath', ResultStr)) and 384 | (ResultStr <> '') then 385 | result := ResultStr; 386 | end; 387 | 388 | function GetJavaVersion(const Home: string): string; 389 | var 390 | Binary: string; 391 | begin 392 | result := ''; 393 | if (Home <> '') and DirExists(Home) then 394 | begin 395 | Binary := JoinPath(Home, '\bin\java.exe'); 396 | if FileExists(Binary) then 397 | result := GetFileVersion(Binary); 398 | end; 399 | end; 400 | 401 | function GetJVMPath(const Home: string): string; 402 | var 403 | Binary: string; 404 | begin 405 | result := ''; 406 | if (Home <> '') and DirExists(Home) then 407 | begin 408 | Binary := JoinPath(Home, 'bin\server\jvm.dll'); 409 | if FileExists(Binary) then 410 | exit(Binary); 411 | Binary := JoinPath(Home, 'jre\bin\server\jvm.dll'); 412 | if FileExists(Binary) then 413 | exit(Binary); 414 | end; 415 | end; 416 | 417 | procedure GetJavaDetail(var JavaHome, JavaJVMPath, JavaVersion: string; 418 | var JavaDetectionType: TJavaDetectionType); 419 | var 420 | CurrentHome, CurrentVersion, LatestVersion, LatestHome: string; 421 | LatestDetectionType: TJavaDetectionType; 422 | begin 423 | // Initialize 424 | JavaHome := ''; 425 | JavaJVMPath := ''; 426 | JavaVersion := ''; 427 | JavaDetectionType := JDNone; 428 | // Environment variable search: Exit if found 429 | CurrentHome := FindJavaHomeEnvironment(); 430 | CurrentVersion := GetJavaVersion(CurrentHome); 431 | if CurrentVersion <> '' then 432 | begin 433 | JavaHome := CurrentHome; 434 | JavaVersion := CurrentVersion; 435 | JavaJVMPath := GetJVMPath(JavaHome); 436 | JavaDetectionType := JDEnvironment; 437 | exit(); 438 | end; 439 | // Path search: Exit if found 440 | CurrentHome := FindJavaHomePath(); 441 | CurrentVersion := GetJavaVersion(CurrentHome); 442 | if CurrentVersion <> '' then 443 | begin 444 | JavaHome := CurrentHome; 445 | JavaVersion := CurrentVersion; 446 | JavaJVMPath := GetJVMPath(JavaHome); 447 | JavaDetectionType := JDPath; 448 | exit(); 449 | end; 450 | // Registry search should return latest version from all searches 451 | LatestHome := ''; 452 | LatestVersion := '0'; 453 | LatestDetectionType := JDNone; 454 | // Search 'HKLM\SOFTWARE\JavaSoft' 455 | CurrentHome := FindJavaHomeRegistryJavaSoft('SOFTWARE\JavaSoft'); 456 | CurrentVersion := GetJavaVersion(CurrentHome); 457 | if CurrentVersion <> '' then 458 | begin 459 | if CompareVersionStrings(CurrentVersion, LatestVersion) > 0 then 460 | begin 461 | LatestHome := CurrentHome; 462 | LatestVersion := CurrentVersion; 463 | LatestDetectionType := JDJavaSoft; 464 | end; 465 | end; 466 | // Search 'HKLM\SOFTWARE\IBM' 467 | CurrentHome := FindJavaHomeRegistryJavaSoft('SOFTWARE\IBM'); 468 | CurrentVersion := GetJavaVersion(CurrentHome); 469 | if CurrentVersion <> '' then 470 | begin 471 | if CompareVersionStrings(CurrentVersion, LatestVersion) > 0 then 472 | begin 473 | LatestHome := CurrentHome; 474 | LatestVersion := CurrentVersion; 475 | LatestDetectionType := JDIBM; 476 | end; 477 | end; 478 | // Search Eclipse Adoptium subkeys 479 | CurrentHome := FindJavaHomeRegistryAdoptium(); 480 | CurrentVersion := GetJavaVersion(CurrentHome); 481 | if CurrentVersion <> '' then 482 | begin 483 | if CompareVersionStrings(CurrentVersion, LatestVersion) > 0 then 484 | begin 485 | LatestHome := CurrentHome; 486 | LatestVersion := CurrentVersion; 487 | LatestDetectionType := JDAdoptium; 488 | end; 489 | end; 490 | // Search Microsoft subkeys 491 | CurrentHome := FindJavaHomeRegistryMicrosoft(); 492 | CurrentVersion := GetJavaVersion(CurrentHome); 493 | if CurrentVersion <> '' then 494 | begin 495 | if CompareVersionStrings(CurrentVersion, LatestVersion) > 0 then 496 | begin 497 | LatestHome := CurrentHome; 498 | LatestVersion := CurrentVersion; 499 | LatestDetectionType := JDMicrosoft; 500 | end; 501 | end; 502 | // Search Zulu subkeys 503 | CurrentHome := FindJavaHomeRegistryZulu(); 504 | CurrentVersion := GetJavaVersion(CurrentHome); 505 | if CurrentVersion <> '' then 506 | begin 507 | if CompareVersionStrings(CurrentVersion, LatestVersion) > 0 then 508 | begin 509 | LatestHome := CurrentHome; 510 | LatestVersion := CurrentVersion; 511 | LatestDetectionType := JDZulu; 512 | end; 513 | end; 514 | if (LatestHome <> '') and (LatestVersion <> '0') then 515 | begin 516 | JavaHome := LatestHome; 517 | JavaJVMPath := GetJVMPath(JavaHome); 518 | JavaVersion := LatestVersion; 519 | JavaDetectionType := LatestDetectionType; 520 | end; 521 | end; 522 | 523 | function wsGetJavaHome(): string; 524 | begin 525 | result := RemoveBackslashUnlessRoot(JavaHome); 526 | end; 527 | 528 | function wsGetJavaJVMPath(): string; 529 | begin 530 | result := JavaJVMPath; 531 | end; 532 | 533 | function wsGetJavaVersion(): string; 534 | begin 535 | result := JavaVersion; 536 | end; 537 | 538 | function wsIsJavaInstalled(): Boolean; 539 | begin 540 | result := (JavaHome <> '') and (JavaVersion <> ''); 541 | end; 542 | 543 | function wsIsJavaMinimumVersion(Version: string; var VersionOK: Boolean): Boolean; 544 | begin 545 | result := TestVersionString(Version); 546 | if result then 547 | VersionOK := CompareVersionStrings(JavaVersion, Version) >= 0; 548 | end; 549 | 550 | function wsGetJavaDetectionType(): TJavaDetectionType; 551 | begin 552 | result := JavaDetectionType; 553 | end; 554 | 555 | initialization 556 | GetJavaDetail(JavaHome, JavaJVMPath, JavaVersion, JavaDetectionType); 557 | 558 | end. 559 | --------------------------------------------------------------------------------