From 4e08aaec0c32962c3515caa668a255d96cd8463a Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 31 Aug 2017 17:02:43 +1000 Subject: [PATCH 01/25] Compile NVDA with the Windows 10 SDK, replace GetVersion with versionHelper macros, and remove a pre-vista TSF check. --- nvdaHelper/archBuild_sconscript | 17 +++++- nvdaHelper/local/mixer.cpp | 4 -- nvdaHelper/remote/ime.cpp | 4 +- nvdaHelper/remote/inputLangChange.cpp | 6 +- nvdaHelper/remote/tsf.cpp | 15 ++--- nvdaHelper/remote/tsf.h | 1 - sconstruct | 6 +- site_scons/site_tools/windowsSdk.py | 88 --------------------------- 8 files changed, 25 insertions(+), 116 deletions(-) delete mode 100644 site_scons/site_tools/windowsSdk.py diff --git a/nvdaHelper/archBuild_sconscript b/nvdaHelper/archBuild_sconscript index 774aa279f6a..d9a61540fd3 100644 --- a/nvdaHelper/archBuild_sconscript +++ b/nvdaHelper/archBuild_sconscript @@ -34,7 +34,14 @@ release=env['release'] signExec=env['signExec'] if env['certFile'] else None #Some defines and includes for the environment -env.Append(CPPDEFINES=['UNICODE','_CRT_SECURE_NO_DEPRECATE',('LOGLEVEL','${nvdaHelperLogLevel}'),('_WIN32_WINNT','_WIN32_WINNT_WS03')]) +env.Append( + CPPDEFINES=[ + 'UNICODE', + '_CRT_SECURE_NO_DEPRECATE', + ('LOGLEVEL','${nvdaHelperLogLevel}'), + ('_WIN32_WINNT','_WIN32_WINNT_WIN7') + ] +) env.Append(CCFLAGS=['/W3','/WX']) if 'analyze' in debug: env.Append(CCFLAGS=['/analyze']) @@ -48,7 +55,13 @@ if 'analyze' in debug: env.Append(CXXFLAGS=['/EHsc']) env.Append(CPPPATH=['#/include','#/miscDeps/include',Dir('.').abspath]) -env.Append(LINKFLAGS=['/incremental:no','/WX']) +env.Append( + LINKFLAGS=[ + '/incremental:no', + '/WX' + '/subsystem:windows,6.01', + ] +) env.Append(LINKFLAGS='/release') #We always want a checksum in the header env.Append(MIDLFLAGS='/x64' if TARGET_ARCH=='x86_64' else '/win32') diff --git a/nvdaHelper/local/mixer.cpp b/nvdaHelper/local/mixer.cpp index 159492e88fe..668d957c170 100644 --- a/nvdaHelper/local/mixer.cpp +++ b/nvdaHelper/local/mixer.cpp @@ -11,10 +11,6 @@ Copyright 2008-2014 NV Access Limited. This license can be found at: http://www.gnu.org/licenses/old-licenses/gpl-2.0.html */ -// This file can only compile for Vista and above -// NVDA will not call this function on lower Loperating Systems -#undef _WIN32_WINNT -#define _WIN32_WINNT 0x0600 #include #include diff --git a/nvdaHelper/remote/ime.cpp b/nvdaHelper/remote/ime.cpp index 5a6d753bdac..6f87964229b 100644 --- a/nvdaHelper/remote/ime.cpp +++ b/nvdaHelper/remote/ime.cpp @@ -444,8 +444,8 @@ static LRESULT handleIMEWindowMessage(HWND hwnd, UINT message, WPARAM wParam, LP break; case IMN_PRIVATE: - // Needed in XP to support EasyDots IME - if (!isUIElementMgrSafe || !isTSFThread(true)) + // Needed to support EasyDots IME when TSF is not available + if (!isTSFThread(true)) handleReadingStringUpdate(hwnd); break; } diff --git a/nvdaHelper/remote/inputLangChange.cpp b/nvdaHelper/remote/inputLangChange.cpp index 8b0864e98f3..dc50eb1f00e 100755 --- a/nvdaHelper/remote/inputLangChange.cpp +++ b/nvdaHelper/remote/inputLangChange.cpp @@ -14,6 +14,7 @@ This license can be found at: #define WIN32_LEAN_AND_MEAN #include +#include #include "nvdaHelperRemote.h" #include "nvdaControllerInternal.h" #include "ime.h" @@ -38,10 +39,7 @@ LRESULT CALLBACK inputLangChange_callWndProcHook(int code, WPARAM wParam, LPARAM } void inputLangChange_inProcess_initialize() { - WORD version=LOWORD(GetVersion()); - if(LOBYTE(version)>6||(LOBYTE(version)==6&&HIBYTE(version)>=2)) { - isWin8=true; - } + isWin8=IsWindows8OrGreater(); registerWindowsHook(WH_CALLWNDPROC,inputLangChange_callWndProcHook); } diff --git a/nvdaHelper/remote/tsf.cpp b/nvdaHelper/remote/tsf.cpp index c823d1f3b05..003f3039177 100644 --- a/nvdaHelper/remote/tsf.cpp +++ b/nvdaHelper/remote/tsf.cpp @@ -28,7 +28,6 @@ This license can be found at: using namespace std; CLSID curTSFClsID={0, 0, 0, {0, 0, 0, 0, 0, 0, 0, 0}}; -bool isUIElementMgrSafe=false; bool fetchRangeExtent(ITfRange* pRange, long* start, ULONG* length) { @@ -182,13 +181,11 @@ bool TsfSink::Initialize() { hr = src->AdviseSink(IID_ITfActiveLanguageProfileNotifySink,(ITfActiveLanguageProfileNotifySink*)this, &mLangProfCookie); } } - if(isUIElementMgrSafe) { - if (hr == S_OK) { - hr = mpThreadMgr->QueryInterface(IID_ITfUIElementMgr,(void**)&mpUIElementMgr); - } - if (hr == S_OK) { - hr = src->AdviseSink(IID_ITfUIElementSink,(ITfUIElementSink*)this, &mUIElementCookie); - } + if (hr == S_OK) { + hr = mpThreadMgr->QueryInterface(IID_ITfUIElementMgr,(void**)&mpUIElementMgr); + } + if (hr == S_OK) { + hr = src->AdviseSink(IID_ITfUIElementSink,(ITfUIElementSink*)this, &mUIElementCookie); } src->Release(); src = NULL; @@ -647,8 +644,6 @@ TsfSink* fetchCurrentTsfSink() { } void TSF_inProcess_initialize() { - //Allow use of UIElementMgr on Vista and higher (crashes things on XP) - if((GetVersion()&0xff)>5) isUIElementMgrSafe=true; // Initialize TLS and use window hook to create TSF sink in each thread gTsfIndex = TlsAlloc(); if (gTsfIndex != TLS_OUT_OF_INDEXES) diff --git a/nvdaHelper/remote/tsf.h b/nvdaHelper/remote/tsf.h index 00d7e01a05b..09dd672c47f 100644 --- a/nvdaHelper/remote/tsf.h +++ b/nvdaHelper/remote/tsf.h @@ -20,6 +20,5 @@ void TSF_inProcess_terminate(); void TSF_thread_detached(); bool isTSFThread(bool checkProfile); extern CLSID curTSFClsID; -extern bool isUIElementMgrSafe; #endif diff --git a/sconstruct b/sconstruct index e34c62e579e..4a8817f63c7 100755 --- a/sconstruct +++ b/sconstruct @@ -142,11 +142,7 @@ def signExec(target,source,env): env['signExec']=signExec #architecture-specific environments -archTools=['default','windowsSdk','midl','msrpc'] -# MSVC analysis is only available in newer Windows SDKs -# So if required, remove the Windows SDK 7.1 (XP) tool -if 'analyze' in env['nvdaHelperDebugFlags']: - archTools.remove('windowsSdk') +archTools=['default','midl','msrpc'] env32=env.Clone(TARGET_ARCH='x86',tools=archTools) env64=env.Clone(TARGET_ARCH='x86_64',tools=archTools) # Hack around odd bug where some tool [after] msvc states that static and shared objects are different diff --git a/site_scons/site_tools/windowsSdk.py b/site_scons/site_tools/windowsSdk.py deleted file mode 100644 index 9cfe9ac73d3..00000000000 --- a/site_scons/site_tools/windowsSdk.py +++ /dev/null @@ -1,88 +0,0 @@ -### -#This file is a part of the NVDA project. -#URL: http://www.nvda-project.org/ -#Copyright 2006-2010 NVDA contributers. -#This program is free software: you can redistribute it and/or modify -#it under the terms of the GNU General Public License version 2.0, as published by -#the Free Software Foundation. -#This program is distributed in the hope that it will be useful, -#but WITHOUT ANY WARRANTY; without even the implied warranty of -#MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. -#This license can be found at: -#http://www.gnu.org/licenses/old-licenses/gpl-2.0.html -### - -import subprocess -import _winreg -import os -from SCons.Tool.MSCommon import common -from SCons.Tool import msvc - -#Forecefully disable MSVC detection and path setup -#msvc.msvc_setup_env_once=lambda env: False -#msvc.msvc_exists=lambda: True - -scriptSwitchByTargetArch={ - 'x86':'/x86', - 'x86_64':'/x64', - 'amd64':'/x64', - 'ia64':'/ia64', -} - -def fetchSDKVars(targetArch,versionString): - common.debug("windowsSdk.py, fetchSDKVars: Searching for SDK %s"%versionString) - archSwitch=scriptSwitchByTargetArch.get(targetArch) - if not archSwitch: - common.debug("windowsSdk.py, fetchSDKVars: Unsupported target arch: %s"%targetArch) - try: - versionKey=_winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE,r'SOFTWARE\Microsoft\Microsoft SDKs\Windows\%s'%versionString) - except Exception as e: - common.debug("windowsSdk.py, fetchSDKVars: failed to open registry key for version %s: %s"%(versionString,e)) - return - try: - installDir=_winreg.QueryValueEx(versionKey,"InstallationFolder")[0] - except Exception as e: - common.debug("windowsSdk.py, fetchSDKVars: no InstallationFolder value in registry key: %s: %s"%(v,e)) - return - if versionString=='v7.1A': - #V7.1A (comes with vc2012) does not come with a batch file - d=dict(PATH=os.path.join(installDir,'bin'),INCLUDE=os.path.join(installDir,'include'),LIB=os.path.join(installDir,'lib')) - if targetArch in ('x86_64','amd64'): - d['PATH']=os.path.join(d['PATH'],'x64') - d['LIB']=os.path.join(d['LIB'],'x64') - return d - scriptPath=os.path.join(installDir,os.path.join('bin','setenv.cmd')) - if not os.path.isfile(scriptPath): - common.debug("windowsSdk.py, fetchSDKVars: Script %s does not exist"%scriptPath) - return - p=subprocess.Popen(['cmd','/V','/c',scriptPath,archSwitch,'&&','set'],stdout=subprocess.PIPE,stderr=subprocess.PIPE) - stdout,stderr=p.communicate() - try: - return common.parse_output(stdout) - except Exception as e: - common.debug("windowsSdk.py, fetchSDKVars: Error parsing script output: %s"%e) - return - common.debug("windowsSdk.py, fetchSDKVars: No suitable SDK could be used") - -def exists(env): - return True - -def generate(env): - targetArch=env.get('TARGET_ARCH','x86') - d=fetchSDKVars(targetArch,'v7.1A') - if d: - env.Append(CPPDEFINES='_USING_V110_SDK71_') - env.Append(CCFLAGS='/wd4091') - if targetArch.endswith('64'): - env.Append(LINKFLAGS=[env['LINKFLAGS'],'/SUBSYSTEM:WINDOWS,5.02']) - else: - # #3730: VC2012 uses SSE2 by default, but NVDA is still run on some older processers (AMD Athlon etc) which don't support this. - env.Append(CCFLAGS='/arch:IA32') - env.Append(LINKFLAGS=[env['LINKFLAGS'],'/SUBSYSTEM:WINDOWS,5.01']) - if not d: - common.debug("windowsSdk.py, Generate: No suitable SDK could be used") - raise RuntimeError("Windows SDK 7.1A could not be found") - #msvc.generate(env) - for k, v in d.iteritems(): - env.PrependENVPath(k,v,delete_existing=True) - From 44d76529f788ef161c6311bc53cef7fc7213c20a Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Wed, 6 Sep 2017 08:47:14 +1000 Subject: [PATCH 02/25] Prototype appx package by running scons appx --- appx/appx_images/nvda_150x150.png | Bin 0 -> 4396 bytes appx/appx_images/nvda_44x44.png | Bin 0 -> 1376 bytes appx/manifest.xml.subst | 47 +++++++ appx/sconscript | 61 +++++++++ sconstruct | 5 +- source/config/__init__.py | 4 +- source/nvda_appx.pyw | 214 ++++++++++++++++++++++++++++++ source/setup.py | 11 ++ 8 files changed, 340 insertions(+), 2 deletions(-) create mode 100644 appx/appx_images/nvda_150x150.png create mode 100644 appx/appx_images/nvda_44x44.png create mode 100644 appx/manifest.xml.subst create mode 100644 appx/sconscript create mode 100644 source/nvda_appx.pyw diff --git a/appx/appx_images/nvda_150x150.png b/appx/appx_images/nvda_150x150.png new file mode 100644 index 0000000000000000000000000000000000000000..90ef87f73a36c0c850e4cec61796ad23f5b0fb17 GIT binary patch literal 4396 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 + + + + %productName% + %publisher% + %description% + appx_images/nvda_150x150.png + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/appx/sconscript b/appx/sconscript new file mode 100644 index 00000000000..554959adcba --- /dev/null +++ b/appx/sconscript @@ -0,0 +1,61 @@ +import subprocess +import versionInfo + +Import(['env']) + +def getCertPublisher(env): + certFile=env.get('certFile') + if not certFile: + return env['publisher'] + certPassword=env.get('certPassword') + cmd=['certutil','-dump'] + if certPassword: + cmd.extend(['-p',certPassword]) + cmd.append(File('#'+certFile).abspath.replace('/','\\')) + lines=subprocess.check_output(cmd).splitlines() + linePrefix='Subject: ' + for line in lines: + if line.startswith(linePrefix): + subject=line[len(linePrefix):].rstrip() + return subject + +signExec=env['signExec'] if env['certFile'] else None +certPublisher=getCertPublisher(env) + +# Make a copy of the dist dir produced by py2exe +# And also place some extra appx specific images in there +appxContent=env.Command( + target='content', + source=[Dir("#dist"),Dir('#appx/appx_images')], + action=[ + Delete("$TARGET"), + Copy("$TARGET","${SOURCES[0]}"), + Copy("${TARGET}\\appx_images","${SOURCES[1]}"), + ] +) +# Ensure that it is always copied as we can't tell if dist changed +env.AlwaysBuild(appxContent) +# Create a preconfig xml file for making the appx +priConfig=env.Command(File('priConfig.xml'),appxContent,"makepri createconfig /cf $TARGET /dq lang-en-US >NUL",CHDIR=appxContent) +env.Depends(priConfig,appxContent) +# Create an appx manifest with version and publisher etc all filled in +manifest=env.Substfile( + 'manifest.xml', + 'manifest.xml.subst', + SUBST_DICT={ + '%name%':versionInfo.name, + '%version%':"%s.%s.%s.%s"%(versionInfo.version_year,versionInfo.version_major,versionInfo.version_minor,env['version_build']), + '%certPublisher%':certPublisher, + '%publisher%':env['publisher'], + '%productName%':"%s (%s)"%(versionInfo.name,versionInfo.longName), + '%description%':versionInfo.description, + }, +) +# Create a pri file indexing all thiles to be included in the appx +priFile,mapFile=env.Command(['appx.pri','appx.map.txt'],[appxContent,priConfig,manifest],"makepri new /v /pr ${SOURCES[0]} /cf ${SOURCES[1]} /manifest ${SOURCES[2]} /mf appx /of ${TARGETS[0]} /o >NUL") +# Package the appx +appx=env.Command(File('#output\\nvda.appx'),[manifest,mapFile],"makeappx pack /p $TARGET /m ${SOURCES[0]} /f ${SOURCES[1]} >${TARGET}.txt") +if signExec: + env.AddPostAction(appx,[signExec]) +env.Depends(appx,appxContent) +env.Alias('appx',appx) diff --git a/sconstruct b/sconstruct index 4a8817f63c7..02ab59328d1 100755 --- a/sconstruct +++ b/sconstruct @@ -119,10 +119,11 @@ Export('sourceLibDir64') buildDir = Dir("build") outFilePrefix = "nvda{type}_{version}".format(type="" if release else "_snapshot", version=version) outputDir=Dir(env['outputDir']) +Export('outputDir') devDocsOutputDir=outputDir.Dir('devDocs') # An action to sign an executable with certFile. -signExecCmd = ["signtool", "sign", "/f", certFile] +signExecCmd = ["signtool", "sign", "/fd", "SHA256", "/f", certFile] if certPassword: signExecCmd.extend(("/p", certPassword)) if certTimestampServer: @@ -393,6 +394,8 @@ symbolsList.extend(env.Glob(os.path.join(sourceLibDir64.path,'*.pdb'))) symbolsArchive = env.ZipArchive(outputDir.File("%s_debugSymbols.zip" % outFilePrefix), symbolsList) env.Alias("symbolsArchive", symbolsArchive) +env.SConscript("appx/sconscript",exports=["env"],variant_dir='build\\appx') + env.Default(dist) env.SConscript("tests/sconscript", exports=["env", "sourceDir", "pot"]) diff --git a/source/config/__init__.py b/source/config/__init__.py index 1832ad6e677..3e3c921e604 100644 --- a/source/config/__init__.py +++ b/source/config/__init__.py @@ -30,6 +30,8 @@ import profileUpgrader from .configSpec import confspec +isAppX=False + #: The active configuration, C{None} if it has not yet been loaded. #: @type: ConfigObj conf = None @@ -91,7 +93,7 @@ def getUserDefaultConfigPath(useInstalledPathIfExists=False): Most callers will want the C{globalVars.appArgs.configPath variable} instead. """ installedUserConfigPath=getInstalledUserConfigPath() - if installedUserConfigPath and (isInstalledCopy() or (useInstalledPathIfExists and os.path.isdir(installedUserConfigPath))): + if installedUserConfigPath and (isInstalledCopy() or isAppX or (useInstalledPathIfExists and os.path.isdir(installedUserConfigPath))): return installedUserConfigPath return u'.\\userConfig\\' diff --git a/source/nvda_appx.pyw b/source/nvda_appx.pyw new file mode 100644 index 00000000000..82090d1a17d --- /dev/null +++ b/source/nvda_appx.pyw @@ -0,0 +1,214 @@ +#nvda.pyw +#A part of NonVisual Desktop Access (NVDA) +#Copyright (C) 2006-2017 NV Access Limited, Aleksey Sadovoy, Babbage B.V. +#This file is covered by the GNU General Public License. +#See the file COPYING for more details. + +"""The NVDA launcher. It can handle some command-line arguments (including help). It sets up logging, and then starts the core.""" + +import sys +import os + +if getattr(sys, "frozen", None): + # We are running as an executable. + # Append the path of the executable to sys so we can import modules from the dist dir. + sys.path.append(sys.prefix) + os.chdir(sys.prefix) +else: + import sourceEnv + #We should always change directory to the location of this module (nvda.pyw), don't rely on sys.path[0] + os.chdir(os.path.normpath(os.path.dirname(__file__))) + +import pythonMonkeyPatches + +import ctypes +import locale +import gettext +import time +import argparse +import win32con +import globalVars +import config +import logHandler +from logHandler import log +import winUser +import winKernel + +class NoConsoleOptionParser(argparse.ArgumentParser): + """A commandline option parser that shows its messages using dialogs, as this pyw file has no dos console window associated with it""" + + def print_help(self, file=None): + """Shows help in a standard Windows message dialog""" + winUser.MessageBox(0, unicode(self.format_help()), u"Help", 0) + + def error(self, message): + """Shows an error in a standard Windows message dialog, and then exits NVDA""" + out = "" + out = self.format_usage() + out += "\nerror: %s" % message + winUser.MessageBox(0, unicode(out), u"Error", 0) + sys.exit(2) + +globalVars.startTime=time.time() + +#Localization settings +locale.setlocale(locale.LC_ALL,'') +try: + gettext.translation('nvda',localedir='locale',languages=[locale.getlocale()[0]]).install(True) +except: + gettext.install('nvda',unicode=True) + +# Check OS version requirements +import winVersion +if not winVersion.isSupportedOS(): + winUser.MessageBox(0, unicode(ctypes.FormatError(winUser.ERROR_OLD_WIN_VERSION)), None, winUser.MB_ICONERROR) + sys.exit(1) + +def decodeMbcs(string): + """Decode a multi-byte character set string""" + return string.decode("mbcs") + +#Process option arguments +parser=NoConsoleOptionParser() +quitGroup = parser.add_mutually_exclusive_group() +quitGroup.add_argument('-q','--quit',action="store_true",dest='quit',default=False,help="Quit already running copy of NVDA") +quitGroup.add_argument('-r','--replace',action="store_true",dest='replace',default=False,help="Quit already running copy of NVDA and start this one") +parser.add_argument('-k','--check-running',action="store_true",dest='check_running',default=False,help="Report whether NVDA is running via the exit code; 0 if running, 1 if not running") +parser.add_argument('-f','--log-file',dest='logFileName',type=decodeMbcs,help="The file where log messages should be written to") +parser.add_argument('-l','--log-level',dest='logLevel',type=int,default=0,choices=[10,20,30,40,50],help="The lowest level of message logged (debug 10, info 20, warning 30, error 40, critical 50), default is warning") +parser.add_argument('-c','--config-path',dest='configPath',default=None,type=decodeMbcs,help="The path where all settings for NVDA are stored") +parser.add_argument('-m','--minimal',action="store_true",dest='minimal',default=False,help="No sounds, no interface, no start message etc") +parser.add_argument('-s','--secure',action="store_true",dest='secure',default=False,help="Secure mode (disable Python console)") +parser.add_argument('--disable-addons',action="store_true",dest='disableAddons',default=False,help="Disable all add-ons") +parser.add_argument('--debug-logging',action="store_true",dest='debugLogging',default=False,help="Enable debug level logging just for this run. This setting will override any other log level (--loglevel, -l) argument given.") +parser.add_argument('--no-sr-flag',action="store_false",dest='changeScreenReaderFlag',default=True,help="Don't change the global system screen reader flag") +installGroup = parser.add_mutually_exclusive_group() +installGroup.add_argument('--install',action="store_true",dest='install',default=False,help="Installs NVDA (starting the new copy after installation)") +installGroup.add_argument('--install-silent',action="store_true",dest='installSilent',default=False,help="Installs NVDA silently (does not start the new copy after installation).") +parser.add_argument('--launcher',action="store_true",dest='launcher',default=False,help="Started from the launcher") +# This option currently doesn't actually do anything. +# It is passed by Ease of Access so that if someone downgrades without uninstalling (despite our discouragement), +# the downgraded copy won't be started in non-secure mode on secure desktops. +# (Older versions always required the --secure option to start in secure mode.) +# If this occurs, the user will see an obscure error, +# but that's far better than a major security hazzard. +parser.add_argument('--ease-of-access',action="store_true",dest='easeOfAccess',default=False,help="Started by Windows Ease of Access") +(globalVars.appArgs,globalVars.appArgsExtra)=parser.parse_known_args() + +def terminateRunningNVDA(window): + processID,threadID=winUser.getWindowThreadProcessID(window) + winUser.PostMessage(window,win32con.WM_QUIT,0,0) + h=winKernel.openProcess(winKernel.SYNCHRONIZE,False,processID) + if not h: + # The process is already dead. + return + try: + res=winKernel.waitForSingleObject(h,4000) + if res==0: + # The process terminated within the timeout period. + return + finally: + winKernel.closeHandle(h) + + # The process is refusing to exit gracefully, so kill it forcefully. + h = winKernel.openProcess(winKernel.PROCESS_TERMINATE | winKernel.SYNCHRONIZE, False, processID) + if not h: + raise OSError("Could not open process for termination") + try: + winKernel.TerminateProcess(h, 1) + winKernel.waitForSingleObject(h, 2000) + finally: + winKernel.closeHandle(h) + +#Handle running multiple instances of NVDA +try: + oldAppWindowHandle=winUser.FindWindow(u'wxWindowClassNR',u'NVDA') +except: + oldAppWindowHandle=0 +if not winUser.isWindow(oldAppWindowHandle): + oldAppWindowHandle=0 +if oldAppWindowHandle and (globalVars.appArgs.quit or globalVars.appArgs.replace): + try: + terminateRunningNVDA(oldAppWindowHandle) + except: + sys.exit(1) +if globalVars.appArgs.quit or (oldAppWindowHandle and not globalVars.appArgs.replace): + sys.exit(0) +elif globalVars.appArgs.check_running: + # NVDA is not running. + sys.exit(1) + +UOI_NAME = 2 +def getDesktopName(): + desktop = ctypes.windll.user32.GetThreadDesktop(ctypes.windll.kernel32.GetCurrentThreadId()) + name = ctypes.create_unicode_buffer(256) + ctypes.windll.user32.GetUserObjectInformationW(desktop, UOI_NAME, ctypes.byref(name), ctypes.sizeof(name), None) + return name.value + +#Ensure multiple instances are not fully started by using a mutex +ERROR_ALREADY_EXISTS=0XB7 +desktopName=getDesktopName() +mutex=ctypes.windll.kernel32.CreateMutexW(None,True,u"Local\\NVDA_%s"%desktopName) +if not mutex or ctypes.windll.kernel32.GetLastError()==ERROR_ALREADY_EXISTS: + if mutex: ctypes.windll.kernel32.CloseHandle(mutex) + sys.exit(1) + +isSecureDesktop = desktopName == "Winlogon" +if isSecureDesktop: + import _winreg + try: + k = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, ur"SOFTWARE\NVDA") + if not _winreg.QueryValueEx(k, u"serviceDebug")[0]: + globalVars.appArgs.secure = True + except WindowsError: + globalVars.appArgs.secure = True + globalVars.appArgs.changeScreenReaderFlag = False + globalVars.appArgs.minimal = True + globalVars.appArgs.configPath = os.path.join(sys.prefix, "systemConfig") + +#os.environ['PYCHECKER']="--limit 10000 -q --changetypes" +#import pychecker.checker + +#Initial logging and logging code + +logLevel=globalVars.appArgs.logLevel +if logLevel<=0: + logLevel=log.INFO +if globalVars.appArgs.debugLogging: + logLevel=log.DEBUG +logHandler.initialize() +logHandler.log.setLevel(logLevel) + +log.info("Starting NVDA") +log.debug("Debug level logging enabled") +if globalVars.appArgs.changeScreenReaderFlag: + winUser.setSystemScreenReaderFlag(True) +#Accept wm_quit from other processes, even if running with higher privilages +try: + if not ctypes.windll.user32.ChangeWindowMessageFilter(win32con.WM_QUIT,1): + raise WinError() +except AttributeError: + pass +# Make this the last application to be shut down and don't display a retry dialog box. +winKernel.SetProcessShutdownParameters(0x100, winKernel.SHUTDOWN_NORETRY) +if False: #not isSecureDesktop: + import easeOfAccess + easeOfAccess.notify(3) +import config +config.isAppX=True +try: + import core + winUser.MessageBox(0, u"Launching NVDA", u"NVDA", 0) + core.main() +except: + log.critical("core failure",exc_info=True) + sys.exit(1) +finally: + if False: #not isSecureDesktop: + easeOfAccess.notify(2) + if globalVars.appArgs.changeScreenReaderFlag: + winUser.setSystemScreenReaderFlag(False) + ctypes.windll.kernel32.CloseHandle(mutex) + +log.info("NVDA exit") +sys.exit(globalVars.exitCode) diff --git a/source/setup.py b/source/setup.py index ecc4820e38f..523c1b1cc0e 100755 --- a/source/setup.py +++ b/source/setup.py @@ -198,6 +198,17 @@ def getRecursiveDataFiles(dest,source,excludes=()): "copyright": copyright, "company_name": publisher, }, + { + "script":"nvda_appx.pyw", + "dest_base":"nvda_appx", + "uac_info": ("asInvoker", False), + "icon_resources":[(1,"images/nvda.ico")], + "version":"%s.%s.%s.%s"%(version_year,version_major,version_minor,version_build), + "description":"NVDA application", + "product_version":version, + "copyright":copyright, + "company_name":publisher, + }, ], service=[{ "modules": ["nvda_service"], From 684b0ebd7bc1c45c8a3cfdb4bbfb4baf05c7b768 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 7 Sep 2017 11:15:58 +1000 Subject: [PATCH 03/25] appveyor should also build the appx package --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 1bdbc9f68c9..dd177ac66f5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -56,7 +56,7 @@ install: build_script: - ps: | - $sconsOutTargets = "launcher" + $sconsOutTargets = "launcher appx" $sconsArgs = "version=$env:version" if ($env:release) { $sconsOutTargets += " changes userGuide developerGuide" From 3f9f5cdf6d20b6ef5df7866d5e30f87ab03450c1 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 7 Sep 2017 11:28:33 +1000 Subject: [PATCH 04/25] test appveyor 1 --- appx/sconscript | 1 + 1 file changed, 1 insertion(+) diff --git a/appx/sconscript b/appx/sconscript index 554959adcba..be4c3c6146f 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -12,6 +12,7 @@ def getCertPublisher(env): if certPassword: cmd.extend(['-p',certPassword]) cmd.append(File('#'+certFile).abspath.replace('/','\\')) + print cmd lines=subprocess.check_output(cmd).splitlines() linePrefix='Subject: ' for line in lines: From f83d093ee4ba729af616af6c078513f818f44d18 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 7 Sep 2017 11:42:40 +1000 Subject: [PATCH 05/25] test appveyor 2 --- appx/sconscript | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/appx/sconscript b/appx/sconscript index be4c3c6146f..3730c0904c7 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -7,11 +7,8 @@ def getCertPublisher(env): certFile=env.get('certFile') if not certFile: return env['publisher'] - certPassword=env.get('certPassword') - cmd=['certutil','-dump'] - if certPassword: - cmd.extend(['-p',certPassword]) - cmd.append(File('#'+certFile).abspath.replace('/','\\')) + certPassword=env.get('certPassword','') + cmd=['certutil','-dump','-p',certPassword,File('#'+certFile).abspath.replace('/','\\')] print cmd lines=subprocess.check_output(cmd).splitlines() linePrefix='Subject: ' From 63b7259cc6544bfa080ea37ef158db02c5d5e4f1 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 7 Sep 2017 11:59:43 +1000 Subject: [PATCH 06/25] test appveyor 3 --- appx/sconscript | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/appx/sconscript b/appx/sconscript index 3730c0904c7..8d2404289c4 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -50,9 +50,9 @@ manifest=env.Substfile( }, ) # Create a pri file indexing all thiles to be included in the appx -priFile,mapFile=env.Command(['appx.pri','appx.map.txt'],[appxContent,priConfig,manifest],"makepri new /v /pr ${SOURCES[0]} /cf ${SOURCES[1]} /manifest ${SOURCES[2]} /mf appx /of ${TARGETS[0]} /o >NUL") +priFile,mapFile=env.Command(['appx.pri','appx.map.txt'],[appxContent,priConfig,manifest],"makepri new /v /pr ${SOURCES[0]} /cf ${SOURCES[1]} /manifest ${SOURCES[2]} /mf appx /of ${TARGETS[0]} /o") # Package the appx -appx=env.Command(File('#output\\nvda.appx'),[manifest,mapFile],"makeappx pack /p $TARGET /m ${SOURCES[0]} /f ${SOURCES[1]} >${TARGET}.txt") +appx=env.Command(File('#output\\nvda.appx'),[manifest,mapFile],"makeappx pack /p $TARGET /m ${SOURCES[0]} /f ${SOURCES[1]}") if signExec: env.AddPostAction(appx,[signExec]) env.Depends(appx,appxContent) From 95b256d5a953472788bc75bb72a04d460426e434 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 7 Sep 2017 14:32:58 +1000 Subject: [PATCH 07/25] test appveyor 4 --- appx/sconscript | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/appx/sconscript b/appx/sconscript index 8d2404289c4..88d078d4d0d 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -3,13 +3,16 @@ import versionInfo Import(['env']) +def PrintUnicodeFileAction(target,source,env): + output=open(target[0].path+'.unicode','rb').read().decode('u16',errors='replace').encode('utf8',errors='replace') + print output + def getCertPublisher(env): certFile=env.get('certFile') if not certFile: return env['publisher'] certPassword=env.get('certPassword','') cmd=['certutil','-dump','-p',certPassword,File('#'+certFile).abspath.replace('/','\\')] - print cmd lines=subprocess.check_output(cmd).splitlines() linePrefix='Subject: ' for line in lines: @@ -44,13 +47,20 @@ manifest=env.Substfile( '%name%':versionInfo.name, '%version%':"%s.%s.%s.%s"%(versionInfo.version_year,versionInfo.version_major,versionInfo.version_minor,env['version_build']), '%certPublisher%':certPublisher, - '%publisher%':env['publisher'], + '%publisher%':'NV Access', #env['publisher'], '%productName%':"%s (%s)"%(versionInfo.name,versionInfo.longName), '%description%':versionInfo.description, }, ) # Create a pri file indexing all thiles to be included in the appx -priFile,mapFile=env.Command(['appx.pri','appx.map.txt'],[appxContent,priConfig,manifest],"makepri new /v /pr ${SOURCES[0]} /cf ${SOURCES[1]} /manifest ${SOURCES[2]} /mf appx /of ${TARGETS[0]} /o") +priFile,mapFile=env.Command( + target=['appx.pri','appx.map.txt'], + source=[appxContent,priConfig,manifest], + action=[ + "makepri new /v /pr ${SOURCES[0]} /cf ${SOURCES[1]} /manifest ${SOURCES[2]} /mf appx /of ${TARGETS[0]} /o >${TARGET}.unicode", + PrintUnicodeFileAction, + ], +) # Package the appx appx=env.Command(File('#output\\nvda.appx'),[manifest,mapFile],"makeappx pack /p $TARGET /m ${SOURCES[0]} /f ${SOURCES[1]}") if signExec: From 11d8d0fbfc8a363cfcd2219713f42e8e4a6ed45f Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 7 Sep 2017 14:42:21 +1000 Subject: [PATCH 08/25] test appveyor 5 --- appx/sconscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appx/sconscript b/appx/sconscript index 88d078d4d0d..6b518d39081 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -57,7 +57,7 @@ priFile,mapFile=env.Command( target=['appx.pri','appx.map.txt'], source=[appxContent,priConfig,manifest], action=[ - "makepri new /v /pr ${SOURCES[0]} /cf ${SOURCES[1]} /manifest ${SOURCES[2]} /mf appx /of ${TARGETS[0]} /o >${TARGET}.unicode", + "-makepri new /v /pr ${SOURCES[0]} /cf ${SOURCES[1]} /manifest ${SOURCES[2]} /mf appx /of ${TARGETS[0]} /o >${TARGET}.unicode", PrintUnicodeFileAction, ], ) From f8e51636cfcd3057db800a5bdf4f3ee620ddbfd1 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 7 Sep 2017 14:54:18 +1000 Subject: [PATCH 09/25] test appveyor 6 --- appx/sconscript | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/appx/sconscript b/appx/sconscript index 6b518d39081..15f8bd590f8 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -18,6 +18,7 @@ def getCertPublisher(env): for line in lines: if line.startswith(linePrefix): subject=line[len(linePrefix):].rstrip() + print subject return subject signExec=env['signExec'] if env['certFile'] else None @@ -41,7 +42,7 @@ priConfig=env.Command(File('priConfig.xml'),appxContent,"makepri createconfig /c env.Depends(priConfig,appxContent) # Create an appx manifest with version and publisher etc all filled in manifest=env.Substfile( - 'manifest.xml', + File('#output/manifest.xml'), 'manifest.xml.subst', SUBST_DICT={ '%name%':versionInfo.name, @@ -66,4 +67,4 @@ appx=env.Command(File('#output\\nvda.appx'),[manifest,mapFile],"makeappx pack /p if signExec: env.AddPostAction(appx,[signExec]) env.Depends(appx,appxContent) -env.Alias('appx',appx) +env.Alias('appx',manifest) From e54b02ae5bf8b5e02adf0c422a85015520164182 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 7 Sep 2017 15:20:26 +1000 Subject: [PATCH 10/25] test appveyor 7 --- appx/sconscript | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/appx/sconscript b/appx/sconscript index 15f8bd590f8..a1286b2cdb7 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -42,13 +42,13 @@ priConfig=env.Command(File('priConfig.xml'),appxContent,"makepri createconfig /c env.Depends(priConfig,appxContent) # Create an appx manifest with version and publisher etc all filled in manifest=env.Substfile( - File('#output/manifest.xml'), + "manifest.xml", 'manifest.xml.subst', SUBST_DICT={ '%name%':versionInfo.name, '%version%':"%s.%s.%s.%s"%(versionInfo.version_year,versionInfo.version_major,versionInfo.version_minor,env['version_build']), '%certPublisher%':certPublisher, - '%publisher%':'NV Access', #env['publisher'], + '%publisher%':env['publisher'], '%productName%':"%s (%s)"%(versionInfo.name,versionInfo.longName), '%description%':versionInfo.description, }, @@ -63,8 +63,8 @@ priFile,mapFile=env.Command( ], ) # Package the appx -appx=env.Command(File('#output\\nvda.appx'),[manifest,mapFile],"makeappx pack /p $TARGET /m ${SOURCES[0]} /f ${SOURCES[1]}") +appx=env.Command(File('#output\\nvda.appx'),[manifest,appxContent],"makeappx pack /p $TARGET /m ${SOURCES[0]} /d ${SOURCES[1]}") if signExec: env.AddPostAction(appx,[signExec]) env.Depends(appx,appxContent) -env.Alias('appx',manifest) +env.Alias('appx',appx) From 14c2ef9c3e205f2c65c01106a4260602baf329b9 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 7 Sep 2017 15:49:32 +1000 Subject: [PATCH 11/25] test appveyor 8 --- appx/sconscript | 33 +++++++++++++++++---------------- 1 file changed, 17 insertions(+), 16 deletions(-) diff --git a/appx/sconscript b/appx/sconscript index a1286b2cdb7..aab994834a5 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -24,15 +24,29 @@ def getCertPublisher(env): signExec=env['signExec'] if env['certFile'] else None certPublisher=getCertPublisher(env) +# Create an appx manifest with version and publisher etc all filled in +manifest=env.Substfile( + "AppxManifest.xml", + 'manifest.xml.subst', + SUBST_DICT={ + '%name%':versionInfo.name, + '%version%':"%s.%s.%s.%s"%(versionInfo.version_year,versionInfo.version_major,versionInfo.version_minor,env['version_build']), + '%certPublisher%':certPublisher, + '%publisher%':env['publisher'], + '%productName%':"%s (%s)"%(versionInfo.name,versionInfo.longName), + '%description%':versionInfo.description, + }, +) # Make a copy of the dist dir produced by py2exe # And also place some extra appx specific images in there appxContent=env.Command( target='content', - source=[Dir("#dist"),Dir('#appx/appx_images')], + source=[Dir("#dist"),Dir('#appx/appx_images'),manifest], action=[ Delete("$TARGET"), Copy("$TARGET","${SOURCES[0]}"), Copy("${TARGET}\\appx_images","${SOURCES[1]}"), + Copy("${TARGET}\\AppxManifest.xml","${SOURCES[2]}"), ] ) # Ensure that it is always copied as we can't tell if dist changed @@ -40,19 +54,6 @@ env.AlwaysBuild(appxContent) # Create a preconfig xml file for making the appx priConfig=env.Command(File('priConfig.xml'),appxContent,"makepri createconfig /cf $TARGET /dq lang-en-US >NUL",CHDIR=appxContent) env.Depends(priConfig,appxContent) -# Create an appx manifest with version and publisher etc all filled in -manifest=env.Substfile( - "manifest.xml", - 'manifest.xml.subst', - SUBST_DICT={ - '%name%':versionInfo.name, - '%version%':"%s.%s.%s.%s"%(versionInfo.version_year,versionInfo.version_major,versionInfo.version_minor,env['version_build']), - '%certPublisher%':certPublisher, - '%publisher%':env['publisher'], - '%productName%':"%s (%s)"%(versionInfo.name,versionInfo.longName), - '%description%':versionInfo.description, - }, -) # Create a pri file indexing all thiles to be included in the appx priFile,mapFile=env.Command( target=['appx.pri','appx.map.txt'], @@ -63,8 +64,8 @@ priFile,mapFile=env.Command( ], ) # Package the appx -appx=env.Command(File('#output\\nvda.appx'),[manifest,appxContent],"makeappx pack /p $TARGET /m ${SOURCES[0]} /d ${SOURCES[1]}") +appx=env.Command(File('#output\\nvda.appx'),[manifest,appxContent],"makeappx pack /p $TARGET /d ${SOURCES[1]}") if signExec: env.AddPostAction(appx,[signExec]) -env.Depends(appx,appxContent) +#env.Depends(appx,appxContent) env.Alias('appx',appx) From f2cc864e0c216aeec926297e56d4f65115c780a3 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 7 Sep 2017 16:00:51 +1000 Subject: [PATCH 12/25] test appveyor 9 --- appx/sconscript | 19 +------------------ 1 file changed, 1 insertion(+), 18 deletions(-) diff --git a/appx/sconscript b/appx/sconscript index aab994834a5..dea644e36b5 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -3,10 +3,6 @@ import versionInfo Import(['env']) -def PrintUnicodeFileAction(target,source,env): - output=open(target[0].path+'.unicode','rb').read().decode('u16',errors='replace').encode('utf8',errors='replace') - print output - def getCertPublisher(env): certFile=env.get('certFile') if not certFile: @@ -18,7 +14,6 @@ def getCertPublisher(env): for line in lines: if line.startswith(linePrefix): subject=line[len(linePrefix):].rstrip() - print subject return subject signExec=env['signExec'] if env['certFile'] else None @@ -51,20 +46,8 @@ appxContent=env.Command( ) # Ensure that it is always copied as we can't tell if dist changed env.AlwaysBuild(appxContent) -# Create a preconfig xml file for making the appx -priConfig=env.Command(File('priConfig.xml'),appxContent,"makepri createconfig /cf $TARGET /dq lang-en-US >NUL",CHDIR=appxContent) -env.Depends(priConfig,appxContent) -# Create a pri file indexing all thiles to be included in the appx -priFile,mapFile=env.Command( - target=['appx.pri','appx.map.txt'], - source=[appxContent,priConfig,manifest], - action=[ - "-makepri new /v /pr ${SOURCES[0]} /cf ${SOURCES[1]} /manifest ${SOURCES[2]} /mf appx /of ${TARGETS[0]} /o >${TARGET}.unicode", - PrintUnicodeFileAction, - ], -) # Package the appx -appx=env.Command(File('#output\\nvda.appx'),[manifest,appxContent],"makeappx pack /p $TARGET /d ${SOURCES[1]}") +appx=env.Command(File('#output\\nvda_%s.appx'%env['version']),appxContent,"makeappx pack /p $TARGET /d $SOURCE") if signExec: env.AddPostAction(appx,[signExec]) #env.Depends(appx,appxContent) From 5c92d79d6e71dda80dd11f7940ca18b333e96b3d Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 7 Sep 2017 16:08:17 +1000 Subject: [PATCH 13/25] Test appveyor 10 --- appx/sconscript | 4 ++-- sconstruct | 1 + source/nvda_appx.pyw | 1 - 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/appx/sconscript b/appx/sconscript index dea644e36b5..00322532226 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -1,7 +1,7 @@ import subprocess import versionInfo -Import(['env']) +Import(['env','outFilePrefix']) def getCertPublisher(env): certFile=env.get('certFile') @@ -47,7 +47,7 @@ appxContent=env.Command( # Ensure that it is always copied as we can't tell if dist changed env.AlwaysBuild(appxContent) # Package the appx -appx=env.Command(File('#output\\nvda_%s.appx'%env['version']),appxContent,"makeappx pack /p $TARGET /d $SOURCE") +appx=env.Command(File('#output\\%s.appx'%outFilePrefix),appxContent,"makeappx pack /p $TARGET /d $SOURCE") if signExec: env.AddPostAction(appx,[signExec]) #env.Depends(appx,appxContent) diff --git a/sconstruct b/sconstruct index 02ab59328d1..fc3b663d1e2 100755 --- a/sconstruct +++ b/sconstruct @@ -118,6 +118,7 @@ sourceLibDir64=sourceDir.Dir('lib64') Export('sourceLibDir64') buildDir = Dir("build") outFilePrefix = "nvda{type}_{version}".format(type="" if release else "_snapshot", version=version) +Export('outFilePrefix') outputDir=Dir(env['outputDir']) Export('outputDir') devDocsOutputDir=outputDir.Dir('devDocs') diff --git a/source/nvda_appx.pyw b/source/nvda_appx.pyw index 82090d1a17d..bff28265dd2 100644 --- a/source/nvda_appx.pyw +++ b/source/nvda_appx.pyw @@ -198,7 +198,6 @@ import config config.isAppX=True try: import core - winUser.MessageBox(0, u"Launching NVDA", u"NVDA", 0) core.main() except: log.critical("core failure",exc_info=True) From 19b2ec6905466a7a00aeb80ed89518988247a686 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 14 Sep 2017 15:44:49 +1000 Subject: [PATCH 14/25] Disable more features that are incompatible with Windows Store policy --- appx/manifest.xml.subst | 4 +- appx/sconscript | 14 ++- source/NVDAHelper.py | 21 ++-- source/addonHandler.py | 3 + source/config/__init__.py | 15 ++- source/globalCommands.py | 2 +- source/gui/__init__.py | 6 +- source/gui/addonGui.py | 9 ++ source/nvda.pyw | 14 ++- source/nvda_appx.pyw | 213 -------------------------------------- source/setup.py | 11 -- source/updateCheck.py | 3 + 12 files changed, 72 insertions(+), 243 deletions(-) delete mode 100644 source/nvda_appx.pyw diff --git a/appx/manifest.xml.subst b/appx/manifest.xml.subst index 9952fe00c3f..7b6fce09d09 100644 --- a/appx/manifest.xml.subst +++ b/appx/manifest.xml.subst @@ -13,7 +13,7 @@ %productName% %publisher% %description% - appx_images/nvda_150x150.png + appx_images/nvda_44x44.png @@ -31,7 +31,7 @@ MaxVersionTested="10.0.16278.100" Date: Thu, 14 Sep 2017 16:12:16 +1000 Subject: [PATCH 15/25] More fixes --- appx/sconscript | 8 +++++++- source/gui/__init__.py | 7 ++++--- source/gui/addonGui.py | 2 +- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/appx/sconscript b/appx/sconscript index f23d1587974..7eb7d466105 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -26,8 +26,14 @@ excludedDistFiles=[ 'nvda_slave.exe', 'nvda_uiAccess.exe', 'lib/IAccessible2Proxy.dll', + 'lib/minHook.dll', 'lib/NVDAHelperRemote.dll', - 'lib/VBufBackend_*.dll', + 'lib/VBufBackend_adobeAcrobat.dll', + 'lib/VBufBackend_adobeFlash.dll', + 'lib/VBufBackend_gecko_ia2.dll', + 'lib/VBufBackend_lotusNotesRichText.dll', + 'lib/VBufBackend_mshtml.dll', + 'lib/VBufBackend_webKit.dll', 'lib64', ] diff --git a/source/gui/__init__.py b/source/gui/__init__.py index cba0cfb0a7b..86f114d1904 100644 --- a/source/gui/__init__.py +++ b/source/gui/__init__.py @@ -409,9 +409,10 @@ def __init__(self, frame): # Translators: The label for the menu item to install NVDA on the computer. item = menu_tools.Append(wx.ID_ANY, _("&Install NVDA...")) self.Bind(wx.EVT_MENU, frame.onInstallCommand, item) - # Translators: The label for the menu item to reload plugins. - item = menu_tools.Append(wx.ID_ANY, _("Reload plugins")) - self.Bind(wx.EVT_MENU, frame.onReloadPluginsCommand, item) + if not config.isAppX: + # Translators: The label for the menu item to reload plugins. + item = menu_tools.Append(wx.ID_ANY, _("Reload plugins")) + self.Bind(wx.EVT_MENU, frame.onReloadPluginsCommand, item) # Translators: The label for the Tools submenu in NVDA menu. self.menu.AppendMenu(wx.ID_ANY, _("Tools"), menu_tools) diff --git a/source/gui/addonGui.py b/source/gui/addonGui.py index b74c54cd79b..d186260bb7d 100644 --- a/source/gui/addonGui.py +++ b/source/gui/addonGui.py @@ -298,7 +298,7 @@ def handleRemoteAddonInstall(cls, addonPath): # Add-ons cannot be installed into a Windows store version of NVDA if config.isAppX: # Translators: The message displayed when an add-on cannot be installed due to NVDA running as a Windows Store app - gui.messageBox(_("Add-ons cannot be installed in the Windows Store version of NVDA", + gui.messageBox(_("Add-ons cannot be installed in the Windows Store version of NVDA"), # Translators: The title of a dialog presented when an error occurs. _("Error"), wx.OK | wx.ICON_ERROR) From 5007b657efee28800e465ec953eb6de4df6ccde5 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 14 Sep 2017 20:04:04 +1000 Subject: [PATCH 16/25] Restart on Windows store updates --- source/core.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/source/core.py b/source/core.py index 5d360b76bf4..ce8822a04fa 100644 --- a/source/core.py +++ b/source/core.py @@ -225,8 +225,13 @@ def OnAssert(self,file,line,cond,msg): message="{file}, line {line}:\nassert {cond}: {msg}".format(file=file,line=line,cond=cond,msg=msg) log.debugWarning(message,codepath="WX Widgets",stack_info=True) app = App(redirect=False) - # We do support QueryEndSession events, but we don't want to do anything for them. - app.Bind(wx.EVT_QUERY_END_SESSION, lambda evt: None) + # We support queryEndSession events, but in general don't do anything for them. + # However, when running as a Windows Store application, we do want to request to be restarted for updates + def onQueryEndSession(evt): + if config.isAppX: + # Automatically restart NVDA on Windows Store update + ctypes.windll.kernel32.RegisterApplicationRestart(None,0) + app.Bind(wx.EVT_QUERY_END_SESSION, onQueryEndSession) def onEndSession(evt): # NVDA will be terminated as soon as this function returns, so save configuration if appropriate. config.saveOnExit() From d02dc13bbf1c85591da18e5d08b4f045f7cac9b0 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Wed, 20 Sep 2017 07:49:57 +1000 Subject: [PATCH 17/25] Change publisher to match Windows store, and set appx version so minor is build and revision is 0 --- appveyor.yml | 2 +- appx/sconscript | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index dd177ac66f5..f39f9dc1511 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -65,7 +65,7 @@ build_script: if ($env:versionType) { $sconsArgs += " updateVersionType=$env:versionType" } - $sconsArgs += ' publisher="NV Access"' + $sconsArgs += ' publisher="NV Access Limited"' $sconsArgs += " certFile=appveyor\authenticode.pfx certTimestampServer=http://timestamp.digicert.com" $sconsArgs += " version_build=$env:APPVEYOR_BUILD_NUMBER" # We use cmd to run scons because PowerShell throws exceptions if warnings get dumped to stderr. diff --git a/appx/sconscript b/appx/sconscript index 7eb7d466105..b4027fdd47d 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -43,7 +43,7 @@ manifest=env.Substfile( 'manifest.xml.subst', SUBST_DICT={ '%name%':versionInfo.name, - '%version%':"%s.%s.%s.%s"%(versionInfo.version_year,versionInfo.version_major,versionInfo.version_minor,env['version_build']), + '%version%':"%s.%s.%s.%s"%(versionInfo.version_year,versionInfo.version_major,env['version_build'],0), '%certPublisher%':certPublisher, '%publisher%':env['publisher'], '%productName%':"%s (%s)"%(versionInfo.name,versionInfo.longName), From 71bf553004612d7ebdd6946edb06765fdbd5f215 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Wed, 20 Sep 2017 08:24:53 +1000 Subject: [PATCH 18/25] Hardcode certain Windows Store publisher details, and don't sign the appx anymore (Windows Store does that). --- appx/manifest.xml.subst | 7 ++++--- appx/sconscript | 3 --- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/appx/manifest.xml.subst b/appx/manifest.xml.subst index 7b6fce09d09..1452d518e29 100644 --- a/appx/manifest.xml.subst +++ b/appx/manifest.xml.subst @@ -4,14 +4,15 @@ xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"> %productName% - %publisher% + NV access Limited %description% appx_images/nvda_44x44.png diff --git a/appx/sconscript b/appx/sconscript index b4027fdd47d..63d1edebd96 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -66,7 +66,4 @@ appxContent=env.Command( env.AlwaysBuild(appxContent) # Package the appx appx=env.Command(File('#output\\%s.appx'%outFilePrefix),appxContent,"makeappx pack /p $TARGET /d $SOURCE") -if signExec: - env.AddPostAction(appx,[signExec]) -#env.Depends(appx,appxContent) env.Alias('appx',appx) From 6feade1736eae9baeb3ba124c045843160d46971 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Wed, 20 Sep 2017 08:26:26 +1000 Subject: [PATCH 19/25] Change back the publisher to NV Access in appveyor.yml. It is now hardcoded separately in the appx manifest for windows store. --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index f39f9dc1511..dd177ac66f5 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -65,7 +65,7 @@ build_script: if ($env:versionType) { $sconsArgs += " updateVersionType=$env:versionType" } - $sconsArgs += ' publisher="NV Access Limited"' + $sconsArgs += ' publisher="NV Access"' $sconsArgs += " certFile=appveyor\authenticode.pfx certTimestampServer=http://timestamp.digicert.com" $sconsArgs += " version_build=$env:APPVEYOR_BUILD_NUMBER" # We use cmd to run scons because PowerShell throws exceptions if warnings get dumped to stderr. From 0efa9c35a0da0ca5f0831962992db0f9aee86820 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Wed, 20 Sep 2017 08:34:52 +1000 Subject: [PATCH 20/25] remove bad attribute in appxmanifest.xml --- appx/manifest.xml.subst | 1 - 1 file changed, 1 deletion(-) diff --git a/appx/manifest.xml.subst b/appx/manifest.xml.subst index 1452d518e29..725c3b34b5c 100644 --- a/appx/manifest.xml.subst +++ b/appx/manifest.xml.subst @@ -4,7 +4,6 @@ xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"> Date: Wed, 20 Sep 2017 11:48:55 +1000 Subject: [PATCH 21/25] scons appx now produces two appx files: * storeSubmition: not signed, and a publisher ID matching NV Access Limited's Store publisher ID. Suitable for submitting to the Windows Store via NV Access Limited's developer account * sideLoadable: signed by NV Access Limited, suitable for installing on a Windows 10 system manually as a side-loaded app for testing. --- appx/manifest.xml.subst | 10 +++++----- appx/sconscript | 39 ++++++++++++++++++++++++++++++--------- sconstruct | 6 +++++- 3 files changed, 40 insertions(+), 15 deletions(-) diff --git a/appx/manifest.xml.subst b/appx/manifest.xml.subst index 725c3b34b5c..fcca4d6b240 100644 --- a/appx/manifest.xml.subst +++ b/appx/manifest.xml.subst @@ -4,14 +4,14 @@ xmlns:uap="http://schemas.microsoft.com/appx/manifest/uap/windows10" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities"> %productName% - NV access Limited + %publisher% %description% appx_images/nvda_44x44.png @@ -30,7 +30,7 @@ MaxVersionTested="10.0.16278.100" diff --git a/appx/sconscript b/appx/sconscript index 63d1edebd96..e3f5d3929f3 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -1,7 +1,11 @@ import subprocess import versionInfo -Import(['env','outFilePrefix']) +Import([ + 'env', + 'outFilePrefix', + 'isStoreSubmition', +]) def getCertPublisher(env): certFile=env.get('certFile') @@ -16,8 +20,20 @@ def getCertPublisher(env): subject=line[len(linePrefix):].rstrip() return subject +packageName="NVAccessLimited.NVDANonVisualDesktopAccess" +packageVersion="%s.%s.%s.%s"%(versionInfo.version_year,versionInfo.version_major,env['version_build'],0) +if isStoreSubmition: + packageFileName=outFilePrefix+"_storeSubmition.appx" + packagePublisher="CN=83B1DA31-9B66-442C-88AB-77B4B815E1DE" + packagePublisherDisplayName="NV Access Limited" + productName="NVDA Screen Reader (Windows Store Edition)" +else: + packageFileName=outFilePrefix+"_sideLoadable.appx" + packagePublisher=getCertPublisher(env) + packagePublisherDisplayName=env['publisher'] + productName="NVDA Screen Reader (Windows Desktop Bridge Edition)" + signExec=env['signExec'] if env['certFile'] else None -certPublisher=getCertPublisher(env) # Files from NVDA's distribution that cannot be included in the appx due to policy or security restrictions excludedDistFiles=[ @@ -35,6 +51,7 @@ excludedDistFiles=[ 'lib/VBufBackend_mshtml.dll', 'lib/VBufBackend_webKit.dll', 'lib64', + 'uninstall.exe', ] # Create an appx manifest with version and publisher etc all filled in @@ -42,11 +59,11 @@ manifest=env.Substfile( "AppxManifest.xml", 'manifest.xml.subst', SUBST_DICT={ - '%name%':versionInfo.name, - '%version%':"%s.%s.%s.%s"%(versionInfo.version_year,versionInfo.version_major,env['version_build'],0), - '%certPublisher%':certPublisher, - '%publisher%':env['publisher'], - '%productName%':"%s (%s)"%(versionInfo.name,versionInfo.longName), + '%packageName%':packageName, + '%packageVersion%':packageVersion, + '%packagePublisher%':packagePublisher, + '%packagePublisherDisplayName%':packagePublisherDisplayName, + '%productName%':productName, '%description%':versionInfo.description, }, ) @@ -65,5 +82,9 @@ appxContent=env.Command( # Ensure that it is always copied as we can't tell if dist changed env.AlwaysBuild(appxContent) # Package the appx -appx=env.Command(File('#output\\%s.appx'%outFilePrefix),appxContent,"makeappx pack /p $TARGET /d $SOURCE") -env.Alias('appx',appx) +appx=env.Command(packageFileName,appxContent,"makeappx pack /p $TARGET /d $SOURCE") +if signExec and not isStoreSubmition: + env.AddPostAction(appx,signExec) + +Return(['appx']) + diff --git a/sconstruct b/sconstruct index fc3b663d1e2..df6fa98a588 100755 --- a/sconstruct +++ b/sconstruct @@ -395,7 +395,11 @@ symbolsList.extend(env.Glob(os.path.join(sourceLibDir64.path,'*.pdb'))) symbolsArchive = env.ZipArchive(outputDir.File("%s_debugSymbols.zip" % outFilePrefix), symbolsList) env.Alias("symbolsArchive", symbolsArchive) -env.SConscript("appx/sconscript",exports=["env"],variant_dir='build\\appx') +appx_storeSubmition=env.SConscript("appx/sconscript",exports={'env':env,'isStoreSubmition':True},variant_dir='build\\appx_storeSubmition') +installed_appx_storeSubmition=env.Install('output',appx_storeSubmition) +appx_sideLoadable=env.SConscript("appx/sconscript",exports={'env':env,'isStoreSubmition':False},variant_dir='build\\appx_sideLoadable') +installed_appx_sideLoadable=env.Install('output',appx_sideLoadable) +env.Alias('appx',[installed_appx_storeSubmition,installed_appx_sideLoadable]) env.Default(dist) From 97bf133aeba2df31ca2f98fc5a300632bb8b0608 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Wed, 20 Sep 2017 12:34:56 +1000 Subject: [PATCH 22/25] Fix publisher --- appx/sconscript | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appx/sconscript b/appx/sconscript index e3f5d3929f3..1a2ec4f2c8f 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -62,7 +62,7 @@ manifest=env.Substfile( '%packageName%':packageName, '%packageVersion%':packageVersion, '%packagePublisher%':packagePublisher, - '%packagePublisherDisplayName%':packagePublisherDisplayName, + '%publisher%':packagePublisherDisplayName, '%productName%':productName, '%description%':versionInfo.description, }, From fcbf0023d6a97a5a45061986dc239925838aebfb Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Wed, 13 Dec 2017 14:34:03 +1000 Subject: [PATCH 23/25] Add some comments, and don't force NVDA to always identify asn a appX (was for debugging). --- appx/sconscript | 11 +++++++++-- sconstruct | 1 + source/nvda.pyw | 4 +++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/appx/sconscript b/appx/sconscript index 1a2ec4f2c8f..f9881dedb69 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -8,6 +8,10 @@ Import([ ]) def getCertPublisher(env): + """ + If no signing certificate is provided, then the given publisher is used as is. + If a signing certificate is given, then the publisher is extracted from the certificate. + """ certFile=env.get('certFile') if not certFile: return env['publisher'] @@ -24,10 +28,12 @@ packageName="NVAccessLimited.NVDANonVisualDesktopAccess" packageVersion="%s.%s.%s.%s"%(versionInfo.version_year,versionInfo.version_major,env['version_build'],0) if isStoreSubmition: packageFileName=outFilePrefix+"_storeSubmition.appx" + # NV Access Limited's Windows Store publisher ID + # It is okay to be here as the only way to submit, validate and sign the package is via the NV Access store account. packagePublisher="CN=83B1DA31-9B66-442C-88AB-77B4B815E1DE" packagePublisherDisplayName="NV Access Limited" productName="NVDA Screen Reader (Windows Store Edition)" -else: +else: # not for submition, just side-loadable packageFileName=outFilePrefix+"_sideLoadable.appx" packagePublisher=getCertPublisher(env) packagePublisherDisplayName=env['publisher'] @@ -42,6 +48,7 @@ excludedDistFiles=[ 'nvda_slave.exe', 'nvda_uiAccess.exe', 'lib/IAccessible2Proxy.dll', + 'lib/ISimpleDOM.dll', 'lib/minHook.dll', 'lib/NVDAHelperRemote.dll', 'lib/VBufBackend_adobeAcrobat.dll', @@ -50,7 +57,7 @@ excludedDistFiles=[ 'lib/VBufBackend_lotusNotesRichText.dll', 'lib/VBufBackend_mshtml.dll', 'lib/VBufBackend_webKit.dll', - 'lib64', + 'lib64/', 'uninstall.exe', ] diff --git a/sconstruct b/sconstruct index e3764c823cd..32b8805d8ac 100755 --- a/sconstruct +++ b/sconstruct @@ -124,6 +124,7 @@ Export('outputDir') devDocsOutputDir=outputDir.Dir('devDocs') # An action to sign an executable with certFile. +# we encrypt with SHA256 as this is the minimum required by the Windows Store for appx packages signExecCmd = ["signtool", "sign", "/fd", "SHA256", "/f", certFile] if certPassword: signExecCmd.extend(("/p", certPassword)) diff --git a/source/nvda.pyw b/source/nvda.pyw index bf3248e7883..d0d78e87e16 100755 --- a/source/nvda.pyw +++ b/source/nvda.pyw @@ -50,7 +50,9 @@ except AttributeError: config.isAppX=False else: bufLen=ctypes.c_int() - config.isAppX=True #(GetCurrentPackageFullName(ctypes.byref(bufLen),None)==0) + # Use GetCurrentPackageFullName to detect if we are running as a store app. + # It returns 0 (success) if in a store app, and an error code otherwise. + config.isAppX=(GetCurrentPackageFullName(ctypes.byref(bufLen),None)==0) class NoConsoleOptionParser(argparse.ArgumentParser): """A commandline option parser that shows its messages using dialogs, as this pyw file has no dos console window associated with it""" From 4809710994980de705c9c9e1f6d6d8bed146a952 Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 21 Dec 2017 09:08:53 +1000 Subject: [PATCH 24/25] Fix whitepsace for appX manifest. --- appx/manifest.xml.subst | 88 ++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 44 deletions(-) diff --git a/appx/manifest.xml.subst b/appx/manifest.xml.subst index fcca4d6b240..2bb859eebdc 100644 --- a/appx/manifest.xml.subst +++ b/appx/manifest.xml.subst @@ -1,47 +1,47 @@ - - - %productName% - %publisher% - %description% - appx_images/nvda_44x44.png - - - - - - - - - - - - - - - - - \ No newline at end of file + + + %productName% + %publisher% + %description% + appx_images/nvda_44x44.png + + + + + + + + + + + + + + + + From 941db96ce804bb72b6cad2d362a49ac89fc12d8b Mon Sep 17 00:00:00 2001 From: Michael Curran Date: Thu, 21 Dec 2017 09:11:06 +1000 Subject: [PATCH 25/25] submition -> submission --- appx/sconscript | 10 +++++----- sconstruct | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/appx/sconscript b/appx/sconscript index f9881dedb69..d1b1c87cb41 100644 --- a/appx/sconscript +++ b/appx/sconscript @@ -4,7 +4,7 @@ import versionInfo Import([ 'env', 'outFilePrefix', - 'isStoreSubmition', + 'isStoreSubmission', ]) def getCertPublisher(env): @@ -26,14 +26,14 @@ def getCertPublisher(env): packageName="NVAccessLimited.NVDANonVisualDesktopAccess" packageVersion="%s.%s.%s.%s"%(versionInfo.version_year,versionInfo.version_major,env['version_build'],0) -if isStoreSubmition: - packageFileName=outFilePrefix+"_storeSubmition.appx" +if isStoreSubmission: + packageFileName=outFilePrefix+"_storeSubmission.appx" # NV Access Limited's Windows Store publisher ID # It is okay to be here as the only way to submit, validate and sign the package is via the NV Access store account. packagePublisher="CN=83B1DA31-9B66-442C-88AB-77B4B815E1DE" packagePublisherDisplayName="NV Access Limited" productName="NVDA Screen Reader (Windows Store Edition)" -else: # not for submition, just side-loadable +else: # not for submission, just side-loadable packageFileName=outFilePrefix+"_sideLoadable.appx" packagePublisher=getCertPublisher(env) packagePublisherDisplayName=env['publisher'] @@ -90,7 +90,7 @@ appxContent=env.Command( env.AlwaysBuild(appxContent) # Package the appx appx=env.Command(packageFileName,appxContent,"makeappx pack /p $TARGET /d $SOURCE") -if signExec and not isStoreSubmition: +if signExec and not isStoreSubmission: env.AddPostAction(appx,signExec) Return(['appx']) diff --git a/sconstruct b/sconstruct index 32b8805d8ac..c4b81edf258 100755 --- a/sconstruct +++ b/sconstruct @@ -392,11 +392,11 @@ symbolsList.extend(env.Glob(os.path.join(sourceLibDir64.path,'*.pdb'))) symbolsArchive = env.ZipArchive(outputDir.File("%s_debugSymbols.zip" % outFilePrefix), symbolsList) env.Alias("symbolsArchive", symbolsArchive) -appx_storeSubmition=env.SConscript("appx/sconscript",exports={'env':env,'isStoreSubmition':True},variant_dir='build\\appx_storeSubmition') -installed_appx_storeSubmition=env.Install('output',appx_storeSubmition) -appx_sideLoadable=env.SConscript("appx/sconscript",exports={'env':env,'isStoreSubmition':False},variant_dir='build\\appx_sideLoadable') +appx_storeSubmission=env.SConscript("appx/sconscript",exports={'env':env,'isStoreSubmission':True},variant_dir='build\\appx_storeSubmission') +installed_appx_storeSubmission=env.Install('output',appx_storeSubmission) +appx_sideLoadable=env.SConscript("appx/sconscript",exports={'env':env,'isStoreSubmission':False},variant_dir='build\\appx_sideLoadable') installed_appx_sideLoadable=env.Install('output',appx_sideLoadable) -env.Alias('appx',[installed_appx_storeSubmition,installed_appx_sideLoadable]) +env.Alias('appx',[installed_appx_storeSubmission,installed_appx_sideLoadable]) env.Default(dist)