Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

No longer require pyWin32 #9639

Merged
merged 5 commits into from
Jun 3, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions nvdaHelper/local/nvdaHelperLocal.def
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ EXPORTS
dllImportTableHooks_unhookSingle
audioDucking_shouldDelay
logMessage
getOleClipboardText
44 changes: 44 additions & 0 deletions nvdaHelper/local/oleUtils.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
This file is a part of the NVDA project.
URL: http://www.nvda-project.org/
Copyright 2019 NV Access Limited.
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
*/

#include <windows.h>
#include <wtypes.h>
#include <common/log.h>

/*
* Fetches a text representation of the given OLE data object
* @param dataObject an IDataObject interface of an OLE object
* @param text a pointer to a BSTR which will hold the resulting text
* @return S_OK on success or an OLE error code.
*/
HRESULT getOleClipboardText(IDataObject* dataObject, BSTR* text) {
FORMATETC format={CF_UNICODETEXT,nullptr,DVASPECT_CONTENT,-1,TYMED_HGLOBAL};
STGMEDIUM medium={0};
HRESULT res=dataObject->GetData(&format,&medium);
if(FAILED(res)) {
LOG_DEBUGWARNING(L"IDataObject::getData failed with error "<<res);
return res;
}
if(medium.tymed!=TYMED_HGLOBAL||!medium.hGlobal) {
LOG_DEBUGWARNING(L"Got back invalid medium");
return E_FAIL;
}
LPVOID addr=GlobalLock(medium.hGlobal);
if(addr) {
*text=SysAllocString((wchar_t*)addr);
}
GlobalUnlock(medium.hGlobal);
ReleaseStgMedium(&medium);
return res;
}
1 change: 1 addition & 0 deletions nvdaHelper/local/sconscript
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ localLib=env.SharedLibrary(
"textUtils.cpp",
"UIAUtils.cpp",
"mixer.cpp",
"oleUtils.cpp",
],
LIBS=[
"advapi32.lib",
Expand Down
15 changes: 4 additions & 11 deletions source/NVDAObjects/window/edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,10 @@
import struct
import ctypes
from comtypes import COMError
import pythoncom
import win32clipboard
import oleTypes
import colors
import globalVars
import NVDAHelper
import eventHandler
import comInterfaces.tom
from logHandler import log
Expand Down Expand Up @@ -578,15 +577,9 @@ def _getEmbeddedObjectLabel(self,embedRangeObj):
except comtypes.COMError:
dataObj=None
if dataObj:
try:
dataObj=pythoncom._univgw.interface(hash(dataObj),pythoncom.IID_IDataObject)
format=(win32clipboard.CF_UNICODETEXT, None, pythoncom.DVASPECT_CONTENT, -1, pythoncom.TYMED_HGLOBAL)
medium=dataObj.GetData(format)
buf=ctypes.create_string_buffer(medium.data)
buf=ctypes.cast(buf,ctypes.c_wchar_p)
label=buf.value
except:
pass
text=comtypes.BSTR()
res=NVDAHelper.localLib.getOleClipboardText(dataObj,ctypes.byref(text));
label=text.value
if label:
return label
# As a final fallback (e.g. could not get display model text for Outlook Express), use the embedded object's user type (e.g. "recipient").
Expand Down
39 changes: 12 additions & 27 deletions source/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

"""General functions for NVDA"""

import ctypes
import config
import textInfos
import review
Expand All @@ -18,8 +19,6 @@
import NVDAObjects.IAccessible
import winUser
import controlTypes
import win32clipboard
import win32con
import eventHandler
import braille
import watchdog
Expand Down Expand Up @@ -286,37 +285,23 @@ def copyToClip(text):
@param text: the text which will be copied to the clipboard
@type text: string
"""
if isinstance(text,basestring) and len(text)>0 and not text.isspace():
try:
win32clipboard.OpenClipboard()
except win32clipboard.error:
return False
try:
win32clipboard.EmptyClipboard()
win32clipboard.SetClipboardData(win32con.CF_UNICODETEXT, text)
finally:
win32clipboard.CloseClipboard()
win32clipboard.OpenClipboard() # there seems to be a bug so to retrieve unicode text we have to reopen the clipboard
try:
got = win32clipboard.GetClipboardData(win32con.CF_UNICODETEXT)
finally:
win32clipboard.CloseClipboard()
if got == text:
return True
return False
if not isinstance(text,basestring) or len(text)==0:
LeonarddeR marked this conversation as resolved.
Show resolved Hide resolved
return False
import gui
with winUser.openClipboard(gui.mainFrame.Handle):
winUser.emptyClipboard()
winUser.setClipboardData(winUser.CF_UNICODETEXT,text)
got=getClipData()
return got == text

def getClipData():
"""Receives text from the windows clipboard.
@returns: Clipboard text
@rtype: string
"""
text = ""
win32clipboard.OpenClipboard()
try:
text = win32clipboard.GetClipboardData(win32con.CF_UNICODETEXT)
finally:
win32clipboard.CloseClipboard()
return text
import gui
with winUser.openClipboard(gui.mainFrame.Handle):
return winUser.getClipboardData(winUser.CF_UNICODETEXT) or u""

def getStatusBar():
"""Obtain the status bar for the current foreground object.
Expand Down
6 changes: 4 additions & 2 deletions source/bdDetect.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,17 +20,19 @@
import hwPortUtils
import braille
import winKernel
import winUser
import core
import ctypes
from logHandler import log
import config
import time
import thread
from win32con import WM_DEVICECHANGE, DBT_DEVNODES_CHANGED
import appModuleHandler
from baseObject import AutoPropertyObject
import re

DBT_DEVNODES_CHANGED=7

_driverDevices = OrderedDict()
USB_ID_REGEX = re.compile(r"^VID_[0-9A-F]{4}&PID_[0-9A-F]{4}$", re.U)

Expand Down Expand Up @@ -295,7 +297,7 @@ def rescan(self, usb=True, bluetooth=True, limitToDevices=None):
self._startBgScan(usb=usb, bluetooth=bluetooth, limitToDevices=limitToDevices)

def handleWindowMessage(self, msg=None, wParam=None):
if msg == WM_DEVICECHANGE and wParam == DBT_DEVNODES_CHANGED:
if msg == winUser.WM_DEVICECHANGE and wParam == DBT_DEVNODES_CHANGED:
self.rescan(bluetooth=self._detectBluetooth, limitToDevices=self._limitToDevices)

def pollBluetoothDevices(self):
Expand Down
2 changes: 1 addition & 1 deletion source/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ def onEndSession(evt):
import windowUtils
class MessageWindow(windowUtils.CustomWindow):
className = u"wxWindowClassNR"
#Just define these constants here, so we don't have to import win32con
# Windows constants for power / display changes
WM_POWERBROADCAST = 0x218
WM_DISPLAYCHANGE = 0x7e
PBT_APMPOWERSTATUSCHANGE = 0xA
Expand Down
5 changes: 2 additions & 3 deletions source/nvda.pyw
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ except:

import time
import argparse
import win32con
import globalVars
import config
import logHandler
Expand Down Expand Up @@ -120,7 +119,7 @@ parser.add_argument('--ease-of-access',action="store_true",dest='easeOfAccess',d

def terminateRunningNVDA(window):
processID,threadID=winUser.getWindowThreadProcessID(window)
winUser.PostMessage(window,win32con.WM_QUIT,0,0)
winUser.PostMessage(window,winUser.WM_QUIT,0,0)
h=winKernel.openProcess(winKernel.SYNCHRONIZE,False,processID)
if not h:
# The process is already dead.
Expand Down Expand Up @@ -217,7 +216,7 @@ 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
if not ctypes.windll.user32.ChangeWindowMessageFilter(win32con.WM_QUIT,1):
if not ctypes.windll.user32.ChangeWindowMessageFilter(winUser.WM_QUIT,1):
raise WinError()
# Make this the last application to be shut down and don't display a retry dialog box.
winKernel.SetProcessShutdownParameters(0x100, winKernel.SHUTDOWN_NORETRY)
Expand Down
2 changes: 0 additions & 2 deletions source/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -208,8 +208,6 @@ def getRecursiveDataFiles(dest,source,excludes=()):
"nvdaBuiltin",
# #3368: bisect was implicitly included with Python 2.7.3, but isn't with 2.7.5.
"bisect",
# Also, the previous service executable used win32api, which some add-ons use for various purposes.
"win32api",
],
}},
data_files=[
Expand Down
13 changes: 11 additions & 2 deletions source/winInputHook.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,17 @@
import time
from ctypes import *
from ctypes.wintypes import *
from win32con import WM_QUIT, HC_ACTION, WH_KEYBOARD_LL, LLKHF_UP, LLKHF_EXTENDED, LLKHF_INJECTED, WH_MOUSE_LL, LLMHF_INJECTED
import watchdog
import winUser

# Some Windows constants
HC_ACTION = 0
WH_KEYBOARD_LL = 13
LLKHF_UP = 128
LLKHF_EXTENDED = 1
LLKHF_INJECTED = 16
WH_MOUSE_LL = 14
LLMHF_INJECTED = 1

class KBDLLHOOKSTRUCT(Structure):
_fields_=[
Expand Down Expand Up @@ -97,6 +106,6 @@ def terminate():
raise RuntimeError("winInputHook not running")
hookThreadRefCount-=1
if hookThreadRefCount==0:
windll.user32.PostThreadMessageW(hookThread.ident,WM_QUIT,0,0)
windll.user32.PostThreadMessageW(hookThread.ident,winUser.WM_QUIT,0,0)
hookThread.join()
hookThread=None
54 changes: 54 additions & 0 deletions source/winKernel.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#This file is covered by the GNU General Public License.
#See the file COPYING for more details.

import contextlib
import ctypes
import ctypes.wintypes
from ctypes import *
Expand Down Expand Up @@ -329,3 +330,56 @@ def DuplicateHandle(sourceProcessHandle, sourceHandle, targetProcessHandle, desi

PAPCFUNC = ctypes.WINFUNCTYPE(None, ctypes.wintypes.ULONG)
THREAD_SET_CONTEXT = 16

GMEM_MOVEABLE=2

class HGLOBAL(HANDLE):
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could you add a short descriptive doc string here?

"""
A class for the HGLOBAL Windows handle type.
This class can auto-free the handle when it goes out of scope,
and also contains a classmethod for alloc,
And a context manager compatible method for locking.
"""

def __init__(self,h,autoFree=True):
"""
@param h: the raw Windows HGLOBAL handle
@param autoFree: True by default, the handle will automatically be freed with GlobalFree
when this object goes out of scope.
"""
super(HGLOBAL,self).__init__(h)
self._autoFree=autoFree

def __del__(self):
if self and self._autoFree:
windll.kernel32.GlobalFree(self)

@classmethod
def alloc(cls,flags,size):
"""
Allocates global memory with GlobalAlloc
providing it as an instance of this class.
This method Takes the same arguments as GlobalAlloc.
"""
h=windll.kernel32.GlobalAlloc(flags,size)
return cls(h)

@contextlib.contextmanager
def lock(self):
"""
Used as a context manager,
This method locks the global memory with GlobalLock,
providing the usable memory address to the body of the 'with' statement.
When the body completes, GlobalUnlock is automatically called.
"""
try:
yield windll.kernel32.GlobalLock(self)
finally:
windll.kernel32.GlobalUnlock(self)

def forget(self):
"""
Sets this HGLOBAL value to NULL, forgetting the existing value.
Necessary if you pass this HGLOBAL to an API that takes ownership and therefore will handle freeing itself.
"""
self.value=None
Loading