When installing my Java app with Inno Setup, I would like the installer to check if Java 7 or above is present, and install it if needed. But apparently, my current code fails to detect Java 8 on the computer of some of my clients. I couldn't reproduce the bug, though. Do you see something that I could be missing? Maybe this code doesn't work with recent Windows?
Basically, I just check the registry for Java >= 1.7.
[Code]
function InitializeSetup(): Boolean;
var
ErrorCode: Integer;
JavaInstalled : Boolean;
ResultMsg : Boolean;
Versions: TArrayOfString;
I: Integer;
regRoot: Integer;
begin
{ Check which view of registry should be taken: }
regRoot := HKLM
begin
if IsWin64 then
begin
regRoot := HKLM64
end;
end;
if (RegGetSubkeyNames(regRoot, 'SOFTWARE\JavaSoft\Java Runtime Environment', Versions)) or (RegGetSubkeyNames(regRoot, 'SOFTWARE\JavaSoft\Java Development Kit', Versions)) then
begin
for I := 0 to GetArrayLength(Versions)-1 do
if JavaInstalled = true then
begin
//do nothing
end else
begin
if ( Versions[I][2]='.' ) and ( ( StrToInt(Versions[I][1]) > 1 ) or ( ( StrToInt(Versions[I][1]) = 1 ) and ( StrToInt(Versions[I][3]) >= 7 ) ) ) then
begin
JavaInstalled := true;
end else
begin
JavaInstalled := false;
end;
end;
end else
begin
JavaInstalled := false;
end;
if JavaInstalled then
begin
Result := true;
end else
begin
ResultMsg := MsgBox(ExpandConstant('{cm:JavaRequired}'), mbConfirmation, MB_YESNO) = idYes;
if ResultMsg = false then
begin
Result := false;
end else
begin
Result := true;
ShellExec('open', 'http://www.java.com/getjava/', '','',SW_SHOWNORMAL,ewNoWait,ErrorCode);
end;
end;
end;
Edit: This is the final result based on Martin's answer.
[Code]
function CutJavaVersionPart(var V: string): Integer;
var
S: string;
P: Integer;
begin
if Length(V) = 0 then
begin
Result := 0;
end
else
begin
P := Pos('.', V);
if P = 0 then P := Pos('_', V);
if P > 0 then
begin
S := Copy(V, 1, P - 1);
Delete(V, 1, P);
end
else
begin
S := V;
V := '';
end;
Result := StrToIntDef(S, 0);
end;
end;
function MaxJavaVersion(V1, V2: string): string;
var
Part1, Part2: Integer;
Buf1, Buf2: string;
begin
Buf1 := V1;
Buf2 := V2;
Result := '';
while (Result = '') and
((Buf1 <> '') or (Buf2 <> '')) do
begin
Part1 := CutJavaVersionPart(Buf1);
Part2 := CutJavaVersionPart(Buf2);
if Part1 > Part2 then Result := V1
else
if Part2 > Part1 then Result := V2;
end;
end;
function GetJavaVersion(): string;
var
TempFile: string;
ResultCode: Integer;
S: AnsiString;
P: Integer;
begin
TempFile := ExpandConstant('{tmp}\javaversion.txt');
if (not ExecAsOriginalUser(
ExpandConstant('{cmd}'), '/c java -version 2> "' + TempFile + '"', '',
SW_HIDE, ewWaitUntilTerminated, ResultCode)) or
(ResultCode <> 0) then
begin
Log('Failed to execute java -version');
end
else
if not LoadStringFromFile(TempFile, S) then
begin
Log(Format('Error reading file %s', [TempFile]));
end
else
if Copy(S, 1, 14) <> 'java version "' then
begin
Log('Output of the java -version not as expected');
end
else
begin
Delete(S, 1, 14);
P := Pos('"', S);
if P = 0 then
begin
Log('Output of the java -version not as expected');
end
else
begin
SetLength(S, P - 1);
Result := S;
end;
end;
DeleteFile(TempFile);
end;
function HasJava1Dot7OrNewer: Boolean;
begin
Result := (MaxJavaVersion('1.6.9', GetJavaVersion) <> '1.6.9');
end;
function InitializeSetup(): Boolean;
var
ErrorCode: Integer;
begin
Result := HasJava1Dot7OrNewer;
if not Result then
begin
Result := MsgBox(ExpandConstant('{cm:JavaRequired}'), mbConfirmation, MB_YESNO) = idYes;
if Result then
begin
ShellExec(
'open', 'https://www.java.com/getjava/', '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);
end;
end;
end;
I do not have the JavaSoft
key in HKLM\Software
. I have it in HKLM\SOFTWARE\WOW6432Node
. What is actually the HKLM\SOFTWARE
in Inno Setup (being 32-bit application).
So it looks like you just need to remove the if IsWin64 then regRoot := HKLM64
block to make it working. Or try both options.
function CutJavaVersionPart(var V: string): Integer;
var
S: string;
P: Integer;
begin
if Length(V) = 0 then
begin
Result := 0;
end
else
begin
P := Pos('.', V);
if P = 0 then P := Pos('_', V);
if P > 0 then
begin
S := Copy(V, 1, P - 1);
Delete(V, 1, P);
end
else
begin
S := V;
V := '';
end;
Result := StrToIntDef(S, 0);
end;
end;
function MaxJavaVersion(V1, V2: string): string;
var
Part1, Part2: Integer;
Buf1, Buf2: string;
begin
Buf1 := V1;
Buf2 := V2;
Result := '';
while (Result = '') and
((Buf1 <> '') or (Buf2 <> '')) do
begin
Part1 := CutJavaVersionPart(Buf1);
Part2 := CutJavaVersionPart(Buf2);
if Part1 > Part2 then Result := V1
else
if Part2 > Part1 then Result := V2;
end;
end;
function GetJavaVersionFromSubKey(RootKey: Integer; SubKeyName: string): string;
var
Versions: TArrayOfString;
I: Integer;
begin
if RegGetSubkeyNames(RootKey, SubKeyName, Versions) then
begin
for I := 0 to GetArrayLength(Versions) - 1 do
begin
Result := MaxJavaVersion(Result, Versions[I]);
end;
end;
end;
function GetJavaVersionFromRootKey(RootKey: Integer): string;
begin
Result :=
MaxJavaVersion(
GetJavaVersionFromSubKey(RootKey, 'SOFTWARE\JavaSoft\Java Runtime Environment'),
GetJavaVersionFromSubKey(RootKey, 'SOFTWARE\JavaSoft\Java Development Kit'));
end;
function GetJavaVersion: string;
begin
Result := GetJavaVersionFromRootKey(HKLM);
if IsWin64 then
begin
Result := MaxJavaVersion(Result, GetJavaVersionFromRootKey(HKLM64));
end;
end;
For your specific needs, you can check, if Java 1.7 or newer is installed like this:
function HasJava1Dot7OrNewer: Boolean;
begin
Result := (MaxJavaVersion('1.6.9', GetJavaVersion) <> '1.6.9');
end;
Or did you consider running java -version
instead?
function GetJavaVersion2: string;
var
TempFile: string;
ResultCode: Integer;
S: AnsiString;
P: Integer;
begin
TempFile := ExpandConstant('{tmp}\javaversion.txt');
if (not ExecAsOriginalUser(
ExpandConstant('{cmd}'), '/c java -version 2> "' + TempFile + '"', '',
SW_HIDE, ewWaitUntilTerminated, ResultCode)) or
(ResultCode <> 0) then
begin
Log('Failed to execute java -version');
end
else
if not LoadStringFromFile(TempFile, S) then
begin
Log(Format('Error reading file %s', [TempFile]));
end
else
if Copy(S, 1, 14) <> 'java version "' then
begin
Log('Output of the java -version not as expected');
end
else
begin
Delete(S, 1, 14);
P := Pos('"', S);
if P = 0 then
begin
Log('Output of the java -version not as expected');
end
else
begin
SetLength(S, P - 1);
Result := S;
end;
end;
DeleteFile(TempFile);
end;
A bit more efficient implementation of the InitializeSetup
:
function InitializeSetup(): Boolean;
var
ErrorCode: Integer;
begin
Result := HasJava1Dot7OrNewer;
if not Result then
begin
Result := MsgBox(ExpandConstant('{cm:JavaRequired}'), mbConfirmation, MB_YESNO) = idYes;
if Result then
begin
ShellExec(
'open', 'https://www.java.com/getjava/', '', '', SW_SHOWNORMAL, ewNoWait, ErrorCode);
end;
end;
end;
Answering my own comment, here's my variation which should work regardless of whether the installed JRE is Oracle's or OpenJDK's, and for Java versions prior to and after Java 9.
The GetJavaMajorVersion()
function returns the Java version as an integer. For versions where java -version
reports "1.x.*" (up to and including Java 8) it will return x, otherwise (Java 9 and later) it will return the actual major number. If for some reason the call to java -version
fails it will return 0.
function GetJavaMajorVersion(): integer;
var
TempFile: string;
ResultCode: Integer;
S: AnsiString;
P: Integer;
begin
Result := 0;
{ execute java -version and redirect output to a temp file }
TempFile := ExpandConstant('{tmp}\javaversion.txt');
if (not ExecAsOriginalUser(ExpandConstant('{cmd}'), '/c java -version 2> "' + TempFile + '"', '',SW_HIDE, ewWaitUntilTerminated, ResultCode))
or (ResultCode <> 0) then
begin
Log('Failed to execute java -version');
exit;
end;
{ read file into variable S }
LoadStringFromFile(TempFile, S)
DeleteFile(TempFile);
Log(Format('java -version output: ' + #13#10 + '%s', [S]));
{ extract version (between quotes) }
P := Pos('"', S);
Delete(S, 1, P);
P := Pos('"', S);
SetLength(S, P - 1);
Log(Format('Extracted version: %s', [S]));
{ extract major }
if Copy(S, 1, 2) = '1.' then
begin
Delete(S, 1, 2)
end;
P := Pos('.', S);
SetLength(S, P - 1);
Log(Format('Major version: %s', [S]));
Result := StrToIntDef(S, 0);
end;
function InitializeSetup(): boolean;
var
ResultCode: Integer;
begin
if GetJavaMajorVersion >= 8 then
begin
Result := true;
exit;
end;
if MsgBox('This tool requires a Java Runtime Environment version 1.8 or newer to run. ' +
'Please download and install the JRE and run this setup again. ' +
'Would you like to open the JRE download page now?', mbCriticalError, MB_YESNO) = idYes then
begin
Result := false;
ShellExec('open', 'https://java.com/download/', '', '', SW_SHOWNORMAL, ewNoWait, ResultCode);
end;
end;
Lots of room for improvement, but it does the job.