blind_html Fwd: [Nvda-dev] commit r2928 - in trunk: . installer source source/NVDAHelper source/appModules source/config source/gui user_docs/en

  • From: Nimer Jaber <nimerjaber1@xxxxxxxxx>
  • To: blind_html@xxxxxxxxxxxxx
  • Date: Fri, 15 May 2009 07:11:31 -0600

This is exciting!!!

-------- Original Message --------
Subject: [Nvda-dev] commit r2928 - in trunk: . installer source source/NVDAHelper source/appModules source/config source/gui user_docs/en
Date:   Fri, 15 May 2009 07:13:21 +0000
From:   NVDA Subversion <svn@xxxxxxxxxxxxxxxx>
Reply-To: News and discussion for NVDA (NonVisual Desktop Access), a free and open source screen reader for Microsoft Windows <nvda-dev@xxxxxxxxxxxxxxxxxx>
To:     nvda-dev@xxxxxxxxxxxxxxxxxx



Author: nvda
Date: Fri May 15 07:13:17 2009
New Revision: 2928

Log:
        * new: NVDA can be configured to start automatically after you log on 
to Windows. The option is in the General Settings dialog.
        * new: NVDA can read secure Windows screens such as the Windows logon, 
control+alt+delete and UAC screens in Windows XP and above. Reading of the 
Windows logon screen can be configured from the General Settings dialog. (#97)
        * fix: On Windows Vista, the NVDA installer now starts NVDA with normal 
user privileges when requested to run NVDA on the finish screen.

New misc-deps package, version 2009-05-15-01. Includes new NVDAHelper with bug 
fixes, new nvVBufLib with bug fixes and optimisations, and NSIS components for 
secure desktop and UAC support.


Added:
   trunk/source/NVDAHelper/IA2Support.cpp
      - copied, changed from r2927, /trunk/source/NVDAHelper/IA2Support.c
   trunk/source/NVDAHelper/hookManager.cpp
      - copied, changed from r2927, /trunk/source/NVDAHelper/hookManager.c
   trunk/source/NVDAHelper/inputLangChange.cpp
      - copied, changed from r2927, /trunk/source/NVDAHelper/inputLangChange.c
   trunk/source/NVDAHelper/typedCharacter.cpp
      - copied, changed from r2927, /trunk/source/NVDAHelper/typedCharacter.c
   trunk/source/appModules/logonui.py
   trunk/source/nvda_service.py
   trunk/source/nvda_slave.pyw
   trunk/source/shellapi.py
Removed:
   trunk/source/NVDAHelper/IA2Support.c
   trunk/source/NVDAHelper/hookManager.c
   trunk/source/NVDAHelper/inputLangChange.c
   trunk/source/NVDAHelper/typedCharacter.c
Modified:
   trunk/dependencies.txt
   trunk/installer/nvda.nsi
   trunk/source/IAccessibleHandler.py
   trunk/source/NVDAHelper/hookManager.h
   trunk/source/config/__init__.py
   trunk/source/gui/settingsDialogs.py
   trunk/source/nvda.pyw
   trunk/source/setup.py
   trunk/source/winKernel.py
   trunk/source/winUser.py
   trunk/user_docs/en/whats new.txt

Modified: trunk/dependencies.txt
==============================================================================
--- trunk/dependencies.txt      (original)
+++ trunk/dependencies.txt      Fri May 15 07:13:17 2009
@@ -7,7 +7,7 @@
 http://www.wxpython.org/
 Python Windows Extensions (for Python 2.6), build 212 or later:
 http://www.sourceforge.net/projects/pywin32/
-Several other packages, made available for convenience in the NVDA 
miscellaneous dependencies package, version 2009-04-16-01 or later:
+Several other packages, made available for convenience in the NVDA 
miscellaneous dependencies package, version 2009-05-15-01 or later:
 http://www.nvda-project.org/wiki/MiscellaneousDependencies

 To build a binary version of NVDA, you will also need:

Modified: trunk/installer/nvda.nsi
==============================================================================
--- trunk/installer/nvda.nsi    (original)
+++ trunk/installer/nvda.nsi    Fri May 15 07:13:17 2009
@@ -11,6 +11,7 @@
 !include "Library.nsh"
 !include "FileFunc.nsh"

+
 ;--------
 ;Settings

@@ -36,6 +37,7 @@
 SetDateSave on
 XPStyle on
 InstProgressFlags Smooth
+RequestExecutionLevel user /* RequestExecutionLevel REQUIRED! */
 !define MUI_ABORTWARNING ;Should ask to exit
 !define MUI_UNINSTALLER ;We want an uninstaller to be generated

@@ -54,6 +56,8 @@
 !define MUI_CUSTOMFUNCTION_GUIINIT NVDA_GUIInit
 !define MUI_CUSTOMFUNCTION_ABORT userAbort

+!include "serviceLib.nsh"
+
 ;--------------------------------
 ;Pages

@@ -71,6 +75,9 @@
 ;Directory selection page
 !insertmacro MUI_PAGE_DIRECTORY

+;Components selection page
+!insertmacro MUI_PAGE_COMPONENTS
+
 ;Start menu page
 Var StartMenuFolder
 !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM"
@@ -153,6 +160,8 @@
 ReserveFile "${NSISDIR}\Plugins\system.dll"
 ReserveFile "${NSISDIR}\Plugins\banner.dll"
 ReserveFile "waves\${SNDLogo}"
+ReserveFile "UAC.dll"
+!addplugindir "."

 ;-----
 ;Include install logger code (depends on some above settings)
@@ -170,8 +179,7 @@
 ;-----
 ;Sections

-;The only installable section
-Section "install"
+Section "-NVDA"
 SetShellVarContext all
 SetOutPath "$INSTDIR"
 ; open and close uninstallation log after ennumerating all the files being 
copied
@@ -214,10 +222,19 @@
 WriteRegStr ${INSTDIR_REG_ROOT} "Software\${PRODUCT}" "" $INSTDIR
  SectionEnd

+section "nvda service (Windows logon / Security dialog support)"
+!insertmacro SERVICE create "nvda" 
"path=$INSTDIR\nvda_service.exe;autostart=1;display=NonVisual Desktop Access;description=Runs 
NVDA at Windows logon and in Windows security dialogs;"
+!insertmacro SERVICE "start" "nvda" ""
+SectionEnd
+
 ;The uninstall section
 Section "Uninstall"
 SetShellVarContext all
-
+;Stop and uninstall the service
+!undef UN
+!define UN "un."
+!insertmacro SERVICE stop "nvda" ""
+!insertmacro SERVICE delete "nvda" ""
 ; Uninstall libraries
 !insertmacro UninstallLib DLL NOTSHARED REBOOT_NOTPROTECTED 
"$INSTDIR\lib\NVDAHelper.dll"
 !insertmacro UninstallLib DLL NOTSHARED REBOOT_NOTPROTECTED 
"$INSTDIR\lib\VBufBase.dll"
@@ -246,6 +263,18 @@
 ;Functions

 Function .onInit
+UAC::RunElevated
+;If couldn't change user then fail
+strcmp 0 $0 +1 elevationFail
+;If we are the outer user process, then silently quit
+strcmp 1 $1 +1 +2
+quit
+;If we are now an admin, success
+strcmp 1 $3 elevationSuccess
+elevationFail:
+MessageBox mb_iconstop "Unable to elevate, error $0"
+quit
+elevationSuccess:
 strcpy $runAppOnInstSuccess "0"
 ; Fix an error from previous installers where the "nvda" file would be left 
behind after uninstall
 IfFileExists "$PROGRAMFILES\NVDA" 0
@@ -356,7 +385,7 @@
 !insertmacro UNINSTALL.LOG_UPDATE_INSTALL
 Execwait "$PLUGINSDIR\${NVDATempDir}\${NVDAApp} -q"
 strcmp $runAppOnInstSuccess "1" +1 end
-Exec "$INSTDIR\${NVDAApp}"
+uac::exec "" "$INSTDIR\${NVDAApp}" "" ""
 end:
 FunctionEnd

@@ -367,6 +396,7 @@
 Function .onGUIEnd
 ; Clean up the temporary folder
 rmdir /R /REBOOTOK "$PLUGINSDIR\${NVDATempDir}"
+UAC::Unload ;Must call unload!
 FunctionEnd

 function makeRunAppOnInstSuccess

Modified: trunk/source/IAccessibleHandler.py
==============================================================================
--- trunk/source/IAccessibleHandler.py  (original)
+++ trunk/source/IAccessibleHandler.py  Fri May 15 07:13:17 2009
@@ -898,9 +898,6 @@
        def _get_role(self):
                return controlTypes.ROLE_PANE

-       def _get_description(self):
-               return _("Inaccessible to NVDA")
-
 def processDesktopSwitchWinEvent(window,objectID,childID):
        hDesk=ctypes.windll.user32.OpenInputDesktop(0, False, 0)
        #name = ctypes.create_string_buffer(256)

Modified: trunk/source/NVDAHelper/hookManager.h
==============================================================================
--- trunk/source/NVDAHelper/hookManager.h       (original)
+++ trunk/source/NVDAHelper/hookManager.h       Fri May 15 07:13:17 2009
@@ -7,7 +7,7 @@
 #include<windows.h>
 #include<wchar.h>

-#define DLLEXPORT __declspec(dllexport)
+#define DLLEXPORT extern "C" __declspec(dllexport)

 //Private variables
 extern HINSTANCE moduleHandle;

Added: trunk/source/appModules/logonui.py
==============================================================================
--- (empty file)
+++ trunk/source/appModules/logonui.py  Fri May 15 07:13:17 2009
@@ -0,0 +1,44 @@
+import keyUtils
+from NVDAObjects.IAccessible import IAccessible
+import _default
+
+class PasswordField(IAccessible):
+
+       def bindKeys(self):
+               for key, script in (
+                       ("extendedUp", "changeUser"),
+                       ("extendedDown", "changeUser"),
+               ):
+                       self.bindKey_runtime(key, script)
+
+       def _get_name(self):
+               # Focus automatically jumps to the password field when a user 
is selected. This field has no name.
+               # This means that the new selected user is not reported.
+               # Therefore, override the name of the password field to be the 
selected user name.
+               try:
+                       # The accessibility hierarchy is totally screwed here, 
so NVDA gets confused.
+                       # Therefore, we'll have to do it the ugly way...
+                       return self.IAccessibleObject.accParent.accName(0)
+               except:
+                       return super(PasswordField, self).name
+
+       def script_changeUser(self, key):
+               # The up and down arrow keys change the selected user, but 
there's no reliable NVDA event for detecting this.
+               oldName = self.name
+               keyUtils.sendKey(key)
+               if oldName == self.name:
+                       return
+               self.event_gainFocus()
+
+class AppModule(_default.AppModule):
+
+       def event_NVDAObject_init(self, obj):
+               if obj.windowClassName == "NativeHWNDHost" and obj.parent and 
not obj.parent.parent:
+                       # Make sure the top level pane is always presented.
+                       obj.isPresentableFocusAncestor = True
+                       return
+
+               if obj.windowClassName == "Edit" and not obj.name and not 
obj.parent:
+                       self.overlayCustomNVDAObjectClass(obj, PasswordField, 
outerMost=True)
+                       obj.bindKeys()
+                       return

Modified: trunk/source/config/__init__.py
==============================================================================
--- trunk/source/config/__init__.py     (original)
+++ trunk/source/config/__init__.py     Fri May 15 07:13:17 2009
@@ -4,6 +4,7 @@
 import globalVars
 import _winreg
 import os
+import sys
 from cStringIO import StringIO
 from configobj import ConfigObj
 from validate import Validator
@@ -205,3 +206,73 @@
                if 
ctypes.windll.shell32.SHGetSpecialFolderPathW(0,buf,CSIDL_APPDATA,0):
                        return u'%s\\nvda'%buf.value
        return u'.\\'
+
+RUN_REGKEY = ur"SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
+
+def getStartAfterLogon():
+       try:
+               k = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, RUN_REGKEY)
+               val = _winreg.QueryValueEx(k, u"nvda")[0]
+               return os.stat(val) == os.stat(sys.argv[0])
+       except (WindowsError, OSError):
+               return False
+
+def setStartAfterLogon(enable):
+       k = _winreg.OpenKey(_winreg.HKEY_CURRENT_USER, RUN_REGKEY, 0, 
_winreg.KEY_WRITE)
+       if enable:
+               _winreg.SetValueEx(k, u"nvda", None, _winreg.REG_SZ, 
sys.argv[0])
+       else:
+               _winreg.DeleteValue(k, u"nvda")
+
+SERVICE_FILENAME = u"nvda_service.exe"
+
+def isServiceInstalled():
+       if not os.path.isfile(SERVICE_FILENAME):
+               return False
+       try:
+               k = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 
ur"SYSTEM\CurrentControlSet\Services\nvda")
+               val = _winreg.QueryValueEx(k, u"ImagePath")[0].replace(u'"', 
u'')
+               return os.stat(val) == os.stat(SERVICE_FILENAME)
+       except (WindowsError, OSError):
+               return False
+
+def execElevated(path, params=None, wait=False):
+       import shellapi
+       import winKernel
+       import winUser
+       sei = shellapi.SHELLEXECUTEINFO(lpVerb=u"runas", 
lpFile=os.path.abspath(path), lpParameters=params, nShow=winUser.SW_HIDE)
+       if wait:
+               sei.fMask = shellapi.SEE_MASK_NOCLOSEPROCESS
+       shellapi.ShellExecuteEx(sei)
+       if wait:
+               try:
+                       winKernel.waitForSingleObject(sei.hProcess, 
winKernel.INFINITE)
+                       return winKernel.GetExitCodeProcess(sei.hProcess)
+               finally:
+                       winKernel.closeHandle(sei.hProcess)
+
+SLAVE_FILENAME = u"nvda_slave.exe"
+
+NVDA_REGKEY = ur"SOFTWARE\NVDA"
+
+def getStartOnLogonScreen():
+       try:
+               k = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, NVDA_REGKEY)
+               return bool(_winreg.QueryValueEx(k, u"startOnLogonScreen")[0])
+       except WindowsError:
+               return False
+
+def _setStartOnLogonScreen(enable):
+       k = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, NVDA_REGKEY, 0, 
_winreg.KEY_WRITE)
+       _winreg.SetValueEx(k, u"startOnLogonScreen", None, _winreg.REG_DWORD, 
int(enable))
+
+def setStartOnLogonScreen(enable):
+       if getStartOnLogonScreen() == enable:
+               return
+       try:
+               # Try setting it directly.
+               _setStartOnLogonScreen(enable)
+       except WindowsError:
+               # We probably don't have admin privs, so we need to elevate to 
do this using the slave.
+               if execElevated(SLAVE_FILENAME, "config_setStartOnLogonScreen 
%d" % enable, wait=True) != 0:
+                       raise RuntimeError("Slave failed to set 
startOnLogonScreen")

Modified: trunk/source/gui/settingsDialogs.py
==============================================================================
--- trunk/source/gui/settingsDialogs.py (original)
+++ trunk/source/gui/settingsDialogs.py Fri May 15 07:13:17 2009
@@ -128,6 +128,16 @@
                        log.debugWarning("Could not set log level list to current 
log level")
                logLevelSizer.Add(self.logLevelList)
                settingsSizer.Add(logLevelSizer,border=10,flag=wx.BOTTOM)
+               self.startAfterLogonCheckBox = wx.CheckBox(self, wx.ID_ANY, 
label=_("&Automatically start NVDA after I log on to Windows"))
+               
self.startAfterLogonCheckBox.SetValue(config.getStartAfterLogon())
+               if not config.isInstalledCopy():
+                       self.startAfterLogonCheckBox.Disable()
+               settingsSizer.Add(self.startAfterLogonCheckBox)
+               self.startOnLogonScreenCheckBox = wx.CheckBox(self, wx.ID_ANY, 
label=_("Use NVDA on the Windows logon screen (requires administrator 
privileges)"))
+               
self.startOnLogonScreenCheckBox.SetValue(config.getStartOnLogonScreen())
+               if not config.isServiceInstalled():
+                       self.startOnLogonScreenCheckBox.Disable()
+               settingsSizer.Add(self.startOnLogonScreenCheckBox)

        def postInit(self):
                self.languageList.SetFocus()
@@ -147,6 +157,13 @@
                logLevel=self.LOG_LEVELS[self.logLevelList.GetSelection()][0]
                
config.conf["general"]["loggingLevel"]=logHandler.levelNames[logLevel]
                logHandler.setLogLevelFromConfig()
+               if self.startAfterLogonCheckBox.IsEnabled():
+                       
config.setStartAfterLogon(self.startAfterLogonCheckBox.GetValue())
+               if self.startOnLogonScreenCheckBox.IsEnabled():
+                       try:
+                               
config.setStartOnLogonScreen(self.startOnLogonScreenCheckBox.GetValue())
+                       except (WindowsError, RuntimeError):
+                               wx.MessageBox(_("This change requires administrator 
privileges."), _("Insufficient Privileges"), style=wx.OK | wx.ICON_ERROR)
                if self.oldLanguage!=newLanguage:
                        if wx.MessageDialog(self,_("For the new language to take effect, the 
configuration must be saved and NVDA must be restarted. Press enter to save and restart NVDA, or 
cancel to manually save and exit at a later time."),_("Language Configuration 
Change"),wx.OK|wx.CANCEL|wx.ICON_WARNING).ShowModal()==wx.ID_OK:
                                config.save()

Modified: trunk/source/nvda.pyw
==============================================================================
--- trunk/source/nvda.pyw       (original)
+++ trunk/source/nvda.pyw       Fri May 15 07:13:17 2009
@@ -62,6 +62,7 @@
 parser=NoConsoleOptionParser()
 
parser.add_option('-q','--quit',action="store_true",dest='quit',default=False,help="Quit
 already running copy of NVDA")
 
parser.add_option('-r','--replace',action="store_true",dest='replace',default=False,help="Quit
 already running copy of NVDA and start this one")
+parser.add_option('-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_option('-f','--log-file',dest='logFileName',default=logFileName,help="The 
file where log messages should be written to")
 parser.add_option('-l','--log-level',type="int",dest='logLevel',default=0,help="The 
lowest level of message logged (debug 10, info 20, warning 30, error 40, critical 50), default is 
warning")
 
parser.add_option('-c','--config-path',dest='configPath',default=config.getUserDefaultConfigPath(),help="The
 path where all settings for NVDA are stored")
@@ -87,6 +88,9 @@
                                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)

 #os.environ['PYCHECKER']="--limit 10000 -q --changetypes"
 #import pychecker.checker

Added: trunk/source/nvda_service.py
==============================================================================
--- (empty file)
+++ trunk/source/nvda_service.py        Fri May 15 07:13:17 2009
@@ -0,0 +1,288 @@
+from ctypes import *
+from ctypes.wintypes import *
+import threading
+import win32serviceutil
+import win32service
+import sys
+import os
+import time
+import _winreg
+
+CREATE_UNICODE_ENVIRONMENT=1024
+INFINITE = 0xffffffff
+UOI_NAME = 2
+SYNCHRONIZE = 0x100000
+WAIT_OBJECT_0 = 0
+MAXIMUM_ALLOWED = 0x2000000
+SecurityIdentification = 2
+TokenPrimary = 1
+PROCESS_QUERY_INFORMATION = 0x0400
+TokenSessionId = 12
+TokenUIAccess = 26
+WTS_CONSOLE_CONNECT = 0x1
+WTS_CONSOLE_DISCONNECT = 0x2
+WTS_SESSION_LOGON = 0x5
+WTS_SESSION_LOGOFF = 0x6
+WTS_SESSION_LOCK = 0x7
+WTS_SESSION_UNLOCK = 0x8
+WTS_CURRENT_SERVER_HANDLE = 0
+WTSUserName = 5
+
+nvdaExec = os.path.join(sys.prefix,"nvda.exe")
+slaveExec = os.path.join(sys.prefix,"nvda_slave.exe")
+
+def debug(msg):
+       try:
+               file(os.path.join(os.getenv("windir"), "temp", "nvda_service.log"), 
"a").write(msg + "\n")
+       except (OSError, IOError):
+               pass
+
+def getInputDesktopName():
+       desktop = windll.user32.OpenInputDesktop(0, False, 0)
+       name = create_unicode_buffer(256)
+       windll.user32.GetUserObjectInformationW(desktop, UOI_NAME, byref(name), 
sizeof(name), None)
+       windll.user32.CloseDesktop(desktop)
+       return ur"WinSta0\%s" % name.value
+
+class STARTUPINFO(Structure):
+       _fields_=[
+               ('cb',DWORD),
+               ('lpReserved',LPWSTR),
+               ('lpDesktop',LPWSTR),
+               ('lpTitle',LPWSTR),
+               ('dwX',DWORD),
+               ('dwY',DWORD),
+               ('dwXSize',DWORD),
+               ('dwYSize',DWORD),
+               ('dwXCountChars',DWORD),
+               ('dwYCountChars',DWORD),
+               ('dwFillAttribute',DWORD),
+               ('dwFlags',DWORD),
+               ('wShowWindow',WORD),
+               ('cbReserved2',WORD),
+               ('lpReserved2',POINTER(c_byte)),
+               ('hSTDInput',HANDLE),
+               ('hSTDOutput',HANDLE),
+               ('hSTDError',HANDLE),
+       ]
+
+class PROCESS_INFORMATION(Structure):
+       _fields_=[
+               ('hProcess',HANDLE),
+               ('hThread',HANDLE),
+               ('dwProcessID',DWORD),
+               ('dwThreadID',DWORD),
+       ]
+
+def getLoggedOnUserToken(session):
+       # Only works in Windows XP and above.
+       token = HANDLE()
+       windll.wtsapi32.WTSQueryUserToken(session, byref(token))
+       return token.value
+
+def duplicateTokenPrimary(token):
+       newToken = HANDLE()
+       windll.advapi32.DuplicateTokenEx(token, MAXIMUM_ALLOWED, None, 
SecurityIdentification, TokenPrimary, byref(newToken))
+       windll.kernel32.CloseHandle(token)
+       return newToken.value
+
+def getOwnToken():
+       process = windll.kernel32.OpenProcess(PROCESS_QUERY_INFORMATION, False, 
os.getpid())
+       token = HANDLE()
+       windll.advapi32.OpenProcessToken(process, MAXIMUM_ALLOWED, byref(token))
+       windll.kernel32.CloseHandle(process)
+       return token
+
+def getSessionSystemToken(session):
+       token = duplicateTokenPrimary(getOwnToken())
+       session = DWORD(session)
+       windll.advapi32.SetTokenInformation(token, TokenSessionId, 
byref(session), sizeof(DWORD))
+       return token
+
+def executeProcess(desktop, token, executable, *argStrings):
+       argsString=" ".join(list(argStrings))
+       startupInfo=STARTUPINFO(cb=sizeof(STARTUPINFO),lpDesktop=desktop)
+       processInformation=PROCESS_INFORMATION()
+       cmdBuf=create_unicode_buffer(u'"%s" %s'%(executable,argsString))
+       if token:
+               env=c_void_p()
+               windll.userenv.CreateEnvironmentBlock(byref(env),token,False)
+               try:
+                       if windll.advapi32.CreateProcessAsUserW(token, None, 
cmdBuf,None,None,False,CREATE_UNICODE_ENVIRONMENT,env,None,byref(startupInfo),byref(processInformation))
 == 0:
+                               raise WinError()
+               finally:
+                       windll.kernel32.CloseHandle(token)
+                       windll.userenv.DestroyEnvironmentBlock(env)
+       else:
+               if windll.kernel32.CreateProcessW(None, 
cmdBuf,None,None,False,0,None,None,byref(startupInfo),byref(processInformation))
 == 0:
+                       raise WinError()
+       return processInformation.hProcess
+
+def nvdaLauncher():
+       desktop = getInputDesktopName()
+       if desktop == ur"WinSta0\Default":
+               return
+
+       startNVDA(desktop)
+       desktopSwitchEvt = windll.kernel32.OpenEventW(SYNCHRONIZE, False, 
u"WinSta0_DesktopSwitch")
+       windll.kernel32.WaitForSingleObject(desktopSwitchEvt, INFINITE)
+       windll.kernel32.CloseHandle(desktopSwitchEvt)
+       exitNVDA(desktop)
+
+def startNVDA(desktop):
+       process = executeProcess(desktop, None, nvdaExec, "-m")
+       windll.kernel32.CloseHandle(process)
+
+def startNVDAUIAccess(session, desktop):
+       token = duplicateTokenPrimary(getLoggedOnUserToken(session))
+       uiAccess = ULONG(1)
+       windll.advapi32.SetTokenInformation(token, TokenUIAccess, 
byref(uiAccess), sizeof(ULONG))
+       process = executeProcess(desktop, token, nvdaExec, "-m")
+       windll.kernel32.CloseHandle(process)
+
+def exitNVDA(desktop):
+       process = executeProcess(desktop, None, nvdaExec, "-q")
+       windll.kernel32.WaitForSingleObject(process, 10000)
+       windll.kernel32.CloseHandle(process)
+
+def isUserRunningNVDA(session):
+       token = getSessionSystemToken(session)
+       process = executeProcess(ur"WinSta0\Default", token, nvdaExec, 
u"--check-running")
+       windll.kernel32.WaitForSingleObject(process, INFINITE)
+       exitCode = DWORD()
+       windll.kernel32.GetExitCodeProcess(process, byref(exitCode))
+       windll.kernel32.CloseHandle(process)
+       return exitCode.value == 0
+
+def isSessionLoggedOn(session):
+       username = c_wchar_p()
+       size = DWORD()
+       windll.wtsapi32.WTSQuerySessionInformationW(WTS_CURRENT_SERVER_HANDLE, 
session, WTSUserName, byref(username), byref(size))
+       ret = bool(username.value)
+       windll.wtsapi32.WTSFreeMemory(username)
+       return ret
+
+def execBg(func):
+       t = threading.Thread(target=func)
+       t.setDaemon(True)
+       t.start()
+
+def shouldStartOnLogonScreen():
+       try:
+               k = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 
ur"SOFTWARE\NVDA")
+               return bool(_winreg.QueryValueEx(k, u"startOnLogonScreen")[0])
+       except WindowsError:
+               return False
+
+class NVDAService(win32serviceutil.ServiceFramework):
+
+       _svc_name_="nvda"
+       _svc_display_name_="nonVisual Desktop Access"
+
+       def GetAcceptedControls(self):
+               return 
win32serviceutil.ServiceFramework.GetAcceptedControls(self) | 
win32service.SERVICE_ACCEPT_SESSIONCHANGE
+
+       def initSession(self, session):
+               debug("init session %d" % session)
+               self.session = session
+               self.launcherLock = threading.RLock()
+               self.launcherStarted = False
+               self.desktopSwitchSupervisorStarted = False
+               self.isSessionLoggedOn = isSessionLoggedOn(session)
+               debug("session logged on: %r" % self.isSessionLoggedOn)
+
+               if self.isSessionLoggedOn:
+                       # The session is logged on, so treat this as a normal 
desktop switch.
+                       self.handleDesktopSwitch()
+                       execBg(self.desktopSwitchSupervisor)
+               else:
+                       # We're at the logon screen.
+                       if shouldStartOnLogonScreen():
+                               execBg(self.startLauncher)
+                       # The desktop switch supervisor will be started by the 
logon event.
+
+       def desktopSwitchSupervisor(self):
+               if self.desktopSwitchSupervisorStarted:
+                       return
+               self.desktopSwitchSupervisorStarted = True
+               origSession = self.session
+               debug("starting desktop switch supervisor, session %d" % 
origSession)
+               desktopSwitchEvt = windll.kernel32.OpenEventW(SYNCHRONIZE, False, 
u"Session\%d\WinSta0_DesktopSwitch" % self.session)
+               if not desktopSwitchEvt:
+                       try:
+                               raise WinError()
+                       except Exception, e:
+                               debug("error opening event: %s" % e)
+                               raise
+
+               while self.session == origSession:
+                       windll.kernel32.WaitForSingleObject(desktopSwitchEvt, 
INFINITE)
+                       debug("desktop switch, session %r" % self.session)
+                       self.handleDesktopSwitch()
+
+               windll.kernel32.CloseHandle(desktopSwitchEvt)
+               debug("desktop switch supervisor terminated, session %d" % 
origSession)
+
+       def handleDesktopSwitch(self):
+               with self.launcherLock:
+                       self.launcherStarted = False
+
+               if (not self.isSessionLoggedOn and shouldStartOnLogonScreen()) 
or isUserRunningNVDA(self.session):
+                       self.startLauncher()
+               else:
+                       debug("not starting launcher")
+
+       def SvcOtherEx(self, control, eventType, data):
+               if control == win32service.SERVICE_CONTROL_SESSIONCHANGE:
+                       self.handleSessionChange(eventType, data[0])
+
+       def handleSessionChange(self, event, session):
+               if event == WTS_CONSOLE_CONNECT:
+                       debug("connect %d" % session)
+                       if session != self.session:
+                               self.initSession(session)
+               elif event == WTS_SESSION_LOGON:
+                       debug("logon %d" % session)
+                       self.isSessionLoggedOn = True
+                       execBg(self.desktopSwitchSupervisor)
+               elif event == WTS_SESSION_LOGOFF:
+                       debug("logoff %d" % session)
+                       self.isSessionLoggedOn = False
+                       # We may be heading back to the logon screen.
+                       if shouldStartOnLogonScreen():
+                               execBg(self.startLauncher)
+               elif event == WTS_SESSION_LOCK:
+                       debug("lock %d" % session)
+                       if session == self.session:
+                               # We always start NVDA for the lock/switch user 
screen.
+                               self.startLauncher()
+
+       def startLauncher(self):
+               with self.launcherLock:
+                       if self.launcherStarted:
+                               return
+
+                       debug("attempt launcher start")
+                       token = getSessionSystemToken(self.session)
+                       try:
+                               process = executeProcess(ur"WinSta0\Winlogon", token, 
slaveExec, u"service_NVDALauncher")
+                               self.launcherStarted = True
+                               debug("launcher started")
+                               windll.kernel32.CloseHandle(process)
+                       except Exception, e:
+                               debug("error starting launcher: %s" % e)
+
+       def SvcDoRun(self):
+               debug("service starting")
+               self.exitEvent = threading.Event()
+               self.initSession(windll.kernel32.WTSGetActiveConsoleSessionId())
+               self.exitEvent.wait()
+               debug("service exiting")
+
+       def SvcStop(self):
+               self.exitEvent.set()
+
+if __name__=='__main__':
+       if not getattr(sys, "frozen", None):
+               raise RuntimeError("Can only be run compiled with py2exe")
+       win32serviceutil.HandleCommandLine(NVDAService)

Added: trunk/source/nvda_slave.pyw
==============================================================================
--- (empty file)
+++ trunk/source/nvda_slave.pyw Fri May 15 07:13:17 2009
@@ -0,0 +1,32 @@
+"""NVDA slave process
+Performs miscellaneous tasks which need to be performed in a separate process.
+"""
+
+import sys
+if hasattr(sys, "frozen"):
+       # Error messages (which are only for debugging) should not cause the 
py2exe log message box to appear.
+       sys.stderr = sys.stdout
+
+def main():
+       try:
+               action = sys.argv[1]
+       except IndexError:
+               sys.exit("No action")
+       args = sys.argv[2:]
+
+       try:
+               if action == "service_NVDALauncher":
+                       import nvda_service
+                       nvda_service.nvdaLauncher()
+               elif action == "config_setStartOnLogonScreen":
+                       enable = bool(int(args[0]))
+                       import config
+                       config._setStartOnLogonScreen(enable)
+               else:
+                       raise ValueError("No such action")
+
+       except Exception, e:
+               sys.exit(e)
+
+if __name__ == "__main__":
+       main()

Modified: trunk/source/setup.py
==============================================================================
--- trunk/source/setup.py       (original)
+++ trunk/source/setup.py       Fri May 15 07:13:17 2009
@@ -98,13 +98,31 @@
 'Operating System :: Microsoft :: Windows',
 ],
        cmdclass={"py2exe": py2exe},
-       windows = [{
-               "script":"nvda.pyw",
-               "uac_info": ("asInvoker", False),
-               "icon_resources":[(1,"images/nvda.ico")],
-               "version":"0.0.0.0",
-               "product_version":version,
-               "copyright":copyright,
+       windows=[
+               {
+                       "script":"nvda.pyw",
+                       "uac_info": ("asInvoker", False),
+                       "icon_resources":[(1,"images/nvda.ico")],
+                       "version":"0.0.0.0",
+                       "product_version":version,
+                       "copyright":copyright,
+               },
+               {
+                       "script": "nvda_slave.pyw",
+                       "icon_resources": [(1,"images/nvda.ico")],
+                       "version": "0.0.0.0",
+                       "product_version": version,
+                       "copyright": copyright,
+               },
+       ],
+       service=[{
+               "modules": ["nvda_service"],
+               "icon_resources": [(1, "images/nvda.ico")],
+               "version": "0.0.0.0",
+               "product_version": version,
+               "copyright": copyright,
+               "uac_info": ("requireAdministrator", False),
+               "cmdline_style": "pywin32",
        }],
        options = {"py2exe": {
                "bundle_files": 3,
@@ -112,7 +130,6 @@
                "packages": ["NVDAObjects","virtualBuffers"],
                "includes": getOptionalIncludes(),
        }},
-       zipfile = None,
        data_files=[
                (".",glob("*.dll")+glob("*.manifest")+["builtin.dic"]),
                ("documentation", ['../copying.txt', '../contributors.txt']),

Added: trunk/source/shellapi.py
==============================================================================
--- (empty file)
+++ trunk/source/shellapi.py    Fri May 15 07:13:17 2009
@@ -0,0 +1,38 @@
+#shellapi.py
+#A part of NonVisual Desktop Access (NVDA)
+#Copyright (C) 2006-2009 NVDA Contributors<http://www.nvda-project.org/>
+#This file is covered by the GNU General Public License.
+#See the file COPYING for more details.
+
+from ctypes import *
+from ctypes.wintypes import *
+
+shell32 = windll.shell32
+
+class SHELLEXECUTEINFOW(Structure):
+       _fields_ = (
+               ("cbSize", DWORD),
+               ("fMask", ULONG),
+               ("hwnd", HWND),
+               ("lpVerb", LPCWSTR),
+               ("lpFile", LPCWSTR),
+               ("lpParameters", LPCWSTR),
+               ("lpDirectory", LPCWSTR),
+               ("nShow", c_int),
+               ("hInstApp", HINSTANCE),
+               ("lpIDList", LPVOID),
+               ("lpClass", LPCWSTR),
+               ("hkeyClass", HKEY),
+               ("dwHotKey", DWORD),
+               ("hIconOrMonitor", HANDLE),
+               ("hProcess", HANDLE),
+       )
+       def __init__(self, **kwargs):
+               super(SHELLEXECUTEINFOW, self).__init__(cbSize=sizeof(self), 
**kwargs)
+SHELLEXECUTEINFO = SHELLEXECUTEINFOW
+
+SEE_MASK_NOCLOSEPROCESS = 0x00000040
+
+def ShellExecuteEx(execInfo):
+       if not shell32.ShellExecuteExW(byref(execInfo)):
+               raise WinError()

Modified: trunk/source/winKernel.py
==============================================================================
--- trunk/source/winKernel.py   (original)
+++ trunk/source/winKernel.py   Fri May 15 07:13:17 2009
@@ -101,3 +101,9 @@
        res = kernel32.SetProcessShutdownParameters(level, flags)
        if res == 0:
                raise ctypes.WinError()
+
+def GetExitCodeProcess(process):
+       exitCode = ctypes.wintypes.DWORD()
+       if not kernel32.GetExitCodeProcess(process, ctypes.byref(exitCode)):
+               raise ctypes.WinError()
+       return exitCode.value

Modified: trunk/source/winUser.py
==============================================================================
--- trunk/source/winUser.py     (original)
+++ trunk/source/winUser.py     Fri May 15 07:13:17 2009
@@ -237,6 +237,10 @@
 EVENT_CONSOLE_START_APPLICATION=0x4006
 EVENT_CONSOLE_END_APPLICATION=0x4007

+# ShowWindow() commands
+SW_HIDE = 0
+SW_SHOWNORMAL = 1
+
 def setSystemScreenReaderFlag(val):
        user32.SystemParametersInfoW(SPI_SETSCREENREADER,val,0,SPIF_SENDCHANGE)


Modified: trunk/user_docs/en/whats new.txt
==============================================================================
--- trunk/user_docs/en/whats new.txt    (original)
+++ trunk/user_docs/en/whats new.txt    Fri May 15 07:13:17 2009
@@ -24,8 +24,11 @@
 *New: In Windows Vista, if the user moves to the secure desktop (either 
because a UAC control dialog appeared, or because control+alt+delete was 
pressed), NVDA will announce the fact that the user is now on the secure 
desktop. This desktop is still not accessible to NVDA, though the user at least 
is notified as to what happened.
 *Fix: better support for Dos consoles. specifically: NVDA can now read the 
content of particular consoles it always used to think were blank. Pressing 
control+break no longer terminates NVDA.
  *New: NVDA can announce text under the mouse within dos console windows.
+* new: NVDA can be configured to start automatically after you log on to 
Windows. The option is in the General Settings dialog.
+* new: NVDA can read secure Windows screens such as the Windows logon, 
control+alt+delete and UAC screens in Windows XP and above. Reading of the 
Windows logon screen can be configured from the General Settings dialog. (#97)
+* fix: On Windows Vista, the NVDA installer now starts NVDA with normal user 
privileges when requested to run NVDA on the finish screen.

- Changes since 0.6 P2:
+Changes since 0.6 P2:
 * new: As Microsoft Excel's formula bar is inaccessible to NVDA, provide an 
NVDA specific dialog box for editing when the user presses f2 on a cell.
 * change: If the audio output device is set to use the Windows default device 
(Microsoft Sound Mapper), NVDA will now switch to the new default device for 
eSpeak and tones when the default device changes. For example, NVDA will switch 
to a USB audio device if it automatically becomes the default device when it is 
connected.
 * fix: The last chunk of audio is no longer cut off when using NVDA with 
eSpeak on a remote desktop server.

_______________________________________________
Nvda-dev mailing list
Nvda-dev@xxxxxxxxxxxxxxxxxx
http://lists.nvaccess.org/listinfo/nvda-dev

blind_html
To unsubscribe, please send a blank email to
blind_html-request@xxxxxxxxxxxxx
with unsubscribe in the subject line.
To access the archives, please visit:
//www.freelists.org/archive/blind_html

Thanks

Other related posts:

  • » blind_html Fwd: [Nvda-dev] commit r2928 - in trunk: . installer source source/NVDAHelper source/appModules source/config source/gui user_docs/en - Nimer Jaber