Repository: nvaccess/nvda

Issue:
No X86 JAVA9 Access Bridge available might cause JAVA support issues for NVDA in future
### Steps to reproduce:

>
> as you know JDK v9 is produced only in 64-bit 
>
> 1. after installing JDK v9
> 2. JAB switch is enabled.

### Expected behavior:
> NVDA should reads elements of the Java window.

### Actual behavior:
> but NVDA does not  read elements.

### System configuration:
> NVDA version:
> Version: next-14581,1014f6d4
NVDA Installed or portable: Installed 
> Windows version: Windows 10 

Name and version of other software in use when reproducing the issue:
> JDK v9 , and JRE v9
### Other questions:

Does the issue still occur after restarting your PC?
> Yes it does
Have you tried any other versions of NVDA?
> No



Discussion:
[**@shadyar**](https://github.com/shadyar) commented on [3 nov. 2017 10:23 CET](https://github.com/nvaccess/nvda/issues/7724#issue-270917165 "2017-11-03T09:23:52Z - Replied by Github Reply Comments"):
> as you know JDK v9 is produced only in 64-bit 

What makes you think this is the case? I have JRE 9 X86 here and it works pretty well.
I mentioned this problem on an issue, I think we can do with following changes:

Source: #7609 
File: source / JABHandler.py

> #Load the first available access bridge dll
legacyAccessBridge=True
try:
	bridgeDll=getattr(cdll,'windowsAccessBridge-64')
	legacyAccessBridge=False

Please open a try-jabswitch64 branch, we should try this code there.
windowsAccessBridge-64 won't work with NVDA, as NVDA is, and will probably remain, x86.

@shadyar: Could you please answer my https://github.com/nvaccess/nvda/issues/7724#issuecomment-341670241 ? I'm afraid I have to close this as invalid otherwise.
Hmm, seems you are right, according to this [Stack Overflow thread](https://stackoverflow.com/questions/46356345/can-java-9-run-on-a-32-bit-os) . It seems I was one of these lucky ones mentioned in this thread.

CC @Michaeldcurran, @feerrenrut, @jcsteh 

Would it be possible to convince Oracle to ship WindowsAccessBridge-32.dll, I wonder. I guess not, since they never shipped that DLL with the X64 jre. Other options might be:

* Just advice people to stick with JAVA 8
* Make people use JAVA 9 for X64 and JAVA8 for x86 applications. This might work, but should be tested
* Build an X86 version of WindowsAccessBridge-32.dll ourselves, if we could
* Proxy all JAB stuff using NVDAHelper
May [this file](http://www.oracle.com/technetwork/java/javase/downloads/jab-2-0-2-download-354311.html) be used? This file is containing a C codes. I think it is able to compiled for supporting 64-bit architecture.
There must be machines still out there no 64 bit so this seems a little odd 
to me. In the past did you not need both enabled to make accessible java 
apps work reliable anyway?


I'm afraid I don't have any Java on this machine after the clunky 
experiences on xp I used to get on such software.
 Brian

@leonardder 
#7724

hi
Leonardder, I do not know how you use JRE v9 x86 due to it has not been published by Oracle as an official final version. it seems JRE & JDK are published only for x64 machines
take a look at these pages
[official  JDK download page](http://www.oracle.com/technetwork/java/javase/downloads/jdk9-downloads-3848520.html)
[official  JRE download page](http://www.oracle.com/technetwork/java/javase/downloads/jre9-downloads-3848532.html)

what restricts the NVDA project to become a 64bit software as well? The problem would be solved and NVDA would be equiped for future projects since there will be less and less applications in 32 bit.
Hi, portability matters because people use NVDA as portable copy, which runs as 32-bit app to maximize compatibility. Thanks.



Hi, but in Windows 10, NVDA portable is pretty  lame. Many bits of software 
do not read, take as a for instance the list of issues in Ccleaner for 
example. One assumes this is the use of UIA only being fully available to 
trusted installed software.
 Brian


Would this not rather mean two versions as 32 bit systems are not going away 
fast and one has to remember that free screenreaders are often used by those 
who do not have much disposable income and so tend to use older hardware.

I also seem to recall reading that there was a Java access bridge for 32 bit 
systems its just not the latest all singing and dancing one. I also recall 
both bridges needed to be active to gat access and at no time did anyone 
suggest a 32 bit screenreader could not access the 64 bit bridge.
 The biggest issue with any Java software seems to me to be the authors 
simply not writing accessible code in the first place.
 Brian
I found a very interesting discussion on this here:

https://portableapps.com/node/24371

Why can NVDA not become a hybrid version?
Alternatively, in case a hybrid version is not feasible, nowadays we don't have really big problems with respect to enough storage on a stick anymore. Sticks with even 32 gb can be bought for 15 USD or cheaper. So why not having the possibility to install 64bit and 32bit versions of NVDA on a stick?


I would say that only properly installed versions of a screenreader are 
allowed to access some of the accessibility APIs and hence I do not see how 
you can have a portable version which is installed as well and both will be 
able to see the uia data etc needed.

Catch 22 and all that.

Unfortunately, like it or not, sometimes Java swing applications are unavoidable, despite how bad we may feel java access bridge is in practice. Android Studio is one such example.

Suggesting users keep Java8 for a 32-bit bridge is not a long term solution as Oracle will be dropping support for it soon.

The best work around I can see for now is for users who must use Java swing applications to use alternative OpenJDK builds from companies such as Azul https://www.azul.com/downloads/zulu/ who offer Windows 32-bit and 64-bit builds of the JDK. Unfortunately in the case of Azul Zulu, it will mean users will need to manually install from ZIP files.

The above though is really a work around and it would be a far better user experience if NVDA could actually just make use of the 64-bit Java access bridge without needing a 32-bit bridge as well.
I thought always that some older 32bit java aplications would expose information only to the 32bit java access bridge. Or not? What is the current status of this now with the newer Java version?
Report from a user of another use case impacted here (because of Java 9) is developing for Android which requires Android Studio which requires uses Java 9 64-bit.
Hello,
I have examined the source code of the WindowsAccessBridge Java DLL on Java 8 and I tested that if I copy the WindowsAccessBridge32.dll from a x86 build of Java 8 it works on a 64 bit JVM, because the interaction is done on the JVM side and not in the WindowsAccessBridge DLL itself.
Maybe this can help a bit with this problem. If this DLL just passes messages between JVM and NVDA then it might be possible to include it on the NVDA folder and newer Java versions could work. The problem would be when the Java team modifies this DLL and no x86 version can be built, but this could be a temporary workaround.
Update:
It seems that the WindowsAccessBridge32.dll from Java 8 works on newer Java VMS.
I tested it now with Java 12 x64 and it worked.

I've had to look into this myself, since I need to use Android Studio.

The simple technical solution is for us to ship WindowsAccessBridge-32.dll with NVDA. The problem in the past was that the Oracle binary license prevents this. Now we have OpenJDK (GPL v2), but the problem now is that there are no 32 bit builds of newer versions.

Today, I created a sconstruct to build just the 32 bit Java Access Bridge from the OpenJDK sources:
https://github.com/jcsteh/javaAccessBridge32
The repository pulls in the OpenJDK GitHub mirror as a Git submodule. Then, you just run scons and end up with build/WindowsAccessBridge-32.dll.

The question is: how do we want to ship this with NVDA? I don't think pulling in the OpenJDK submodule for every build is feasible, as the checkout is over 1 gb. Do we want to just shove it in miscDeps or do we want a separate binary repo containing just the single dll? @MichaelDCurran, thoughts?
@jcsteh Not that you asked me, but personally I would prefer a separate binary repo. It makes it harder to get in a tangle when updating the dependencies and makes it easier to document the process for creating / updating each of the dependencies. I think it also makes it easier to see what the dependencies are.
Your input is just as welcome and authoritative, @feerrenrut. :) I assumed
Mick might have more history here, which is why I asked him, but that's
probably not true in this case anyway. I'll proceed as you suggest. I feel
weird about lots of little Git repos containing single dll files, but I
also know what you mean about the clarity.

I think it makes sense if you could provide the sconstruct file for reference in the binary repo. though it won't be uset, it might make it much easier to update the build if needed.
> I think it makes sense if you could provide the sconstruct file for reference in the binary repo

Instead, the readme can just refer to the repo by @jcsteh which has everything necessary: https://github.com/jcsteh/javaAccessBridge32 Perhaps we should have our own fork.
There are 32-bit builds of OpenJDK for Windows from Azul Systems https://www.azul.com/downloads/zulu-community/?&os=&os=windows&architecture=x86-32-bit&package=jre

The JRE community build in the above link contains WindowsAccessBridge-32.dll and the Zulu community builds are free to download and use without restrictions. 

Seems like here is the binary which needs to be bundled with suitable licensing, no compiling, no need to download 1GB+ of repositories, etc. 

Sorry I don't get why this is so difficult. I mentioned Azul zulu builds before and this is why I don't get why more complicated/involved solutions are being considered. Have I missed something? 
@mwhapples: sorry, I seem to have missed the significance of your comment. I probably saw it briefly, but wasn't sure about the licensing or similar at the time. (Being able to download and use it as a whole without restrictions is very different to pulling part of it out and bundling it with something else, which is the problem we've had in the past with the JAB. We really don't want users to have to install this; it's much simpler if we can bundle it.)

Anyway, I agree this is a simpler solution, although the download size isn't really a huge factor here (it's still ~190 mbvs ~390 mb for the Git download; both take up even more space when extracted). I wish I'd dug into your comment sooner; I could have saved myself a few hours. :)

So we just need to extract the binary, shove it in a repo, add a quick readme about where it came from, and submit a PR to NVDA to include it as a submodule and use it. We should probably also drop support for the legacy JAB in JABHandler, since it won't be usable once this is bundled.
Scrap that; this build is only 40 mb. Not sure why the official OpenJDK builds are so much larger.

Resolving patch:
diff --git a/source/JABHandler.py b/source/JABHandler.py
--- a/source/JABHandler.py
+++ b/source/JABHandler.py
@@ -4,6 +4,7 @@
 # This file is covered by the GNU General Public License.
 # See the file COPYING for more details.
 
+import os
 import queue
 from ctypes import (
 	c_short,
@@ -37,6 +38,7 @@
 import NVDAObjects.JAB
 import core
 import textUtils
+import NVDAHelper
 
 #Some utility functions to help with function defines
 
@@ -56,16 +58,8 @@ def _fixBridgeFunc(restype,name,*argtypes,**kwargs):
 	if kwargs.get('errcheck'):
 		func.errcheck=_errcheck
 
-#Load the first available access bridge dll
-legacyAccessBridge=True
-try:
-	bridgeDll=getattr(cdll,'windowsAccessBridge-32')
-	legacyAccessBridge=False
-except WindowsError:
-	try:
-		bridgeDll=cdll.windowsAccessBridge
-	except WindowsError:
-		bridgeDll=None
+
+bridgeDll = None
 
 #Definitions of access bridge types, structs and prototypes
 
@@ -73,7 +67,9 @@ def _fixBridgeFunc(restype,name,*argtypes,**kwargs):
 jint=c_int
 jfloat=c_float
 jboolean=c_bool
-class JOBJECT64(c_int if legacyAccessBridge else c_int64):
+
+
+class JOBJECT64(c_int64):
 	pass
 AccessibleTable=JOBJECT64
 
@@ -248,8 +244,10 @@ class AccessibleKeyBindings(Structure):
 AccessBridge_PropertyCaretChangeFP=CFUNCTYPE(None,c_long,JOBJECT64,JOBJECT64,c_int,c_int)
 AccessBridge_PropertyActiveDescendentChangeFP=CFUNCTYPE(None,c_long,JOBJECT64,JOBJECT64,JOBJECT64,JOBJECT64)
 
-#Appropriately set the return and argument types of all the access bridge dll functions
-if bridgeDll:
+
+def _fixBridgeFuncs():
+	"""Appropriately set the return and argument types of all the access bridge dll functions
+	"""
 	_fixBridgeFunc(None,'Windows_run')
 	_fixBridgeFunc(None,'setFocusGainedFP',c_void_p)
 	_fixBridgeFunc(None,'setPropertyNameChangeFP',c_void_p)
@@ -741,9 +739,13 @@ def isJavaWindow(hwnd):
 	return bridgeDll.isJavaWindow(hwnd)
 
 def initialize():
-	global isRunning
-	if not bridgeDll:
+	global bridgeDll, isRunning
+	try:
+		bridgeDll = cdll.LoadLibrary(
+			os.path.join(NVDAHelper.versionedLibPath, "windowsaccessbridge-32.dll"))
+	except WindowsError:
 		raise NotImplementedError("dll not available")
+	_fixBridgeFuncs()
 	bridgeDll.Windows_run()
 	# Accept wm_copydata and any wm_user messages from other processes even if running with higher privileges
 	if not windll.user32.ChangeWindowMessageFilter(winUser.WM_COPYDATA, 1):
@@ -775,9 +777,5 @@ def terminate():
 	bridgeDll.setPropertyCaretChangeFP(None)
 	h=bridgeDll._handle
 	bridgeDll=None
-	if legacyAccessBridge:
-		del cdll.windowsAccessBridge 
-	else:
-		delattr(cdll,'windowsAccessBridge-32')
 	windll.kernel32.FreeLibrary(h)
 	isRunning=False


Test patch: