Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Installer: try to clean up old library files at the end of installation with out affecting the rest of a successful install #7649

Merged
merged 14 commits into from
Oct 25, 2017

Conversation

michaelDCurran
Copy link
Member

@michaelDCurran michaelDCurran commented Oct 9, 2017

Link to issue number:

Fixes #7616
Fixes #7639

Summary of the issue:

With the merging of #7535 (which causes NVDA to now store libs in a version-specific directory), NVDA updates were constantly failing for certain users.
One or more of these users seemed to be running some kind of Intell security software which would occasionally lock files.
In most situations though, the majority of users were not experiencing the failing updates.
However, #7535 did introduce two specific mistakes. these were:

  1. logHandler did not look in the right path for nvdaHelperRemote in order to do remote logging from nvda_slave. This meant that we were no longer getting any logging from the installer.
  2. Files marked for deletion on reboot were never actually getting deleted as the directory they were in may also have been renamed and marked for delete on reboot, thus invalidating the path to the first lot of marked files. When the directory itself was going to be deleted on reboot, it still contained files and therefore Windows refuzed to delete it.
  3. It seems on some systems that if there are locked files in a directory, although you can rename the locked files, you can't rename the directory the locked files are in. Ensure IAccessible2 remains available in Firefox after restarting NVDA on Windows 10 Creaters update, register ISimpleDOM proxy to allow math in Google Chrome without Firefox installed, and Allow the user to get back a virtualBuffer when restarting NVDA with a Firefox document in focus #7535 was trying to rename these directories for delete on reboot.

Description of how this pull request fixes the issue:

Fix logging

logHandler now looks for nvdaHelperRemote in its version-specific directory, allowing logging to work from nvda_slave again. It also fails much earlier now if that dll cannot be found.

Old lib files are deleted only at the very end

Rather than deleting old library files at the beginning of the installation (which meant if this did introduce an error, the rest of the install would fail), instead try to delete them at the very end.
Also, if one of these files fails to be deleted, don't treet this as a hard error, only log a warning. The installer next time it is run can try and get rid of them again.

Testing performed:

Multiple NVDA updates on Windows 10 and Windows 7, with reboots. the updates succeeed, and all locked files are removed on reboot.
This also included performing the update with the old copy of NVDA still running on another session on the machine. The installer still successfully installed the new copy.

This uses Windows' MoveFileEx (either unicode or ascii depending on the given path) and also prepends'\\?\' to allow for long file names.
"""
MoveFileEx=windll.kernel32.MoveFileExW if isinstance(path,unicode) else windll.kernel32.MoveFileExA
return MoveFileEx("\\\\?\\"+path,None,4)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you replace the 4 with something like MOVEFILE_DELAY_UNTIL_REBOOT

def copyFile(sourceFilePath,destFilePath):
"""
Copies the file at sourcePath to destPath.
This uses Windows' CopyFile and prepends'\\?\' to allow for long file names.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does the return value mean?

