Use a little C code to interface with windows for device detection and unicode command line arguments

This commit is contained in:
Kovid Goyal 2008-07-24 20:26:58 -07:00
parent e854a9ffd5
commit 50add0c72d
11 changed files with 682 additions and 176 deletions

View File

@ -253,7 +253,7 @@ _check_symlinks_prescript()
print 'Adding plugins'
module_dir = os.path.join(resource_dir, 'lib', 'python2.5', 'lib-dynload')
print 'Adding fontconfig'
for f in glob.glob(os.path.expanduser('~/fontconfig2/*')):
for f in glob.glob(os.path.expanduser('~/fontconfig-bundled/*')):
os.link(f, os.path.join(frameworks_dir, os.path.basename(f)))
dst = os.path.join(resource_dir, 'fonts')
if os.path.exists(dst):

View File

@ -56,10 +56,16 @@ if __name__ == '__main__':
include_dirs=['src/calibre/utils/lzx'])]
if iswindows:
ext_modules.append(Extension('calibre.plugins.winutil',
sources=['src/calibre/utils/winutil.c'], libraries=['shell32'])
sources=['src/calibre/utils/windows/winutil.c'],
libraries=['shell32', 'setupapi'],
include_dirs=['C:/WinDDK/6001.18001/inc/api/'])
)
# Build PyQt extensions
for path in [(os.path.join('src', 'calibre', 'gui2', 'pictureflow'))]:
if isosx:
ext_modules.append(Extension('calibre.plugins.usbobserver',
sources=['src/calibre/driver/usbobserver/usbobserver.c'])
)
def build_PyQt_extension(path):
pro = glob.glob(os.path.join(path, '*.pro'))[0]
raw = open(pro).read()
base = qtplugin = re.search(r'TARGET\s*=\s*(.*)', raw).group(1)
@ -72,7 +78,7 @@ if __name__ == '__main__':
os.chdir('.build')
subprocess.check_call(( (os.path.expanduser('~/qt/bin/qmake') if isosx else 'qmake'), '..'+os.sep+os.path.basename(pro)))
subprocess.check_call(['mingw32-make' if iswindows else 'make'])
os.chdir(os.path.join('..'+(os.sep+'..' if iswindows else ''), 'PyQt'))
os.chdir(os.path.join('..', 'PyQt'))
if not os.path.exists('.build'):
os.mkdir('.build')
os.chdir('.build')
@ -142,5 +148,8 @@ if __name__ == '__main__':
]
)
for path in [(os.path.join('src', 'calibre', 'gui2', 'pictureflow'))]:
build_PyQt_extension(path)
if 'develop' in ' '.join(sys.argv) and islinux:
subprocess.check_call('calibre_postinstall', shell=True)

View File

@ -23,6 +23,10 @@ Run an embedded python interpreter.
return parser
def update_zipfile(zipfile, mod, path):
if 'win32' in sys.platform:
print 'WARNING: On Windows Vista you must run this from a console that has been started in Administrator mode.'
print 'Press Enter to continue or Ctrl-C to Cancel'
raw_input()
pat = re.compile(mod.replace('.', '/')+r'\.py[co]*')
name = mod.replace('.', '/') + os.path.splitext(path)[-1]
update(zipfile, [pat], [path], [name])

View File

@ -133,31 +133,7 @@ class KINDLE(Device):
def open_windows(self):
drives = []
import wmi
c = wmi.WMI()
for drive in c.Win32_DiskDrive():
'''print drive.PNPDeviceID'''
if self.__class__.is_device(drive.PNPDeviceID):
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((drive.Index, prefix))
except IndexError:
continue
if not drives:
print self.__class__.__name__
raise DeviceError('Unable to find %s. Is it connected?'%(self.__class__.__name__,))
drives.sort(cmp=lambda a, b: cmp(a[0], b[0]))
self._main_prefix = drives[0][1]
if len(drives) > 1:
self._card_prefix = drives[1][1]
raise NotImplementedError
def open_linux(self):

View File

