mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Switch to non WMI based method of detecting devices on windows
This commit is contained in:
parent
8a5d3cfaba
commit
786e50e2cf
@ -5,7 +5,7 @@ __copyright__ = '2008, Kovid Goyal <kovid at kovidgoyal.net>'
|
|||||||
Device drivers.
|
Device drivers.
|
||||||
'''
|
'''
|
||||||
|
|
||||||
import sys, os, time, pprint
|
import sys, time, pprint
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from StringIO import StringIO
|
from StringIO import StringIO
|
||||||
|
|
||||||
@ -29,7 +29,7 @@ def strftime(epoch, zone=time.gmtime):
|
|||||||
|
|
||||||
def debug(ioreg_to_tmp=False, buf=None):
|
def debug(ioreg_to_tmp=False, buf=None):
|
||||||
from calibre.customize.ui import device_plugins
|
from calibre.customize.ui import device_plugins
|
||||||
from calibre.devices.scanner import DeviceScanner
|
from calibre.devices.scanner import DeviceScanner, win_pnp_drives
|
||||||
from calibre.constants import iswindows, isosx, __version__
|
from calibre.constants import iswindows, isosx, __version__
|
||||||
from calibre import prints
|
from calibre import prints
|
||||||
oldo, olde = sys.stdout, sys.stderr
|
oldo, olde = sys.stdout, sys.stderr
|
||||||
@ -37,19 +37,11 @@ def debug(ioreg_to_tmp=False, buf=None):
|
|||||||
if buf is None:
|
if buf is None:
|
||||||
buf = StringIO()
|
buf = StringIO()
|
||||||
sys.stdout = sys.stderr = buf
|
sys.stdout = sys.stderr = buf
|
||||||
if iswindows:
|
|
||||||
import pythoncom
|
|
||||||
pythoncom.CoInitialize()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
out = partial(prints, file=buf)
|
out = partial(prints, file=buf)
|
||||||
out('Version:', __version__)
|
out('Version:', __version__)
|
||||||
wmi = Wmi =None
|
|
||||||
if iswindows:
|
|
||||||
wmi = __import__('wmi', globals(), locals(), [], -1)
|
|
||||||
Wmi = wmi.WMI(find_classes=False)
|
|
||||||
s = DeviceScanner()
|
s = DeviceScanner()
|
||||||
s.wmi = Wmi
|
|
||||||
s.scan()
|
s.scan()
|
||||||
devices = (s.devices)
|
devices = (s.devices)
|
||||||
if not iswindows:
|
if not iswindows:
|
||||||
@ -60,21 +52,9 @@ def debug(ioreg_to_tmp=False, buf=None):
|
|||||||
out('USB devices on system:')
|
out('USB devices on system:')
|
||||||
out(pprint.pformat(devices))
|
out(pprint.pformat(devices))
|
||||||
if iswindows:
|
if iswindows:
|
||||||
drives = []
|
drives = win_pnp_drives(debug=True)
|
||||||
out('Drives detected:')
|
out('Drives detected:')
|
||||||
out('\t', '(ID, Partitions, Drive letter)')
|
out(pprint.pformat(drives))
|
||||||
for drive in Wmi.Win32_DiskDrive():
|
|
||||||
if drive.Partitions == 0:
|
|
||||||
continue
|
|
||||||
try:
|
|
||||||
partition = drive.associators("Win32_DiskDriveToDiskPartition")[0]
|
|
||||||
logical_disk = partition.associators('Win32_LogicalDiskToPartition')[0]
|
|
||||||
prefix = logical_disk.DeviceID+os.sep
|
|
||||||
drives.append((str(drive.PNPDeviceID), drive.Index, prefix))
|
|
||||||
except IndexError:
|
|
||||||
drives.append((str(drive.PNPDeviceID), 'No mount points found'))
|
|
||||||
for drive in drives:
|
|
||||||
out('\t', drive)
|
|
||||||
|
|
||||||
ioreg = None
|
ioreg = None
|
||||||
if isosx:
|
if isosx:
|
||||||
@ -84,13 +64,10 @@ def debug(ioreg_to_tmp=False, buf=None):
|
|||||||
ioreg = 'Output from mount:\n\n'+mount+'\n\n'+ioreg
|
ioreg = 'Output from mount:\n\n'+mount+'\n\n'+ioreg
|
||||||
connected_devices = []
|
connected_devices = []
|
||||||
for dev in device_plugins():
|
for dev in device_plugins():
|
||||||
owmi = getattr(dev, 'wmi', None)
|
|
||||||
dev.wmi = Wmi
|
|
||||||
out('Looking for', dev.__class__.__name__)
|
out('Looking for', dev.__class__.__name__)
|
||||||
connected, det = s.is_device_connected(dev, debug=True)
|
connected, det = s.is_device_connected(dev, debug=True)
|
||||||
if connected:
|
if connected:
|
||||||
connected_devices.append((dev, det))
|
connected_devices.append((dev, det))
|
||||||
dev.wmi = owmi
|
|
||||||
|
|
||||||
errors = {}
|
errors = {}
|
||||||
success = False
|
success = False
|
||||||
@ -102,8 +79,6 @@ def debug(ioreg_to_tmp=False, buf=None):
|
|||||||
out(' ')
|
out(' ')
|
||||||
for dev, det in connected_devices:
|
for dev, det in connected_devices:
|
||||||
out('Trying to open', dev.name, '...', end=' ')
|
out('Trying to open', dev.name, '...', end=' ')
|
||||||
owmi = getattr(dev, 'wmi', None)
|
|
||||||
dev.wmi = Wmi
|
|
||||||
try:
|
try:
|
||||||
dev.reset(detected_device=det)
|
dev.reset(detected_device=det)
|
||||||
dev.open()
|
dev.open()
|
||||||
@ -113,8 +88,6 @@ def debug(ioreg_to_tmp=False, buf=None):
|
|||||||
errors[dev] = traceback.format_exc()
|
errors[dev] = traceback.format_exc()
|
||||||
out('failed')
|
out('failed')
|
||||||
continue
|
continue
|
||||||
finally:
|
|
||||||
dev.wmi = owmi
|
|
||||||
success = True
|
success = True
|
||||||
if hasattr(dev, '_main_prefix'):
|
if hasattr(dev, '_main_prefix'):
|
||||||
out('Main memory:', repr(dev._main_prefix))
|
out('Main memory:', repr(dev._main_prefix))
|
||||||
@ -142,7 +115,4 @@ def debug(ioreg_to_tmp=False, buf=None):
|
|||||||
finally:
|
finally:
|
||||||
sys.stdout = oldo
|
sys.stdout = oldo
|
||||||
sys.stderr = olde
|
sys.stderr = olde
|
||||||
if iswindows:
|
|
||||||
import pythoncom
|
|
||||||
pythoncom.CoUninitialize()
|
|
||||||
|
|
||||||
|
@ -60,7 +60,7 @@ class DevicePlugin(Plugin):
|
|||||||
import traceback
|
import traceback
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
|
|
||||||
def is_usb_connected_windows(self, devices_on_system, pnp_id_iterator, debug=False):
|
def is_usb_connected_windows(self, devices_on_system, debug=False):
|
||||||
|
|
||||||
def id_iterator():
|
def id_iterator():
|
||||||
if hasattr(self.VENDOR_ID, 'keys'):
|
if hasattr(self.VENDOR_ID, 'keys'):
|
||||||
@ -85,7 +85,7 @@ class DevicePlugin(Plugin):
|
|||||||
self.test_bcd_windows(device_id, bcd):
|
self.test_bcd_windows(device_id, bcd):
|
||||||
if debug:
|
if debug:
|
||||||
self.print_usb_device_info(device_id)
|
self.print_usb_device_info(device_id)
|
||||||
if self.can_handle_windows(device_id, pnp_id_iterator, debug=debug):
|
if self.can_handle_windows(device_id, debug=debug):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -97,7 +97,7 @@ class DevicePlugin(Plugin):
|
|||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def is_usb_connected(self, devices_on_system, pnp_id_iterator, debug=False):
|
def is_usb_connected(self, devices_on_system, debug=False):
|
||||||
'''
|
'''
|
||||||
Return True, device_info if a device handled by this plugin is currently connected.
|
Return True, device_info if a device handled by this plugin is currently connected.
|
||||||
|
|
||||||
@ -105,7 +105,7 @@ class DevicePlugin(Plugin):
|
|||||||
'''
|
'''
|
||||||
if iswindows:
|
if iswindows:
|
||||||
return self.is_usb_connected_windows(devices_on_system,
|
return self.is_usb_connected_windows(devices_on_system,
|
||||||
pnp_id_iterator, debug=debug), None
|
debug=debug), None
|
||||||
|
|
||||||
vendors_on_system = set([x[0] for x in devices_on_system])
|
vendors_on_system = set([x[0] for x in devices_on_system])
|
||||||
vendors = self.VENDOR_ID if hasattr(self.VENDOR_ID, '__len__') else [self.VENDOR_ID]
|
vendors = self.VENDOR_ID if hasattr(self.VENDOR_ID, '__len__') else [self.VENDOR_ID]
|
||||||
@ -147,7 +147,7 @@ class DevicePlugin(Plugin):
|
|||||||
"""
|
"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
def can_handle_windows(self, device_id, pnp_id_iterator, debug=False):
|
def can_handle_windows(self, device_id, debug=False):
|
||||||
'''
|
'''
|
||||||
Optional method to perform further checks on a device to see if this driver
|
Optional method to perform further checks on a device to see if this driver
|
||||||
is capable of handling it. If it is not it should return False. This method
|
is capable of handling it. If it is not it should return False. This method
|
||||||
|
@ -9,7 +9,7 @@ For usage information run the script.
|
|||||||
import StringIO, sys, time, os
|
import StringIO, sys, time, os
|
||||||
from optparse import OptionParser
|
from optparse import OptionParser
|
||||||
|
|
||||||
from calibre import __version__, iswindows, __appname__
|
from calibre import __version__, __appname__
|
||||||
from calibre.devices.errors import PathError
|
from calibre.devices.errors import PathError
|
||||||
from calibre.utils.terminfo import TerminalController
|
from calibre.utils.terminfo import TerminalController
|
||||||
from calibre.devices.errors import ArgumentError, DeviceError, DeviceLocked
|
from calibre.devices.errors import ArgumentError, DeviceError, DeviceLocked
|
||||||
@ -198,14 +198,9 @@ def main():
|
|||||||
args = args[1:]
|
args = args[1:]
|
||||||
dev = None
|
dev = None
|
||||||
scanner = DeviceScanner()
|
scanner = DeviceScanner()
|
||||||
if iswindows:
|
|
||||||
import wmi, pythoncom
|
|
||||||
pythoncom.CoInitialize()
|
|
||||||
scanner.wmi = wmi.WMI(find_classes=False)
|
|
||||||
scanner.scan()
|
scanner.scan()
|
||||||
connected_devices = []
|
connected_devices = []
|
||||||
for d in device_plugins():
|
for d in device_plugins():
|
||||||
d.wmi = scanner.wmi
|
|
||||||
ok, det = scanner.is_device_connected(d)
|
ok, det = scanner.is_device_connected(d)
|
||||||
if ok:
|
if ok:
|
||||||
dev = d
|
dev = d
|
||||||
|
@ -6,6 +6,7 @@ manner.
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
import sys, os
|
import sys, os
|
||||||
|
from threading import RLock
|
||||||
|
|
||||||
from calibre import iswindows, isosx, plugins, islinux
|
from calibre import iswindows, isosx, plugins, islinux
|
||||||
|
|
||||||
@ -22,6 +23,54 @@ elif isosx:
|
|||||||
except:
|
except:
|
||||||
raise RuntimeError('Failed to load the usbobserver plugin: %s'%plugins['usbobserver'][1])
|
raise RuntimeError('Failed to load the usbobserver plugin: %s'%plugins['usbobserver'][1])
|
||||||
|
|
||||||
|
class WinPNPScanner(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.scanner = None
|
||||||
|
if iswindows:
|
||||||
|
self.scanner = plugins['winutil'][0].get_removable_drives
|
||||||
|
self.lock = RLock()
|
||||||
|
|
||||||
|
def drive_is_ok(self, letter, debug=False):
|
||||||
|
import win32api, win32file
|
||||||
|
with self.lock:
|
||||||
|
oldError = win32api.SetErrorMode(1) #SEM_FAILCRITICALERRORS = 1
|
||||||
|
try:
|
||||||
|
ans = True
|
||||||
|
try:
|
||||||
|
win32file.GetDiskFreeSpaceEx(letter+':\\')
|
||||||
|
except:
|
||||||
|
ans = False
|
||||||
|
return ans
|
||||||
|
finally:
|
||||||
|
win32api.SetErrorMode(oldError)
|
||||||
|
|
||||||
|
def __call__(self, debug=False):
|
||||||
|
if self.scanner is None:
|
||||||
|
return {}
|
||||||
|
try:
|
||||||
|
drives = self.scanner(debug)
|
||||||
|
except:
|
||||||
|
drives = {}
|
||||||
|
if debug:
|
||||||
|
import traceback
|
||||||
|
traceback.print_exc()
|
||||||
|
remove = set([])
|
||||||
|
for letter in drives:
|
||||||
|
if not self.drive_is_ok(letter, debug=debug):
|
||||||
|
remove.add(letter)
|
||||||
|
for letter in remove:
|
||||||
|
drives.pop(letter)
|
||||||
|
ans = {}
|
||||||
|
for key, val in drives.items():
|
||||||
|
val = [x.upper() for x in val]
|
||||||
|
val = [x for x in val if 'USBSTOR' in x]
|
||||||
|
if val:
|
||||||
|
ans[key+':\\'] = val[-1]
|
||||||
|
return ans
|
||||||
|
|
||||||
|
win_pnp_drives = WinPNPScanner()
|
||||||
|
|
||||||
class LinuxScanner(object):
|
class LinuxScanner(object):
|
||||||
|
|
||||||
SYSFS_PATH = os.environ.get('SYSFS_PATH', '/sys')
|
SYSFS_PATH = os.environ.get('SYSFS_PATH', '/sys')
|
||||||
@ -85,26 +134,13 @@ class DeviceScanner(object):
|
|||||||
raise RuntimeError('DeviceScanner requires the /sys filesystem to work.')
|
raise RuntimeError('DeviceScanner requires the /sys filesystem to work.')
|
||||||
self.scanner = win_scanner if iswindows else osx_scanner if isosx else linux_scanner
|
self.scanner = win_scanner if iswindows else osx_scanner if isosx else linux_scanner
|
||||||
self.devices = []
|
self.devices = []
|
||||||
self.wmi = None
|
|
||||||
self.pnp_ids = set([])
|
|
||||||
self.rescan_pnp_ids = True
|
|
||||||
|
|
||||||
def scan(self):
|
def scan(self):
|
||||||
'''Fetch list of connected USB devices from operating system'''
|
'''Fetch list of connected USB devices from operating system'''
|
||||||
self.devices = self.scanner()
|
self.devices = self.scanner()
|
||||||
if self.rescan_pnp_ids:
|
|
||||||
self.pnp_ids = set([])
|
|
||||||
|
|
||||||
def pnp_id_iterator(self):
|
|
||||||
if self.wmi is not None and not self.pnp_ids:
|
|
||||||
for drive in self.wmi.Win32_DiskDrive():
|
|
||||||
if drive.Partitions > 0:
|
|
||||||
self.pnp_ids.add(str(drive.PNPDeviceID))
|
|
||||||
for x in self.pnp_ids:
|
|
||||||
yield x
|
|
||||||
|
|
||||||
def is_device_connected(self, device, debug=False):
|
def is_device_connected(self, device, debug=False):
|
||||||
return device.is_usb_connected(self.devices, self.pnp_id_iterator, debug=debug)
|
return device.is_usb_connected(self.devices, debug=debug)
|
||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv):
|
def main(args=sys.argv):
|
||||||
|
@ -203,18 +203,6 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
|
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def windows_get_drive_prefix(self, drive):
|
|
||||||
prefix = None
|
|
||||||
|
|
||||||
try:
|
|
||||||
partition = drive.associators("Win32_DiskDriveToDiskPartition")[0]
|
|
||||||
logical_disk = partition.associators('Win32_LogicalDiskToPartition')[0]
|
|
||||||
prefix = logical_disk.DeviceID + os.sep
|
|
||||||
except IndexError:
|
|
||||||
pass
|
|
||||||
|
|
||||||
return prefix
|
|
||||||
|
|
||||||
def windows_sort_drives(self, drives):
|
def windows_sort_drives(self, drives):
|
||||||
'''
|
'''
|
||||||
Called to disambiguate main memory and storage card for devices that
|
Called to disambiguate main memory and storage card for devices that
|
||||||
@ -223,8 +211,10 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
'''
|
'''
|
||||||
return drives
|
return drives
|
||||||
|
|
||||||
def can_handle_windows(self, device_id, pnp_id_iterator, debug=False):
|
def can_handle_windows(self, device_id, debug=False):
|
||||||
for pnp_id in pnp_id_iterator():
|
from calibre.devices.scanner import win_pnp_drives
|
||||||
|
drives = win_pnp_drives()
|
||||||
|
for pnp_id in drives.values():
|
||||||
if self.windows_match_device(pnp_id, 'WINDOWS_MAIN_MEM'):
|
if self.windows_match_device(pnp_id, 'WINDOWS_MAIN_MEM'):
|
||||||
return True
|
return True
|
||||||
if debug:
|
if debug:
|
||||||
@ -232,29 +222,20 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def open_windows(self):
|
def open_windows(self):
|
||||||
|
from calibre.devices.scanner import win_pnp_drives
|
||||||
|
|
||||||
def matches_q(drive, attr):
|
time.sleep(5)
|
||||||
q = getattr(self, attr)
|
|
||||||
if q is None: return False
|
|
||||||
if isinstance(q, basestring):
|
|
||||||
q = [q]
|
|
||||||
pnp = str(drive.PNPDeviceID)
|
|
||||||
for x in q:
|
|
||||||
if x in pnp:
|
|
||||||
return True
|
|
||||||
return False
|
|
||||||
|
|
||||||
time.sleep(8)
|
|
||||||
drives = {}
|
drives = {}
|
||||||
c = self.wmi
|
for drive, pnp_id in win_pnp_drives().items():
|
||||||
for drive in c.Win32_DiskDrive():
|
if self.windows_match_device(pnp_id, 'WINDOWS_CARD_A_MEM') and \
|
||||||
pnp_id = str(drive.PNPDeviceID)
|
not drives.get('carda', False):
|
||||||
if self.windows_match_device(pnp_id, 'WINDOWS_CARD_A_MEM') and not drives.get('carda', None):
|
drives['carda'] = drive
|
||||||
drives['carda'] = self.windows_get_drive_prefix(drive)
|
elif self.windows_match_device(pnp_id, 'WINDOWS_CARD_B_MEM') and \
|
||||||
elif self.windows_match_device(pnp_id, 'WINDOWS_CARD_B_MEM') and not drives.get('cardb', None):
|
not drives.get('cardb', False):
|
||||||
drives['cardb'] = self.windows_get_drive_prefix(drive)
|
drives['cardb'] = drive
|
||||||
elif self.windows_match_device(pnp_id, 'WINDOWS_MAIN_MEM') and not drives.get('main', None):
|
elif self.windows_match_device(pnp_id, 'WINDOWS_MAIN_MEM') and \
|
||||||
drives['main'] = self.windows_get_drive_prefix(drive)
|
not drives.get('main', False):
|
||||||
|
drives['main'] = drive
|
||||||
|
|
||||||
if 'main' in drives.keys() and 'carda' in drives.keys() and \
|
if 'main' in drives.keys() and 'carda' in drives.keys() and \
|
||||||
'cardb' in drives.keys():
|
'cardb' in drives.keys():
|
||||||
|
@ -13,7 +13,6 @@ from PyQt4.Qt import QMenu, QAction, QActionGroup, QIcon, SIGNAL, QPixmap, \
|
|||||||
from calibre.customize.ui import available_input_formats, available_output_formats, \
|
from calibre.customize.ui import available_input_formats, available_output_formats, \
|
||||||
device_plugins
|
device_plugins
|
||||||
from calibre.devices.interface import DevicePlugin
|
from calibre.devices.interface import DevicePlugin
|
||||||
from calibre.constants import iswindows
|
|
||||||
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
|
from calibre.gui2.dialogs.choose_format import ChooseFormatDialog
|
||||||
from calibre.utils.ipc.job import BaseJob
|
from calibre.utils.ipc.job import BaseJob
|
||||||
from calibre.devices.scanner import DeviceScanner
|
from calibre.devices.scanner import DeviceScanner
|
||||||
@ -85,7 +84,6 @@ class DeviceManager(Thread):
|
|||||||
self.job_manager = job_manager
|
self.job_manager = job_manager
|
||||||
self.current_job = None
|
self.current_job = None
|
||||||
self.scanner = DeviceScanner()
|
self.scanner = DeviceScanner()
|
||||||
self.wmi = None
|
|
||||||
self.connected_device = None
|
self.connected_device = None
|
||||||
self.ejected_devices = set([])
|
self.ejected_devices = set([])
|
||||||
|
|
||||||
@ -133,7 +131,6 @@ class DeviceManager(Thread):
|
|||||||
self.connected_device = None
|
self.connected_device = None
|
||||||
|
|
||||||
def detect_device(self):
|
def detect_device(self):
|
||||||
self.scanner.rescan_pnp_ids = not self.is_device_connected
|
|
||||||
self.scanner.scan()
|
self.scanner.scan()
|
||||||
if self.is_device_connected:
|
if self.is_device_connected:
|
||||||
connected, detected_device = \
|
connected, detected_device = \
|
||||||
@ -170,15 +167,6 @@ class DeviceManager(Thread):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
if iswindows:
|
|
||||||
import pythoncom
|
|
||||||
pythoncom.CoInitialize()
|
|
||||||
wmi = __import__('wmi', globals(), locals(), [], -1)
|
|
||||||
self.wmi = wmi.WMI(find_classes=False)
|
|
||||||
self.scanner.wmi = self.wmi
|
|
||||||
for x in self.devices:
|
|
||||||
x.wmi = self.wmi
|
|
||||||
try:
|
|
||||||
while self.keep_going:
|
while self.keep_going:
|
||||||
self.detect_device()
|
self.detect_device()
|
||||||
while True:
|
while True:
|
||||||
@ -191,9 +179,6 @@ class DeviceManager(Thread):
|
|||||||
else:
|
else:
|
||||||
break
|
break
|
||||||
time.sleep(self.sleep_time)
|
time.sleep(self.sleep_time)
|
||||||
finally:
|
|
||||||
if iswindows:
|
|
||||||
pythoncom.CoUninitialize()
|
|
||||||
|
|
||||||
|
|
||||||
def create_job(self, func, done, description, args=[], kwargs={}):
|
def create_job(self, func, done, description, args=[], kwargs={}):
|
||||||
|
Loading…
x
Reference in New Issue
Block a user