def deleteFileOnReboot(path):
"""
Marks a file for delete on reboot.
This uses Windows' MoveFileEx (either unicode or ascii depending on the given path) and also prepends'\\?\' to allow for long file names.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is the type/meaning of the return value?

h=ctypes.windll.kernel32.LoadLibraryExW(os.path.abspath(ur"lib\nvdaHelperRemote.dll"),0,0x8)
self._remoteLib=ctypes.WinDLL("nvdaHelperRemote",handle=h) if h else None
path=os.path.abspath(os.path.join(u"lib",versionInfo.version,u"nvdaHelperRemote.dll"))
h=ctypes.windll.kernel32.LoadLibraryExW(path,0,0x8)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

replace 0x8 with something like LOAD_WITH_ALTERED_SEARCH_PATH

@Brian1Gaff
Copy link

Brian1Gaff commented Oct 9, 2017 via email

@michaelDCurran
Copy link
Member Author

Backed out of next. Multiple people were seeing problems. Perhaps this is simply a timing issue. The older installer code took more time trying to remove the first lot of files and therefore the old NVDA had enough time to fully exit or something. Very very strnage.

@michaelDCurran
Copy link
Member Author

At very least we need to take the logging fixes from this PR (assuming that we keep #7535).

michaelDCurran added a commit that referenced this pull request Oct 18, 2017
@michaelDCurran michaelDCurran changed the title Make installer handle more situations of locked files and add more logging Installer: try to clean up old library files at the end of installation with out affecting the rest of a successful install Oct 18, 2017
@michaelDCurran
Copy link
Member Author

This pr is now much les drastic, in that old lib files are only removed at the very end of the instllation, and no rewriting of other installer code such as tryRemovefile or trycopyFile exists in this pr now. thus, the rhythm and logic of the install is exactly the same as NVDA 2017.3 except for removing of old lib files at the very end. If this part fails, it is not a hard error.

@michaelDCurran
Copy link
Member Author

This has also been incubated in Next again for further testing of Next snapshots by Brian1Gaff and others. Testing Master is not necessary at this stage as the change have not yet reached that branch.

michaelDCurran added a commit that referenced this pull request Oct 18, 2017
@LeonarddeR
Copy link
Collaborator

May I ask why these changes have been incubated into Next without review?

@michaelDCurran
Copy link
Member Author

michaelDCurran commented Oct 19, 2017 via email

@Brian1Gaff
Copy link

Brian1Gaff commented Oct 19, 2017 via email

@Mary5958
Copy link
Contributor

Today I could "finally" reproduce it also on Windows 10; first while updating to the latest next, then also when doing a fresh install after a complete uninstall of the previous version and even removing the user data if that would make any difference.

INFO - main (21:13:29.977):
Starting NVDA
INFO - core.main (21:13:32.641):
Config dir: C:\Users\Martina\AppData\Local\Temp\nsk316.tmp\app\userConfig
INFO - config.ConfigManager._loadConfig (21:13:32.641):
Loading config: .\userConfig\nvda.ini
INFO - core.main (21:13:32.904):
NVDA version next-14538,68978508
INFO - core.main (21:13:32.904):
Using Windows version 10.0.15063 workstation
INFO - core.main (21:13:32.904):
Using Python version 2.7.13 (v2.7.13:a06454b1afa1, Dec 17 2016, 20:42:59) [MSC v.1500 32 bit (Intel)]
INFO - core.main (21:13:32.904):
Using comtypes version 0.6.2
INFO - synthDrivers.espeak.SynthDriver.init (21:13:34.642):
Using eSpeak NG version 1.49.1 dev
INFO - speechDictHandler.dictFormatUpgrade._doSynthVoiceDictBackupAndMove (21:13:34.709):
Upgrading voice dictionaries for espeak
INFO - synthDriverHandler.setSynth (21:13:34.819):
Loaded synthDriver espeak
INFO - core.main (21:13:34.819):
Using wx version 3.0.2.0 msw (classic)
INFO - brailleInput.initialize (21:13:34.822):
Braille input initialized
INFO - braille.initialize (21:13:34.822):
Using liblouis version 3.3.0
INFO - braille.BrailleHandler.setDisplayByName (21:13:34.825):
Loaded braille display driver noBraille, current display has 0 cells.
WARNING - core.main (21:13:35.026):
Java Access Bridge not available
INFO - _UIAHandler.UIAHandler.MTAThreadFunc (21:13:35.045):
UIAutomation: IUIAutomation3
INFO - core.main (21:13:36.140):
NVDA initialized
ERROR - RPC process 14668 (nvda_slave.exe) (21:14:20.546):
main.main:
slave error
Traceback (most recent call last):
File "nvda_slave.pyw", line 42, in main
File "installer.pyc", line 414, in install
File "installer.pyc", line 159, in removeOldLibFiles
UnicodeDecodeError: 'ascii' codec can't decode byte 0xe1 in position 17: ordinal not in range(128)
ERROR - gui.installerGui.doInstall (21:14:20.628):
Installation failed: 1

But then after rebooting from this temporary version of the new install the new version works fine as if it was installed successfully.

michaelDCurran added a commit that referenced this pull request Oct 20, 2017
@michaelDCurran
Copy link
Member Author

michaelDCurran commented Oct 20, 2017 via email

@Mary5958
Copy link
Contributor

Yes, it worked fine this time, thanks.

@michaelDCurran
Copy link
Member Author

@feerrenrut another review please. This time probably easiest to just review whole thing again as much of the code was removed and only left the most necessary bits, now isolated to the very end of the install. This now seems to work on all systems people have tested on, thankfully.

@Brian1Gaff
Copy link

Brian1Gaff commented Oct 21, 2017 via email

@michaelDCurran
Copy link
Member Author

michaelDCurran commented Oct 21, 2017 via email

@Brian1Gaff
Copy link

Brian1Gaff commented Oct 21, 2017 via email

Copy link
Contributor

@feerrenrut feerrenrut left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you outline what testing has been done on this? The permutations that I can think of are:

  • Different OS versions
  • Upgrade from installed 2017.3
  • Clean install
  • Upgrade of portable 2017.3

This is assuming that upgrade from last master or next is not so important.

@@ -154,14 +184,6 @@ def removeOldProgramFiles(destPath):
else:
os.remove(fn)

# #7546: Remove old version-specific libs
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this issue number still relevant? If so we should probably include it.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This issue number was totally wrong. Not sure why it was there in the first place. It was pointing to the one about not supporting XP anymore.

@@ -432,6 +455,7 @@ def createPortableCopy(destPath,shouldCopyUserConfig=True):
tryCopyFile(os.path.join(destPath,"nvda_noUIAccess.exe"),os.path.join(destPath,"nvda.exe"))
if shouldCopyUserConfig:
copyUserConfig(os.path.join(destPath,'userConfig'))
removeOldLibFiles(destPath,rebootOK=False)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is rebootOK=False here, and rebootOK=True in def install?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because usually for portable copies it is impossible to delete after reboot as you need admin privilege to set this. However, there is no error if it doesn't work, so I've now allowed it anyway. Some people always running as admin this can work for. Plus, this also ensures that files remain renamed to temp files even if they can't be deleted on reboot, which keeps them out of the way of future installs.

@Brian1Gaff
Copy link

Brian1Gaff commented Oct 23, 2017 via email

…ser *may* have admin access which means it would work. Also it ensures the file, deleted or not, will still be kept renamed as a temp file out of the way.
@michaelDCurran
Copy link
Member Author

I have tested both on Windows 7 and Windows 10, for both installed and portable, for upgrading from 2017.3, and clean.

@@ -455,7 +455,7 @@ def createPortableCopy(destPath,shouldCopyUserConfig=True):
tryCopyFile(os.path.join(destPath,"nvda_noUIAccess.exe"),os.path.join(destPath,"nvda.exe"))
if shouldCopyUserConfig:
copyUserConfig(os.path.join(destPath,'userConfig'))
removeOldLibFiles(destPath,rebootOK=False)
removeOldLibFiles(destPath,rebootOK=True)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about the case where a portable copy resides on a removable drive and that drive is no longer available after a reboot? I assume Windows silently ignores this?

@michaelDCurran
Copy link
Member Author

michaelDCurran commented Oct 23, 2017 via email

@Brian1Gaff
Copy link

Brian1Gaff commented Oct 23, 2017 via email

@Brian1Gaff
Copy link

Brian1Gaff commented Oct 23, 2017 via email

michaelDCurran added a commit that referenced this pull request Oct 24, 2017
@michaelDCurran michaelDCurran merged commit 1fcc7fe into master Oct 25, 2017
@nvaccessAuto nvaccessAuto added this to the 2017.4 milestone Oct 25, 2017
@Brian1Gaff
Copy link

Brian1Gaff commented Oct 26, 2017 via email

@Brian1Gaff
Copy link

Brian1Gaff commented Nov 2, 2017 via email

@Brian1Gaff
Copy link

Brian1Gaff commented Nov 17, 2017 via email

@michaelDCurran
Copy link
Member Author

michaelDCurran commented Nov 19, 2017 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Failures to update an installed copy of master branch Updating portable next snaps not working today
6 participants