Module JABHandler
[hide private]
[frames] | no frames]

Source Code for Module JABHandler

  1  #javaAccessBridgeHandler.py 
  2  #A part of NonVisual Desktop Access (NVDA) 
  3  #Copyright (C) 2006-2007 NVDA Contributors <http://www.nvda-project.org/> 
  4  #This file is covered by the GNU General Public License. 
  5  #See the file COPYING for more details. 
  6   
  7  import Queue 
  8  from ctypes import * 
  9  from ctypes.wintypes import * 
 10  import queueHandler 
 11  import speech 
 12  import globalVars 
 13  from logHandler import log 
 14  import winUser 
 15  import api 
 16  import eventHandler 
 17  import controlTypes 
 18  import NVDAObjects.JAB 
19 20 #Some utility functions to help with function defines 21 22 -def _errcheck(res, func, args):
23 if not res: 24 raise RuntimeError("Result %s" % res) 25 return res
26
27 -def _fixBridgeFunc(restype,name,*argtypes,**kwargs):
28 try: 29 func=getattr(bridgeDll,name) 30 except AttributeError: 31 log.warning("%s not found in Java Access Bridge dll"%name) 32 return 33 func.restype=restype 34 func.argtypes=argtypes 35 if kwargs.get('errcheck'): 36 func.errcheck=_errcheck
37 38 #Load the first available access bridge dll 39 legacyAccessBridge=True 40 try: 41 bridgeDll=getattr(cdll,'windowsAccessBridge-32') 42 legacyAccessBridge=False 43 except WindowsError: 44 try: 45 bridgeDll=cdll.windowsAccessBridge 46 except WindowsError: 47 bridgeDll=None 48 49 #Definitions of access bridge types, structs and prototypes 50 51 jint=c_int 52 jfloat=c_float
53 -class JOBJECT64(c_int if legacyAccessBridge else c_int64):
54 pass
55 56 MAX_STRING_SIZE=1024 57 SHORT_STRING_SIZE=256
58 59 -class AccessBridgeVersionInfo(Structure):
60 _fields_=[ 61 ('VMVersion',WCHAR*SHORT_STRING_SIZE), 62 ('bridgeJavaClassVersion',WCHAR*SHORT_STRING_SIZE), 63 ('bridgeJavaDLLVersion',WCHAR*SHORT_STRING_SIZE), 64 ('bridgeWinDLLVersion',WCHAR*SHORT_STRING_SIZE), 65 ]
66
67 -class AccessibleContextInfo(Structure):
68 _fields_=[ 69 ('name',WCHAR*MAX_STRING_SIZE), 70 ('description',WCHAR*MAX_STRING_SIZE), 71 ('role',WCHAR*SHORT_STRING_SIZE), 72 ('role_en_US',WCHAR*SHORT_STRING_SIZE), 73 ('states',WCHAR*SHORT_STRING_SIZE), 74 ('states_en_US',WCHAR*SHORT_STRING_SIZE), 75 ('indexInParent',jint), 76 ('childrenCount',jint), 77 ('x',jint), 78 ('y',jint), 79 ('width',jint), 80 ('height',jint), 81 ('accessibleComponent',BOOL), 82 ('accessibleAction',BOOL), 83 ('accessibleSelection',BOOL), 84 ('accessibleText',BOOL), 85 ('accessibleValue',BOOL), 86 ]
87
88 -class AccessibleTextInfo(Structure):
89 _fields_=[ 90 ('charCount',jint), 91 ('caretIndex',jint), 92 ('indexAtPoint',jint), 93 ]
94
95 -class AccessibleTextItemsInfo(Structure):
96 _fields_=[ 97 ('letter',WCHAR), 98 ('word',WCHAR*SHORT_STRING_SIZE), 99 ('sentence',WCHAR*MAX_STRING_SIZE), 100 ]
101
102 -class AccessibleTextSelectionInfo(Structure):
103 _fields_=[ 104 ('selectionStartIndex',jint), 105 ('selectionEndIndex',jint), 106 ('selectedText',WCHAR*MAX_STRING_SIZE), 107 ]
108
109 -class AccessibleTextRectInfo(Structure):
110 _fields_=[ 111 ('x',jint), 112 ('y',jint), 113 ('width',jint), 114 ('height',jint), 115 ]
116
117 -class AccessibleTextAttributesInfo(Structure):
118 _fields_=[ 119 ('bold',BOOL), 120 ('italic',BOOL), 121 ('underline',BOOL), 122 ('strikethrough',BOOL), 123 ('superscript',BOOL), 124 ('subscript',BOOL), 125 ('backgroundColor',WCHAR*SHORT_STRING_SIZE), 126 ('foregroundColor',WCHAR*SHORT_STRING_SIZE), 127 ('fontFamily',WCHAR*SHORT_STRING_SIZE), 128 ('fontSize',jint), 129 ('alignment',jint), 130 ('bidiLevel',jint), 131 ('firstLineIndent',jfloat), 132 ('LeftIndent',jfloat), 133 ('rightIndent',jfloat), 134 ('lineSpacing',jfloat), 135 ('spaceAbove',jfloat), 136 ('spaceBelow',jfloat), 137 ('fullAttributesString',WCHAR*MAX_STRING_SIZE), 138 ]
139 140 MAX_RELATION_TARGETS = 25 141 MAX_RELATIONS = 5
142 143 -class AccessibleRelationInfo(Structure):
144 _fields_ = [ 145 ("key", WCHAR * SHORT_STRING_SIZE), 146 ("targetCount", jint), 147 ("targets", JOBJECT64 * MAX_RELATION_TARGETS), 148 ]
149
150 -class AccessibleRelationSetInfo(Structure):
151 _fields_ = [ 152 ("relationCount", jint), 153 ("relations", AccessibleRelationInfo * MAX_RELATIONS), 154 ]
155 156 MAX_ACTION_INFO = 256 157 MAX_ACTIONS_TO_DO = 32
158 159 -class AccessibleActionInfo(Structure):
160 _fields_ = ( 161 ("name", c_wchar * SHORT_STRING_SIZE), 162 )
163
164 -class AccessibleActions(Structure):
165 _fields_ = ( 166 ("actionsCount", jint), 167 ("actionInfo", AccessibleActionInfo * MAX_ACTION_INFO), 168 )
169
170 -class AccessibleActionsToDo(Structure):
171 _fields_ = ( 172 ("actionsCount", jint), 173 ("actions", AccessibleActionInfo * MAX_ACTIONS_TO_DO), 174 )
175 176 AccessBridge_FocusGainedFP=CFUNCTYPE(None,c_long,JOBJECT64,JOBJECT64) 177 AccessBridge_PropertyStateChangeFP=CFUNCTYPE(None,c_long,JOBJECT64,JOBJECT64,c_wchar_p,c_wchar_p) 178 AccessBridge_PropertyCaretChangeFP=CFUNCTYPE(None,c_long,JOBJECT64,JOBJECT64,c_int,c_int) 179 AccessBridge_PropertyActiveDescendentChangeFP=CFUNCTYPE(None,c_long,JOBJECT64,JOBJECT64,JOBJECT64,JOBJECT64) 180 181 #Appropriately set the return and argument types of all the access bridge dll functions 182 if bridgeDll: 183 _fixBridgeFunc(None,'Windows_run') 184 _fixBridgeFunc(None,'setFocusGainedFP',c_void_p) 185 _fixBridgeFunc(None,'setPropertyStateChangeFP',c_void_p) 186 _fixBridgeFunc(None,'setPropertyCaretChangeFP',c_void_p) 187 _fixBridgeFunc(None,'setPropertyActiveDescendentChangeFP',c_void_p) 188 _fixBridgeFunc(None,'releaseJavaObject',c_long,JOBJECT64) 189 _fixBridgeFunc(BOOL,'getVersionInfo',POINTER(AccessBridgeVersionInfo),errcheck=True) 190 _fixBridgeFunc(BOOL,'isJavaWindow',HWND) 191 _fixBridgeFunc(BOOL,'isSameObject',c_long,JOBJECT64,JOBJECT64) 192 _fixBridgeFunc(BOOL,'getAccessibleContextFromHWND',HWND,POINTER(c_long),POINTER(JOBJECT64),errcheck=True) 193 _fixBridgeFunc(HWND,'getHWNDFromAccessibleContext',c_long,JOBJECT64,errcheck=True) 194 _fixBridgeFunc(BOOL,'getAccessibleContextAt',c_long,JOBJECT64,jint,jint,POINTER(JOBJECT64),errcheck=True) 195 _fixBridgeFunc(BOOL,'getAccessibleContextWithFocus',HWND,POINTER(c_long),POINTER(JOBJECT64),errcheck=True) 196 _fixBridgeFunc(BOOL,'getAccessibleContextInfo',c_long,JOBJECT64,POINTER(AccessibleContextInfo),errcheck=True) 197 _fixBridgeFunc(JOBJECT64,'getAccessibleChildFromContext',c_long,JOBJECT64,jint,errcheck=True) 198 _fixBridgeFunc(JOBJECT64,'getAccessibleParentFromContext',c_long,JOBJECT64) 199 _fixBridgeFunc(BOOL,'getAccessibleRelationSet',c_long,JOBJECT64,POINTER(AccessibleRelationSetInfo),errcheck=True) 200 _fixBridgeFunc(BOOL,'getAccessibleTextInfo',c_long,JOBJECT64,POINTER(AccessibleTextInfo),jint,jint,errcheck=True) 201 _fixBridgeFunc(BOOL,'getAccessibleTextItems',c_long,JOBJECT64,POINTER(AccessibleTextItemsInfo),jint,errcheck=True) 202 _fixBridgeFunc(BOOL,'getAccessibleTextSelectionInfo',c_long,JOBJECT64,POINTER(AccessibleTextSelectionInfo),errcheck=True) 203 _fixBridgeFunc(BOOL,'getAccessibleTextAttributes',c_long,JOBJECT64,jint,POINTER(AccessibleTextAttributesInfo),errcheck=True) 204 _fixBridgeFunc(BOOL,'getAccessibleTextLineBounds',c_long,JOBJECT64,jint,POINTER(jint),POINTER(jint),errcheck=True) 205 _fixBridgeFunc(BOOL,'getAccessibleTextRange',c_long,JOBJECT64,jint,jint,POINTER(c_wchar),c_short,errcheck=True) 206 _fixBridgeFunc(BOOL,'getCurrentAccessibleValueFromContext',c_long,JOBJECT64,POINTER(c_wchar),c_short,errcheck=True) 207 _fixBridgeFunc(BOOL,'selectTextRange',c_long,JOBJECT64,c_int,c_int,errcheck=True) 208 _fixBridgeFunc(BOOL,'getTextAttributesInRange',c_long,JOBJECT64,c_int,c_int,POINTER(AccessibleTextAttributesInfo),POINTER(c_short),errcheck=True) 209 _fixBridgeFunc(JOBJECT64,'getTopLevelObject',c_long,JOBJECT64,errcheck=True) 210 _fixBridgeFunc(c_int,'getObjectDepth',c_long,JOBJECT64) 211 _fixBridgeFunc(JOBJECT64,'getActiveDescendent',c_long,JOBJECT64) 212 _fixBridgeFunc(BOOL,'requestFocus',c_long,JOBJECT64,errcheck=True) 213 _fixBridgeFunc(BOOL,'setCaretPosition',c_long,JOBJECT64,c_int,errcheck=True) 214 _fixBridgeFunc(BOOL,'getCaretLocation',c_long,JOBJECT64,POINTER(AccessibleTextRectInfo),jint,errcheck=True) 215 _fixBridgeFunc(BOOL,'getAccessibleActions',c_long,JOBJECT64,POINTER(AccessibleActions),errcheck=True) 216 _fixBridgeFunc(BOOL,'doAccessibleActions',c_long,JOBJECT64,POINTER(AccessibleActionsToDo),POINTER(jint),errcheck=True) 217 218 #NVDA specific code 219 220 isRunning=False 221 vmIDsToWindowHandles={} 222 internalFunctionQueue=Queue.Queue(1000) 223 internalFunctionQueue.__name__="JABHandler.internalFunctionQueue"
224 225 -def internalQueueFunction(func,*args,**kwargs):
226 internalFunctionQueue.put_nowait((func,args,kwargs))
227
228 -class JABContext(object):
229
230 - def __init__(self,hwnd=None,vmID=None,accContext=None):
231 if hwnd and not vmID: 232 vmID=c_int() 233 accContext=JOBJECT64() 234 bridgeDll.getAccessibleContextFromHWND(hwnd,byref(vmID),byref(accContext)) 235 vmID=vmID.value 236 #Record this vm ID and window handle for later use with other objects 237 vmIDsToWindowHandles[vmID]=hwnd 238 elif vmID and not hwnd: 239 hwnd=vmIDsToWindowHandles.get(vmID) 240 if not hwnd: 241 topAC=bridgeDll.getTopLevelObject(vmID,accContext) 242 hwnd=bridgeDll.getHWNDFromAccessibleContext(vmID,topAC) 243 bridgeDll.releaseJavaObject(vmID,topAC) 244 #Record this vm ID and window handle for later use with other objects 245 vmIDsToWindowHandles[vmID]=hwnd 246 self.hwnd=hwnd 247 self.vmID=vmID 248 self.accContext=accContext
249
250 - def __del__(self):
251 if isRunning: 252 try: 253 bridgeDll.releaseJavaObject(self.vmID,self.accContext) 254 except: 255 log.debugWarning("Error releasing java object",exc_info=True)
256 257
258 - def __eq__(self,jabContext):
259 if self.vmID==jabContext.vmID and bridgeDll.isSameObject(self.vmID,self.accContext,jabContext.accContext): 260 return True 261 else: 262 return False
263
264 - def __ne__(self,jabContext):
265 if self.vmID!=jabContext.vmID or not bridgeDll.isSameObject(self.vmID,self.accContext,jabContext.accContext): 266 return True 267 else: 268 return False
269
270 - def getVersionInfo(self):
271 info=AccessBridgeVersionInfo() 272 bridgeDll.getVersionInfo(self.vmID,byref(info)) 273 return info
274
275 - def getAccessibleContextInfo(self):
276 info=AccessibleContextInfo() 277 bridgeDll.getAccessibleContextInfo(self.vmID,self.accContext,byref(info)) 278 return info
279
280 - def getAccessibleTextInfo(self,x,y):
281 textInfo=AccessibleTextInfo() 282 bridgeDll.getAccessibleTextInfo(self.vmID,self.accContext,byref(textInfo),x,y) 283 return textInfo
284
285 - def getAccessibleTextItems(self,index):
286 textItemsInfo=AccessibleTextItemsInfo() 287 bridgeDll.getAccessibleTextItems(self.vmID,self.accContext,byref(textItemsInfo),index) 288 return textItemsInfo
289
291 textSelectionInfo=AccessibleTextSelectionInfo() 292 bridgeDll.getAccessibleTextSelectionInfo(self.vmID,self.accContext,byref(textSelectionInfo)) 293 return textSelectionInfo
294
295 - def getAccessibleTextRange(self,start,end):
296 length=((end+1)-start) 297 if length<=0: 298 return "\n" 299 text=create_unicode_buffer(length+1) 300 bridgeDll.getAccessibleTextRange(self.vmID,self.accContext,start,end,text,length) 301 return text.value
302
303 - def getAccessibleTextLineBounds(self,index):
304 index=max(index,0) 305 log.debug("lineBounds: index %s"%index) 306 #Java returns end as the last character, not end as past the last character 307 startIndex=c_int() 308 endIndex=c_int() 309 bridgeDll.getAccessibleTextLineBounds(self.vmID,self.accContext,index,byref(startIndex),byref(endIndex)) 310 start=startIndex.value 311 end=endIndex.value 312 log.debug("line bounds: start %s, end %s"%(start,end)) 313 if end<start: 314 # Invalid or empty line. 315 return (0,-1) 316 ok=False 317 # OpenOffice sometimes returns offsets encompassing more than one line, so try to narrow them down. 318 # Try to retract the end offset. 319 while not ok: 320 bridgeDll.getAccessibleTextLineBounds(self.vmID,self.accContext,end,byref(startIndex),byref(endIndex)) 321 tempStart=max(startIndex.value,0) 322 tempEnd=max(endIndex.value,0) 323 log.debug("line bounds: tempStart %s, tempEnd %s"%(tempStart,tempEnd)) 324 if tempStart>(index+1): 325 # This line starts after the requested index, so set end to point at the line before. 326 end=tempStart-1 327 else: 328 ok=True 329 ok=False 330 # Try to retract the start. 331 while not ok: 332 bridgeDll.getAccessibleTextLineBounds(self.vmID,self.accContext,start,byref(startIndex),byref(endIndex)) 333 tempStart=max(startIndex.value,0) 334 tempEnd=max(endIndex.value,0) 335 log.debug("line bounds: tempStart %s, tempEnd %s"%(tempStart,tempEnd)) 336 if tempEnd<(index-1): 337 # This line ends before the requested index, so set start to point at the line after. 338 start=tempEnd+1 339 else: 340 ok=True 341 log.debug("line bounds: returning %s, %s"%(start,end)) 342 return (start,end)
343 344
346 accContext=bridgeDll.getAccessibleParentFromContext(self.vmID,self.accContext) 347 if accContext: 348 return self.__class__(self.hwnd,self.vmID,accContext) 349 else: 350 return None
351
352 - def getAccessibleChildFromContext(self,index):
353 accContext=bridgeDll.getAccessibleChildFromContext(self.vmID,self.accContext,index) 354 if accContext: 355 return self.__class__(self.hwnd,self.vmID,accContext) 356 else: 357 return None
358
359 - def getActiveDescendent(self):
360 accContext=bridgeDll.getActiveDescendent(self.vmID,self.accContext) 361 if accContext: 362 return self.__class__(self.hwnd,self.vmID,accContext) 363 else: 364 return None
365
366 - def getAccessibleContextAt(self,x,y):
367 newAccContext=JOBJECT64() 368 res=bridgeDll.getAccessibleContextAt(self.vmID,self.accContext,x,y,byref(newAccContext)) 369 if not res or not newAccContext: 370 return None 371 if not bridgeDll.isSameObject(self.vmID,newAccContext,self.accContext): 372 return self.__class__(self.hwnd,self.vmID,newAccContext) 373 elif newAccContext!=self.accContext: 374 bridgeDll.releaseJavaObject(self.vmID,newAccContext) 375 return None
376
378 buf=create_unicode_buffer(SHORT_STRING_SIZE+1) 379 bridgeDll.getCurrentAccessibleValueFromContext(self.vmID,self.accContext,buf,SHORT_STRING_SIZE) 380 return buf.value
381
382 - def selectTextRange(self,start,end):
383 bridgeDll.selectTextRange(start,end)
384
385 - def setCaretPosition(self,offset):
386 bridgeDll.setCaretPosition(self.vmID,self.accContext,offset)
387
388 - def getTextAttributesInRange(self, startIndex, endIndex):
389 attributes = AccessibleTextAttributesInfo() 390 length = c_short() 391 bridgeDll.getTextAttributesInRange(self.vmID, self.accContext, startIndex, endIndex, byref(attributes), byref(length)) 392 return attributes, length.value
393
394 - def getAccessibleRelationSet(self):
395 relations = AccessibleRelationSetInfo() 396 bridgeDll.getAccessibleRelationSet(self.vmID, self.accContext, byref(relations)) 397 return relations
398
399 @AccessBridge_FocusGainedFP 400 -def internal_event_focusGained(vmID, event,source):
401 internalQueueFunction(event_gainFocus,vmID,source) 402 bridgeDll.releaseJavaObject(vmID,event)
403
404 -def event_gainFocus(vmID,accContext):
405 tempContext=accContext 406 while tempContext: 407 try: 408 tempContext=bridgeDll.getActiveDescendent(vmID,tempContext) 409 except: 410 tempContext=None 411 try: 412 depth=bridgeDll.getObjectDepth(vmID,tempContext) 413 except: 414 depth=-1 415 if tempContext and (depth<=0 or bridgeDll.isSameObject(vmID,accContext,tempContext)): 416 tempContext=None 417 if tempContext: 418 bridgeDll.releaseJavaObject(vmID,accContext) 419 accContext=tempContext 420 jabContext=JABContext(vmID=vmID,accContext=accContext) 421 if not winUser.isDescendantWindow(winUser.getForegroundWindow(),jabContext.hwnd): 422 return 423 focus=eventHandler.lastQueuedFocusObject 424 if (isinstance(focus,NVDAObjects.JAB.JAB) and focus.jabContext==jabContext): 425 return 426 obj=NVDAObjects.JAB.JAB(jabContext=jabContext) 427 if obj.role==controlTypes.ROLE_UNKNOWN: 428 return 429 eventHandler.queueEvent("gainFocus",obj)
430
431 @AccessBridge_PropertyActiveDescendentChangeFP 432 -def internal_event_activeDescendantChange(vmID, event,source,oldDescendant,newDescendant):
433 internalQueueFunction(event_gainFocus,vmID,newDescendant) 434 for accContext in [event,oldDescendant]: 435 bridgeDll.releaseJavaObject(vmID,accContext)
436
437 @AccessBridge_PropertyStateChangeFP 438 -def internal_event_stateChange(vmID,event,source,oldState,newState):
439 internalQueueFunction(event_stateChange,vmID,source,oldState,newState) 440 bridgeDll.releaseJavaObject(vmID,event)
441
442 -def event_stateChange(vmID,accContext,oldState,newState):
443 jabContext=JABContext(vmID=vmID,accContext=accContext) 444 focus=api.getFocusObject() 445 #For broken tabs and menus, we need to watch for things being selected and pretend its a focus change 446 stateList=newState.split(',') 447 if "focused" in stateList or "selected" in stateList: 448 obj=NVDAObjects.JAB.JAB(jabContext=jabContext) 449 if not obj: 450 return 451 if focus!=obj and eventHandler.lastQueuedFocusObject!=obj and obj.role in (controlTypes.ROLE_MENUITEM,controlTypes.ROLE_TAB,controlTypes.ROLE_MENU): 452 eventHandler.queueEvent("gainFocus",obj) 453 return 454 if isinstance(focus,NVDAObjects.JAB.JAB) and focus.jabContext==jabContext: 455 obj=focus 456 else: 457 obj=NVDAObjects.JAB.JAB(jabContext=jabContext) 458 if not obj: 459 return 460 eventHandler.queueEvent("stateChange",obj)
461
462 @AccessBridge_PropertyCaretChangeFP 463 -def internal_event_caretChange(vmID, event,source,oldPos,newPos):
464 if oldPos<0 and newPos>=0: 465 internalQueueFunction(event_gainFocus,vmID,source) 466 bridgeDll.releaseJavaObject(vmID,event)
467
468 -def event_enterJavaWindow(hwnd):
469 vmID=c_int() 470 accContext=JOBJECT64() 471 try: 472 bridgeDll.getAccessibleContextFromHWND(hwnd,byref(vmID),byref(accContext)) 473 except: 474 return 475 vmID=vmID.value 476 vmIDsToWindowHandles[vmID]=hwnd 477 lastFocus=eventHandler.lastQueuedFocusObject 478 if isinstance(lastFocus,NVDAObjects.JAB.JAB) and lastFocus.windowHandle==hwnd: 479 return 480 internalQueueFunction(event_gainFocus,vmID,accContext)
481
482 -def isJavaWindow(hwnd):
483 if not bridgeDll or not isRunning: 484 return False 485 return bridgeDll.isJavaWindow(hwnd)
486
487 -def initialize():
488 global isRunning 489 if not bridgeDll: 490 raise NotImplementedError("dll not available") 491 bridgeDll.Windows_run() 492 #Accept wm_copydata and any wm_user messages from other processes even if running with higher privilages 493 ChangeWindowMessageFilter=getattr(windll.user32,'ChangeWindowMessageFilter',None) 494 if ChangeWindowMessageFilter: 495 if not ChangeWindowMessageFilter(winUser.WM_COPYDATA,1): 496 raise WinError() 497 for msg in xrange(winUser.WM_USER+1,65535): 498 if not ChangeWindowMessageFilter(msg,1): 499 raise WinError() 500 #Register java events 501 bridgeDll.setFocusGainedFP(internal_event_focusGained) 502 bridgeDll.setPropertyActiveDescendentChangeFP(internal_event_activeDescendantChange) 503 bridgeDll.setPropertyStateChangeFP(internal_event_stateChange) 504 bridgeDll.setPropertyCaretChangeFP(internal_event_caretChange) 505 isRunning=True
506
507 -def pumpAll():
508 if isRunning: 509 queueHandler.flushQueue(internalFunctionQueue)
510
511 -def terminate():
512 global isRunning, bridgeDll 513 if not bridgeDll or not isRunning: 514 return 515 bridgeDll.setFocusGainedFP(None) 516 bridgeDll.setPropertyActiveDescendentChangeFP(None) 517 bridgeDll.setPropertyStateChangeFP(None) 518 bridgeDll.setPropertyCaretChangeFP(None) 519 h=bridgeDll._handle 520 bridgeDll=None 521 if legacyAccessBridge: 522 del cdll.windowsAccessBridge 523 else: 524 delattr(cdll,'windowsAccessBridge-32') 525 windll.kernel32.FreeLibrary(h) 526 isRunning=False
527