@ -7,59 +7,48 @@ manner.
import sys
from calibre import iswindows, isosx
from calibre import iswindows, isosx, plugins
from calibre.devices import libusb
osx_scanner = None
osx_scanner = win_scanner = linux_scanner = None
try:
import usbobserver
osx_scanner = usbobserver.get_devices
except ImportError:
pass
linux_scanner = libusb.get_devices
if iswindows:
try:
win_scanner = plugins['winutil'][0].get_usb_devices
except:
raise RuntimeError('Failed to load the winutil plugin: %s'%plugins['winutil'][1])
elif isosx:
try:
osx_scanner = plugins['usbobserver'][0].get_usb_devices
except:
raise RuntimeError('Failed to load the usbobserver plugin: %s'%plugins['usbobserver'][1])
else:
linux_scanner = libusb.get_devices
class DeviceScanner(object):
def __init__(self, wmi=None):
self.wmi = wmi
if iswindows and wmi is None:
raise RuntimeError('You must pass a wmi instance to DeviceScanner on windows.')
def __init__(self, *args):
if isosx and osx_scanner is None:
raise RuntimeError('The Python extension usbobserver must be available on OS X.')
if not (isosx or iswindows) and not libusb.has_library():
raise RuntimeError('DeviceScanner requires libusb to work.')
self.scanner = win_scanner if iswindows else osx_scanner if isosx else linux_scanner
self.devices = []
def get_devices(self):
if iswindows:
devices = []
for c in self.wmi.USBControllerDevice():
devices.append(c.Dependent.DeviceID.upper())
return devices
if isosx:
return osx_scanner()
return linux_scanner()
def scan(self):
try: # Windows WMI occasionally and temporarily barfs
self.devices = self.get_devices()
except Exception, e:
if not iswindows and e:
raise e
'''Fetch list of connected USB devices from operating system'''
self.devices = self.scanner()
def is_device_connected(self, device):
if iswindows:
for device_id in self.devices:
if 'VEN_'+device.VENDOR_NAME in device_id and \
'PROD_'+device.PRODUCT_NAME in device_id:
return True
vid, pid = hex(device.VENDOR_ID)[2:], hex(device.PRODUCT_ID)[2:]
if len(vid) < 4: vid = '0'+vid
if len(pid) < 4: pid = '0'+pid
vid, pid = 'VID_'+vid.upper(), 'PID_'+pid.upper()
vid, pid = 'vid_%4.4x'%device.VENDOR_ID, 'pid_%4.4x'%device.PRODUCT_ID
if vid in device_id and pid in device_id:
return True
return False

View File

@ -29,7 +29,7 @@
static PyObject *
usbobserver_get_devices(PyObject *self, PyObject *args) {
usbobserver_get_usb_devices(PyObject *self, PyObject *args) {
mach_port_t masterPort;
CFMutableDictionaryRef matchingDict;
@ -62,7 +62,7 @@ usbobserver_get_devices(PyObject *self, PyObject *args) {
PyObject *devices, *device;
devices = PyList_New(0);
if (devices == NULL) {
PyErr_SetString(PyExc_RuntimeError, "Out of memory allocating list");
PyErr_NoMemory();
mach_port_deallocate(mach_task_self(), masterPort);
return NULL;
}
@ -112,7 +112,7 @@ usbobserver_get_devices(PyObject *self, PyObject *args) {
}
static PyMethodDef usbobserver_methods[] = {
{"get_devices", usbobserver_get_devices, METH_VARARGS,
{"get_usb_devices", usbobserver_get_usb_devices, METH_VARARGS,
"Get list of connected USB devices. Returns a list of tuples. Each tuple is of the form (vendor_id, product_id)."
},
{NULL, NULL, 0, NULL}

View File

@ -131,13 +131,18 @@ Why does |app| show only some of my fonts on OS X?
The graphical user interface of |app| is not starting on Windows?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you've never used the graphical user interface before, try deleting the file library1.db (it will be somewhere under :file:`C:\\Documents and Settings` on Windows XP and :file:`C:\\Users` on Windows Vista. If that doesn't fix the problem, locate the file calibre.log (in the same places as library1.db) and post its contents in a help message on the `Forums <http://calibre.kovidgoyal.net/discussion>`_. If you can't find either file, try using the windows find feature to search for them. If the files dont exist on your system, try the following:
Start a command prompt (press the windows key and R and type cmd.exe in the run dialog). At the command prompt type the following command and press Enter::
There can be several causes for this:
* **Any windows version**: Search for the files `calibre2.ini` and `calibre.ini` on your computer and delete them. Search for the file `library1.db` and rename it (this file contains all your converted books so deleting it is not a good idea. Now try again.
* **Windows Vista**: If the folder :file:`C:\Users\Your User Name\AppData\Local\VirtualStore\Program Files\calibre` exists, delete it. Uninstall |app|. Reboot. Re-install.
* **Any windows version**: Search your computer for a folder named :file:`_ipython`. Delete it and try again.
If it still wont launch, start a command prompt (press the windows key and R; then type :command:`cmd.exe` in the Run dialog that appears). At the command prompt type the following command and press Enter::
calibre-debug -c "from calibre.gui2.main import main; main()"
Post any output you see when asking for help.
Post any output you see in a help message on the `Forums <http://calibre.kovidgoyal.net/discussion`_.
I want some feature added to |app|. What can I do?
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

View File

@ -22,7 +22,7 @@ match to a given font specification. The main functions in this module are:
.. autofunction:: match
'''
import sys, os, locale, codecs, platform
import sys, os, locale, codecs, ctypes
from ctypes import cdll, c_void_p, Structure, c_int, POINTER, c_ubyte, c_char, util, \
pointer, byref, create_string_buffer, Union, c_char_p, c_double
@ -34,14 +34,25 @@ except:
iswindows = 'win32' in sys.platform or 'win64' in sys.platform
isosx = 'darwin' in sys.platform
is64bit = '64bit' in platform.architecture()[0]
DISABLED = False
#if isosx:
# libc = ctypes.cdll.LoadLibrary(ctypes.util.find_library('c'))
# size = ctypes.c_uint(0)
# ok = libc.sysctlbyname("hw.cpu64bit_capable", None, byref(size), None, 0)
# if ok != 0:
# is64bit = False
# else:
# buf = ctypes.c_char_p("\0" * size.value)
# ok = libc.sysctlbyname("hw.cpu64bit_capable", buf, byref(size), None, 0)
# if ok != 0:
# is64bit = False
# else:
# is64bit = '1' in buf.value
# DISABLED = is64bit
def load_library():
if isosx:
if os.path.exists('/usr/X11/lib/libfontconfig.1.dylib'): # The fontconfig shipped with calibre doesn't work on Leopard
lib = '/usr/X11/lib/libfontconfig.1.dylib'
else:
lib = os.path.join(getattr(sys, 'frameworks_dir'), 'libfontconfig.1.dylib') \
lib = os.path.join(getattr(sys, 'frameworks_dir'), 'libfontconfig.1.dylib') \
if hasattr(sys, 'frameworks_dir') else util.find_library('fontconfig')
return cdll.LoadLibrary(lib)
elif iswindows:
@ -160,9 +171,9 @@ class FontScanner(Thread):
global _initialized
_initialized = True
_scanner = FontScanner()
_scanner.start()
if not DISABLED:
_scanner = FontScanner()
_scanner.start()
def join():
_scanner.join(120)
@ -178,6 +189,8 @@ def find_font_families(allowed_extensions=['ttf', 'otf']):
`allowed_extensions`: A list of allowed extensions for font file types. Defaults to
`['ttf', 'otf']`. If it is empty, it is ignored.
'''
if DISABLED:
return []
join()
allowed_extensions = [i.lower() for i in allowed_extensions]
@ -220,6 +233,8 @@ def files_for_family(family, normalize=True):
they are a tuple (slant, weight) otherwise they are strings from the set
`('normal', 'bold', 'italic', 'bi', 'light', 'li')`
'''
if DISABLED:
return {}
join()
if isinstance(family, unicode):
family = family.encode(preferred_encoding)
@ -296,6 +311,8 @@ def match(name, sort=False, verbose=False):
decreasing closeness of matching.
`verbose`: If `True` print debugging information to stdout
'''
if DISABLED:
return []
join()
if isinstance(name, unicode):
name = name.encode(preferred_encoding)

View File

@ -0,0 +1,18 @@
# Makefile to test the winutil module
# Invoke with nmake /f Makefile.winutil
test : winutil.pyd
python.exe -c "import winutil; winutil.set_debug(True); print winutil.get_usb_devices(); print winutil.get_mounted_volumes_for_usb_device(0x054c, 0x031e)"
winutil.pyd : winutil.obj
link.exe /DLL /nologo /INCREMENTAL:NO /LIBPATH:c:\Python25\libs \
/LIBPATH:c:\Python25\PCBuild shell32.lib setupapi.lib /EXPORT:initwinutil \
winutil.obj /OUT:winutil.pyd
winutil.obj : winutil.c
cl.exe /c /nologo /Ox /MD /W3 /GX /DNDEBUG -Ic:\Python25\include \
-Ic:\Python25\PC -Ic:\WinDDK\6001.18001\inc\api /Tcwinutil.c /Fowinutil.obj
clean :
del winutil.pyd winutil.obj winutil.exp winutil.lib

View File

@ -0,0 +1,587 @@
/*
:mod:`winutil` -- Interface to Windows
============================================
.. module:: winutil
:platform: Windows
:synopsis: Various methods to interface with the operating system
.. moduleauthor:: Kovid Goyal <kovid@kovidgoyal.net> Copyright 2008
This module contains utility functions to interface with the windows operating
system. It should be compiled with the same version of VisualStudio used to
compile python. It hasn't been tested with MinGW. We try to use unicode
wherever possible in this module.
.. exception:: winutil.DriveError
Raised when scanning for mounted volumes fails.
.. function:: is_usb_device_connected(vid : integer, pid : integer) -> bool
Return `True` iff the USB device identified by the VendorID `vid` and
ProductID `pid` is connected to the system.
.. function:: get_usb_devices() -> list of lowercase strings
Return a list of all USB devices connected to the system. Each
device is represented by a lowercase unicode string whoose format is
the windows *Device Identifier* format. See the MSDN documentation.
.. function:: get_mounted_volumes_for_usb_device(vid : integer, pid : integer) -> dictionary
Return a dictionary of the form `volume_id`:`drive_letter` for all
volumes mounted from the device specified by `vid` and `pid`.
:raises: :exception:`winutil.DriveError` if scanning fails.
.. function:: special_folder_path(csidl_id) -> path
Get paths to common system folders.
See windows documentation of SHGetFolderPath.
The paths are returned as unicode objects. `csidl_id` should be one
of the symbolic constants defined in this module. You can also `OR`
a symbolic constant with :data:`CSIDL_FLAG_CREATE` to force the operating
system to create a folder if it does not exist. For example::
>>> from winutil import *
>>> special_folder_path(CSIDL_APPDATA)
u'C:\\Documents and Settings\\Kovid Goyal\\Application Data'
>>> special_folder_path(CSIDL_PERSONAL)
u'C:\\Documents and Settings\\Kovid Goyal\\My Documents'
.. function:: argv() -> list of unicode command line arguments
Get command line arguments as unicode objects. Note that the
first argument will be the path to the interpreter, *not* the
script being run. So to replace sys.argv, you should use
sys.argv[1:] = winutil.argv()[1:].
*/
#define UNICODE
#include <Windows.h>
#include <Python.h>
#include <shlobj.h>
#include <stdio.h>
#include <setupapi.h>
#include <devguid.h>
#include <cfgmgr32.h>
#include <stdarg.h>
#define BUFSIZE 512
#define MAX_DRIVES 26
static PyObject *DriveError;
static BOOL DEBUG = FALSE;
//#define debug(fmt, ...) if DEBUG printf(x, __VA_ARGS__);
void
debug(const char *fmt, ...) {
va_list argList;
va_start(argList, fmt);
if (DEBUG) vprintf(fmt, argList);
va_end(argList);
}
struct tagDrives
{
WCHAR letter;
WCHAR volume[BUFSIZE];
};
static PyObject *
winutil_folder_path(PyObject *self, PyObject *args) {
int res; DWORD dwFlags;
PyObject *ans = NULL;
TCHAR wbuf[MAX_PATH]; CHAR buf[4*MAX_PATH];
memset(wbuf, 0, sizeof(TCHAR)*MAX_PATH); memset(buf, 0, sizeof(CHAR)*MAX_PATH);
if (!PyArg_ParseTuple(args, "l", &dwFlags)) return NULL;
res = SHGetFolderPath(NULL, dwFlags, NULL, 0, wbuf);
if (res != S_OK) {
if (res == E_FAIL) PyErr_SetString(PyExc_ValueError, "Folder does not exist.");
PyErr_SetString(PyExc_ValueError, "Folder not valid");
return NULL;
}
res = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, 4*MAX_PATH, NULL, NULL);
ans = PyUnicode_DecodeUTF8(buf, res-1, "strict");
return ans;
}
static PyObject *
winutil_argv(PyObject *self, PyObject *args) {
PyObject *argv, *v;
LPWSTR *_argv;
LPSTR buf;
int argc, i, bytes;
if (!PyArg_ParseTuple(args, "")) return NULL;
_argv = CommandLineToArgvW(GetCommandLine(), &argc);
if (_argv == NULL) { PyErr_NoMemory(); return NULL; }
argv = PyList_New(argc);
if (argv != NULL) {
for (i = 0; i < argc; i++) {
bytes = WideCharToMultiByte(CP_UTF8, 0, _argv[i], -1, NULL, 0, NULL, NULL);
buf = (LPSTR)PyMem_Malloc(sizeof(CHAR)*bytes);
if (buf == NULL) { Py_DECREF(argv); argv = NULL; break; }
WideCharToMultiByte(CP_UTF8, 0, _argv[i], -1, buf, bytes, NULL, NULL);
v = PyUnicode_DecodeUTF8(buf, bytes-1, "strict");
PyMem_Free(buf);
if (v == NULL) { Py_DECREF(argv); argv = NULL; break; }
PyList_SetItem(argv, i, v);
}
}
LocalFree(_argv);
return argv;
}
static LPVOID
format_last_error() {
/* Format the last error as a string. The returned pointer should
be freed with :cfunction:`LocalFree(lpMsgBuf)`. It can be printed with
:cfunction:`printf("\n%ws\n", (LPCTSTR)lpMsgBuf)`.
*/
LPVOID lpMsgBuf;
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
GetLastError(),
0, // Default language
(LPTSTR) &lpMsgBuf,
0,
NULL
);
return lpMsgBuf;
}
static PyObject *
winutil_set_debug(PyObject *self, PyObject *args) {
PyObject *yes;
if (!PyArg_ParseTuple(args, "O", &yes)) return NULL;
DEBUG = (BOOL)PyObject_IsTrue(yes);
return Py_None;
}
static LPTSTR
get_registry_property(HDEVINFO hDevInfo, DWORD index, DWORD property, BOOL *iterate) {
/* Get a the property specified by `property` from the registry for the
* device enumerated by `index` in the collection `hDevInfo`. `iterate`
* will be set to `FALSE` if `index` points outside `hDevInfo`.
* :return: A string allocated on the heap containing the property or
* `NULL` if an error occurred.
*/
SP_DEVINFO_DATA DeviceInfoData;
DWORD DataT;
LPTSTR buffer = NULL;
DWORD buffersize = 0;
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if (!SetupDiEnumDeviceInfo(hDevInfo, index, &DeviceInfoData)) {
*iterate = FALSE;
return NULL;
}
while(!SetupDiGetDeviceRegistryProperty(
hDevInfo,
&DeviceInfoData,
property,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
buffer = (LPTSTR)PyMem_Malloc(2*buffersize); // Twice for bug in Win2k
} else {
PyMem_Free(buffer);
PyErr_SetFromWindowsErr(0);
buffer = NULL;
break;
}
} //while
return buffer;
}
static BOOL
check_device_id(LPTSTR buffer, unsigned int vid, unsigned int pid) {
WCHAR xVid[9], dVid[9], xPid[9], dPid[9];
unsigned int j;
swprintf(xVid, L"vid_%4.4x", vid);
swprintf(dVid, L"vid_%4.4d", vid);
swprintf(xPid, L"pid_%4.4x", pid);
swprintf(dPid, L"pid_%4.4d", pid);
for (j = 0; j < wcslen(buffer); j++) buffer[j] = tolower(buffer[j]);
return ( (wcsstr(buffer, xVid) != NULL || wcsstr(buffer, dVid) != NULL ) &&
(wcsstr(buffer, xPid) != NULL || wcsstr(buffer, dPid) != NULL )
);
}
static HDEVINFO
create_device_info_set(LPGUID guid, PCTSTR enumerator, HWND parent, DWORD flags) {
HDEVINFO hDevInfo;
hDevInfo = SetupDiGetClassDevs(
guid,
enumerator,
parent,
flags
);
if (hDevInfo == INVALID_HANDLE_VALUE) {
PyErr_SetFromWindowsErr(0);
}
return hDevInfo;
}
int n;
BOOL
get_all_removable_disks(struct tagDrives *g_drives)
{
WCHAR caDrive[4];
WCHAR volume[BUFSIZE];
int nLoopIndex;
DWORD dwDriveMask;
unsigned int g_count=0;
caDrive[0] = 'A';
caDrive[1] = ':';
caDrive[2] = '\\';
caDrive[3] = 0;
// Get all drives in the system.
dwDriveMask = GetLogicalDrives();
if(dwDriveMask == 0)
{
PyErr_SetString(DriveError, "GetLogicalDrives failed");
return FALSE;
}
// Loop for all drives (MAX_DRIVES = 26)
for(nLoopIndex = 0; nLoopIndex< MAX_DRIVES; nLoopIndex++)
{
// if a drive is present,
if(dwDriveMask & 1)
{
caDrive[0] = 'A' + nLoopIndex;
// If a drive is removable
if(GetDriveType(caDrive) == DRIVE_REMOVABLE)
{
//Get its volume info and store it in the global variable.
if(GetVolumeNameForVolumeMountPoint(caDrive, volume, BUFSIZE))
{
g_drives[g_count].letter = caDrive[0];
wcscpy(g_drives[g_count].volume, volume);
g_count ++;
}
}
}
dwDriveMask >>= 1;
}
// success if atleast one removable drive is found.
if(g_count == 0)
{
PyErr_SetString(DriveError, "No removable drives found");
return FALSE;
}
return TRUE;
}
PSP_DEVICE_INTERFACE_DETAIL_DATA
get_device_grandparent(HDEVINFO hDevInfo, DWORD index, PWSTR buf, PWSTR volume_id,
BOOL *iterate) {
SP_DEVICE_INTERFACE_DATA interfaceData;
SP_DEVINFO_DATA devInfoData;
BOOL status;
PSP_DEVICE_INTERFACE_DETAIL_DATA interfaceDetailData;
DWORD interfaceDetailDataSize,
reqSize;
DEVINST parent;
interfaceData.cbSize = sizeof (SP_INTERFACE_DEVICE_DATA);
devInfoData.cbSize = sizeof (SP_DEVINFO_DATA);
status = SetupDiEnumDeviceInterfaces (
hDevInfo, // Interface Device Info handle
NULL, // Device Info data
(LPGUID)&GUID_DEVINTERFACE_VOLUME, // Interface registered by driver
index, // Member
&interfaceData // Device Interface Data
);
if ( status == FALSE ) {
*iterate = FALSE;
return NULL;
}
SetupDiGetDeviceInterfaceDetail (
hDevInfo, // Interface Device info handle
&interfaceData, // Interface data for the event class
NULL, // Checking for buffer size
0, // Checking for buffer size
&reqSize, // Buffer size required to get the detail data
NULL // Checking for buffer size
);
interfaceDetailDataSize = reqSize;
interfaceDetailData = (PSP_DEVICE_INTERFACE_DETAIL_DATA)PyMem_Malloc(interfaceDetailDataSize+10);
if ( interfaceDetailData == NULL ) {
PyErr_NoMemory();
return NULL;
}
interfaceDetailData->cbSize = sizeof (SP_INTERFACE_DEVICE_DETAIL_DATA);
status = SetupDiGetDeviceInterfaceDetail (
hDevInfo, // Interface Device info handle
&interfaceData, // Interface data for the event class
interfaceDetailData, // Interface detail data
interfaceDetailDataSize, // Interface detail data size
&reqSize, // Buffer size required to get the detail data
&devInfoData); // Interface device info
if ( status == FALSE ) {PyErr_SetFromWindowsErr(0); PyMem_Free(interfaceDetailData); return NULL;}
// Get the device instance of parent. This points to USBSTOR.
CM_Get_Parent(&parent, devInfoData.DevInst, 0);
// Get the device ID of the USBSTORAGE volume
CM_Get_Device_ID(parent, volume_id, BUFSIZE, 0);
// Get the device instance of grand parent. This points to USB root.
CM_Get_Parent(&parent, parent, 0);
// Get the device ID of the USB root.
CM_Get_Device_ID(parent, buf, BUFSIZE, 0);
return interfaceDetailData;
}
static PyObject *
winutil_get_mounted_volumes_for_usb_device(PyObject *self, PyObject *args) {
unsigned int vid, pid, length, j;
HDEVINFO hDevInfo;
BOOL iterate = TRUE;
PSP_DEVICE_INTERFACE_DETAIL_DATA interfaceDetailData;
DWORD i;
WCHAR buf[BUFSIZE], volume[BUFSIZE], volume_id[BUFSIZE];
struct tagDrives g_drives[MAX_DRIVES];
PyObject *volumes, *key, *val;
if (!PyArg_ParseTuple(args, "ii", &vid, &pid)) {
return NULL;
}
volumes = PyDict_New();
if (volumes == NULL) return NULL;
for (j = 0; j < MAX_DRIVES; j++) g_drives[j].letter = 0;
// Find all removable drives
if (!get_all_removable_disks(g_drives)) {
return NULL;
}
hDevInfo = create_device_info_set((LPGUID)&GUID_DEVINTERFACE_VOLUME,
NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE) return NULL;
// Enumerate through the set
for (i=0; iterate; i++) {
interfaceDetailData = get_device_grandparent(hDevInfo, i, buf, volume_id, &iterate);
if (interfaceDetailData == NULL) {
PyErr_Print(); continue;
}
debug("Device num: %d Device Id: %ws\n\n", i, buf);
if (check_device_id(buf, vid, pid)) {
debug("Device matches\n\n");
length = wcslen(interfaceDetailData->DevicePath);
interfaceDetailData->DevicePath[length] = '\\';
interfaceDetailData->DevicePath[length+1] = 0;
if(GetVolumeNameForVolumeMountPoint(interfaceDetailData->DevicePath, volume, BUFSIZE)) {
for(j = 0; j < MAX_DRIVES; j++) {
// Compare volume mount point with the one stored earlier.
// If both match, return the corresponding drive letter.
if(g_drives[j].letter != 0 && wcscmp(g_drives[j].volume, volume)==0)
{
key = PyUnicode_FromWideChar(volume_id, wcslen(volume_id));
val = PyString_FromFormat("%c", (char)g_drives[j].letter);
if (key == NULL || val == NULL) {
PyErr_NoMemory();
PyMem_Free(interfaceDetailData);
return NULL;
}
PyDict_SetItem(volumes, key, val);
}
}
} else {
debug("Failed to get volume name for volume mount point:\n");
if (DEBUG) debug("%ws\n\n", format_last_error());
}
PyMem_Free(interfaceDetailData);
}
} //for
SetupDiDestroyDeviceInfoList(hDevInfo);
return volumes;
}
static PyObject *
winutil_get_usb_devices(PyObject *self, PyObject *args) {
unsigned int j, buffersize;
HDEVINFO hDevInfo;
DWORD i; BOOL iterate = TRUE;
PyObject *devices, *temp = (PyObject *)1;
LPTSTR buffer;
if (!PyArg_ParseTuple(args, "")) return NULL;
devices = PyList_New(0);
if (devices == NULL) {PyErr_NoMemory(); return NULL;}
// Create a Device information set with all USB devices
hDevInfo = create_device_info_set(NULL, L"USB", 0,
DIGCF_PRESENT | DIGCF_ALLCLASSES);
if (hDevInfo == INVALID_HANDLE_VALUE)
return NULL;
// Enumerate through the set
for (i=0; iterate; i++) {
buffer = get_registry_property(hDevInfo, i, SPDRP_HARDWAREID, &iterate);
if (buffer == NULL) {
PyErr_Print(); continue;
}
buffersize = wcslen(buffer);
for (j = 0; j < buffersize; j++) buffer[j] = tolower(buffer[j]);
temp = PyUnicode_FromWideChar(buffer, buffersize);
PyMem_Free(buffer);
if (temp == NULL) {
PyErr_NoMemory();
break;
}
PyList_Append(devices, temp);
} //for
if (temp == NULL) { Py_DECREF(devices); devices = NULL; }
SetupDiDestroyDeviceInfoList(hDevInfo);
return devices;
}
static PyObject *
winutil_is_usb_device_connected(PyObject *self, PyObject *args) {
unsigned int vid, pid;
HDEVINFO hDevInfo;
DWORD i; BOOL iterate = TRUE;
LPTSTR buffer;
int found = FALSE;
PyObject *ans;
if (!PyArg_ParseTuple(args, "ii", &vid, &pid)) {
return NULL;
}
// Create a Device information set with all USB devices
hDevInfo = create_device_info_set(NULL, L"USB", 0,
DIGCF_PRESENT | DIGCF_ALLCLASSES);
if (hDevInfo == INVALID_HANDLE_VALUE)
return NULL;
// Enumerate through the set
for (i=0; iterate && !found; i++) {
buffer = get_registry_property(hDevInfo, i, SPDRP_HARDWAREID, &iterate);
if (buffer == NULL) {
PyErr_Print(); continue;
}
found = check_device_id(buffer, vid, pid);
PyMem_Free(buffer);
} // for
SetupDiDestroyDeviceInfoList(hDevInfo);
ans = (found) ? Py_True : Py_False;
Py_INCREF(ans);
return ans;
}
static PyMethodDef WinutilMethods[] = {
{"special_folder_path", winutil_folder_path, METH_VARARGS,
"special_folder_path(csidl_id) -> path\n\n"
"Get paths to common system folders. "
"See windows documentation of SHGetFolderPath. "
"The paths are returned as unicode objects. csidl_id should be one "
"of the symbolic constants defined in this module. You can also OR "
"a symbolic constant with CSIDL_FLAG_CREATE to force the operating "
"system to create a folder if it does not exist."},
{"argv", winutil_argv, METH_VARARGS,
"argv() -> list of command line arguments\n\n"
"Get command line arguments as unicode objects. Note that the "
"first argument will be the path to the interpreter, *not* the "
"script being run. So to replace sys.argv, you should use "
"sys.argv[1:] = argv()[1:]."},
{"is_usb_device_connected", winutil_is_usb_device_connected, METH_VARARGS,
"is_usb_device_connected(vid, pid) -> bool\n\n"
"Check if the USB device identified by VendorID: vid (integer) and"
" ProductID: pid (integer) is currently connected."},
{"get_usb_devices", winutil_get_usb_devices, METH_VARARGS,
"get_usb_devices() -> list of strings\n\n"
"Return a list of the hardware IDs of all USB devices "
"connected to the system."},
{"get_mounted_volumes_for_usb_device", winutil_get_mounted_volumes_for_usb_device, METH_VARARGS,
"get_mounted_volumes_for_usb_device(vid, pid) -> dict\n\n"
"Return a dictionary of volume_id:drive_letter for all"
"volumes mounted on the system that belong to the"
"usb device specified by vid (integer) and pid (integer)."},
{"set_debug", winutil_set_debug, METH_VARARGS,
"set_debug(bool)\n\nSet debugging mode."
},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
initwinutil(void) {
PyObject *m;
m = Py_InitModule3("winutil", WinutilMethods,
"Defines utility methods to interface with windows."
);
if (m == NULL) return;
DriveError = PyErr_NewException("winutil.DriveError", NULL, NULL);
PyModule_AddIntConstant(m, "CSIDL_ADMINTOOLS", CSIDL_ADMINTOOLS);
PyModule_AddIntConstant(m, "CSIDL_APPDATA", CSIDL_APPDATA);
PyModule_AddIntConstant(m, "CSIDL_COMMON_ADMINTOOLS", CSIDL_COMMON_ADMINTOOLS);
PyModule_AddIntConstant(m, "CSIDL_COMMON_APPDATA", CSIDL_COMMON_APPDATA);
PyModule_AddIntConstant(m, "CSIDL_COMMON_DOCUMENTS", CSIDL_COMMON_DOCUMENTS);
PyModule_AddIntConstant(m, "CSIDL_COOKIES", CSIDL_COOKIES);
PyModule_AddIntConstant(m, "CSIDL_FLAG_CREATE", CSIDL_FLAG_CREATE);
PyModule_AddIntConstant(m, "CSIDL_FLAG_DONT_VERIFY", CSIDL_FLAG_DONT_VERIFY);
PyModule_AddIntConstant(m, "CSIDL_HISTORY", CSIDL_HISTORY);
PyModule_AddIntConstant(m, "CSIDL_INTERNET_CACHE", CSIDL_INTERNET_CACHE);
PyModule_AddIntConstant(m, "CSIDL_LOCAL_APPDATA", CSIDL_LOCAL_APPDATA);
PyModule_AddIntConstant(m, "CSIDL_MYPICTURES", CSIDL_MYPICTURES);
PyModule_AddIntConstant(m, "CSIDL_PERSONAL", CSIDL_PERSONAL);
PyModule_AddIntConstant(m, "CSIDL_PROGRAM_FILES", CSIDL_PROGRAM_FILES);
PyModule_AddIntConstant(m, "CSIDL_PROGRAM_FILES_COMMON", CSIDL_PROGRAM_FILES_COMMON);
PyModule_AddIntConstant(m, "CSIDL_SYSTEM", CSIDL_SYSTEM);
PyModule_AddIntConstant(m, "CSIDL_WINDOWS", CSIDL_WINDOWS);
}

View File

@ -1,99 +0,0 @@
#define UNICODE
#include <shlobj.h>
#include <Windows.h>
#include <Python.h>
#include <stdio.h>
static PyObject *
winutil_folder_path(PyObject *self, PyObject *args) {
int res; DWORD dwFlags;
PyObject *ans = NULL;
TCHAR wbuf[MAX_PATH]; CHAR buf[4*MAX_PATH];
memset(wbuf, 0, sizeof(TCHAR)*MAX_PATH); memset(buf, 0, sizeof(CHAR)*MAX_PATH);
if (!PyArg_ParseTuple(args, "l", &dwFlags)) return NULL;
res = SHGetFolderPath(NULL, dwFlags, NULL, 0, wbuf);
if (res != S_OK) {
if (res == E_FAIL) PyErr_SetString(PyExc_ValueError, "Folder does not exist.");
PyErr_SetString(PyExc_ValueError, "Folder not valid");
return NULL;
}
res = WideCharToMultiByte(CP_UTF8, 0, wbuf, -1, buf, 4*MAX_PATH, NULL, NULL);
ans = PyUnicode_DecodeUTF8(buf, res-1, "strict");
return ans;
}
static PyObject *
winutil_argv(PyObject *self, PyObject *args) {
PyObject *argv, *v;
LPWSTR *_argv;
LPSTR buf;
int argc, i, bytes;
if (!PyArg_ParseTuple(args, "")) return NULL;
_argv = CommandLineToArgvW(GetCommandLine(), &argc);
if (_argv == NULL) { PyErr_SetString(PyExc_RuntimeError, "Out of memory."); return NULL; }
argv = PyList_New(argc);
if (argv != NULL) {
for (i = 0; i < argc; i++) {
bytes = WideCharToMultiByte(CP_UTF8, 0, _argv[i], -1, NULL, 0, NULL, NULL);
buf = (LPSTR)PyMem_Malloc(sizeof(CHAR)*bytes);
if (buf == NULL) { Py_DECREF(argv); argv = NULL; break; }
WideCharToMultiByte(CP_UTF8, 0, _argv[i], -1, buf, bytes, NULL, NULL);
v = PyUnicode_DecodeUTF8(buf, bytes-1, "strict");
PyMem_Free(buf);
if (v == NULL) { Py_DECREF(argv); argv = NULL; break; }
PyList_SetItem(argv, i, v);
}
}
LocalFree(_argv);
return argv;
}
static PyMethodDef WinutilMethods[] = {
{"folder_path", winutil_folder_path, METH_VARARGS,
"folder_path(csidl_id) -> path\n\n"
"Get paths to common system folders. "
"See windows documentation of SHGetFolderPath. "
"The paths are returned as unicode objects. csidl_id should be one "
"of the symbolic constants defined in this module. You can also OR "
"a symbolic constant with CSIDL_FLAG_CREATE to force the operating "
"system to create a folder if it does not exist."},
{"argv", winutil_argv, METH_VARARGS,
"argv() -> list of command line arguments\n\n"
"Get command line arguments as unicode objects. Note that the "
"first argument will be the path to the interpreter, *not* the "
"script being run. So to replace sys.argv, you should use "
"sys.argv[1:] = argv()[1:]."},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC
initwinutil(void) {
PyObject *m;
m = Py_InitModule3("winutil", WinutilMethods,
"Defines utility methods to interface with windows."
);
if (m == NULL) return;
PyModule_AddIntConstant(m, "CSIDL_ADMINTOOLS", CSIDL_ADMINTOOLS);
PyModule_AddIntConstant(m, "CSIDL_APPDATA", CSIDL_APPDATA);
PyModule_AddIntConstant(m, "CSIDL_COMMON_ADMINTOOLS", CSIDL_COMMON_ADMINTOOLS);
PyModule_AddIntConstant(m, "CSIDL_COMMON_APPDATA", CSIDL_COMMON_APPDATA);
PyModule_AddIntConstant(m, "CSIDL_COMMON_DOCUMENTS", CSIDL_COMMON_DOCUMENTS);
PyModule_AddIntConstant(m, "CSIDL_COOKIES", CSIDL_COOKIES);
PyModule_AddIntConstant(m, "CSIDL_FLAG_CREATE", CSIDL_FLAG_CREATE);
PyModule_AddIntConstant(m, "CSIDL_FLAG_DONT_VERIFY", CSIDL_FLAG_DONT_VERIFY);
PyModule_AddIntConstant(m, "CSIDL_HISTORY", CSIDL_HISTORY);
PyModule_AddIntConstant(m, "CSIDL_INTERNET_CACHE", CSIDL_INTERNET_CACHE);
PyModule_AddIntConstant(m, "CSIDL_LOCAL_APPDATA", CSIDL_LOCAL_APPDATA);
PyModule_AddIntConstant(m, "CSIDL_MYPICTURES", CSIDL_MYPICTURES);
PyModule_AddIntConstant(m, "CSIDL_PERSONAL", CSIDL_PERSONAL);
PyModule_AddIntConstant(m, "CSIDL_PROGRAM_FILES", CSIDL_PROGRAM_FILES);
PyModule_AddIntConstant(m, "CSIDL_PROGRAM_FILES_COMMON", CSIDL_PROGRAM_FILES_COMMON);
PyModule_AddIntConstant(m, "CSIDL_SYSTEM", CSIDL_SYSTEM);
PyModule_AddIntConstant(m, "CSIDL_WINDOWS", CSIDL_WINDOWS);
}