mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-07 10:14:46 -04:00
Support for ejecting devices on windows
This commit is contained in:
parent
943f6f4c4b
commit
03fd7f164e
@ -230,7 +230,7 @@ _check_symlinks_prescript()
|
|||||||
all_functions = main_functions['console'] + main_functions['gui']
|
all_functions = main_functions['console'] + main_functions['gui']
|
||||||
print
|
print
|
||||||
print 'Adding PoDoFo'
|
print 'Adding PoDoFo'
|
||||||
pdf = glob.glob(os.path.expanduser('/Volumes/sw/podofo/*.dylib'))[0]
|
pdf = glob.glob(os.path.expanduser('/Volumes/sw/podofo/libpodofo.0.7.0.dylib'))[0]
|
||||||
shutil.copyfile(pdf, os.path.join(frameworks_dir, os.path.basename(pdf)))
|
shutil.copyfile(pdf, os.path.join(frameworks_dir, os.path.basename(pdf)))
|
||||||
|
|
||||||
|
|
||||||
|
@ -492,7 +492,26 @@ class Device(DeviceConfig, DevicePlugin):
|
|||||||
self.open_osx()
|
self.open_osx()
|
||||||
|
|
||||||
def eject_windows(self):
|
def eject_windows(self):
|
||||||
pass
|
from calibre.constants import plugins
|
||||||
|
from threading import Thread
|
||||||
|
winutil, winutil_err = plugins['winutil']
|
||||||
|
drives = []
|
||||||
|
for x in ('_main_prefix', '_card_a_prefix', '_card_b_prefix'):
|
||||||
|
x = getattr(self, x, None)
|
||||||
|
if x is not None:
|
||||||
|
drives.append(x[0].upper())
|
||||||
|
|
||||||
|
def do_it(drives):
|
||||||
|
for d in drives:
|
||||||
|
try:
|
||||||
|
winutil.eject_drive(d)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
t = Thread(target=do_it, args=[drives])
|
||||||
|
t.daemon = True
|
||||||
|
t.start()
|
||||||
|
self.__save_win_eject_thread = t
|
||||||
|
|
||||||
def eject_osx(self):
|
def eject_osx(self):
|
||||||
for x in ('_main_prefix', '_card_a_prefix', '_card_b_prefix'):
|
for x in ('_main_prefix', '_card_a_prefix', '_card_b_prefix'):
|
||||||
|
@ -271,7 +271,7 @@ get_all_removable_disks(struct tagDrives *g_drives)
|
|||||||
// Loop for all drives (MAX_DRIVES = 26)
|
// Loop for all drives (MAX_DRIVES = 26)
|
||||||
|
|
||||||
|
|
||||||
for(nLoopIndex = 0; nLoopIndex< MAX_DRIVES; nLoopIndex++)
|
for(nLoopIndex = 0; nLoopIndex < MAX_DRIVES; nLoopIndex++)
|
||||||
{
|
{
|
||||||
// if a drive is present,
|
// if a drive is present,
|
||||||
if(dwDriveMask & 1)
|
if(dwDriveMask & 1)
|
||||||
@ -306,6 +306,213 @@ get_all_removable_disks(struct tagDrives *g_drives)
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static DEVINST
|
||||||
|
GetDrivesDevInstByDeviceNumber(long DeviceNumber,
|
||||||
|
UINT DriveType, char* szDosDeviceName)
|
||||||
|
{
|
||||||
|
GUID *guid;
|
||||||
|
HDEVINFO hDevInfo;
|
||||||
|
DWORD dwIndex, dwBytesReturned;
|
||||||
|
BOOL bRet, IsFloppy;
|
||||||
|
BYTE Buf[1024];
|
||||||
|
PSP_DEVICE_INTERFACE_DETAIL_DATA pspdidd;
|
||||||
|
long res;
|
||||||
|
HANDLE hDrive;
|
||||||
|
STORAGE_DEVICE_NUMBER sdn;
|
||||||
|
|
||||||
|
IsFloppy = (strstr(szDosDeviceName, "\\Floppy") != NULL); // is there a better way?
|
||||||
|
|
||||||
|
switch (DriveType) {
|
||||||
|
case DRIVE_REMOVABLE:
|
||||||
|
if ( IsFloppy ) {
|
||||||
|
guid = (GUID*)&GUID_DEVINTERFACE_FLOPPY;
|
||||||
|
} else {
|
||||||
|
guid = (GUID*)&GUID_DEVINTERFACE_DISK;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case DRIVE_FIXED:
|
||||||
|
guid = (GUID*)&GUID_DEVINTERFACE_DISK;
|
||||||
|
break;
|
||||||
|
case DRIVE_CDROM:
|
||||||
|
guid = (GUID*)&GUID_DEVINTERFACE_CDROM;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Invalid drive type");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get device interface info set handle
|
||||||
|
// for all devices attached to system
|
||||||
|
hDevInfo = SetupDiGetClassDevs(guid, NULL, NULL,
|
||||||
|
DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
|
||||||
|
|
||||||
|
if (hDevInfo == INVALID_HANDLE_VALUE) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Invalid handle value");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retrieve a context structure for a device interface
|
||||||
|
// of a device information set.
|
||||||
|
dwIndex = 0;
|
||||||
|
bRet = FALSE;
|
||||||
|
|
||||||
|
|
||||||
|
pspdidd =
|
||||||
|
(PSP_DEVICE_INTERFACE_DETAIL_DATA)Buf;
|
||||||
|
SP_DEVICE_INTERFACE_DATA spdid;
|
||||||
|
SP_DEVINFO_DATA spdd;
|
||||||
|
DWORD dwSize;
|
||||||
|
|
||||||
|
spdid.cbSize = sizeof(spdid);
|
||||||
|
|
||||||
|
while ( TRUE ) {
|
||||||
|
bRet = SetupDiEnumDeviceInterfaces(hDevInfo, NULL,
|
||||||
|
guid, dwIndex, &spdid);
|
||||||
|
if ( !bRet ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
dwSize = 0;
|
||||||
|
SetupDiGetDeviceInterfaceDetail(hDevInfo,
|
||||||
|
&spdid, NULL, 0, &dwSize, NULL);
|
||||||
|
|
||||||
|
if ( dwSize!=0 && dwSize<=sizeof(Buf) ) {
|
||||||
|
pspdidd->cbSize = sizeof(*pspdidd); // 5 Bytes!
|
||||||
|
|
||||||
|
ZeroMemory((PVOID)&spdd, sizeof(spdd));
|
||||||
|
spdd.cbSize = sizeof(spdd);
|
||||||
|
|
||||||
|
res =
|
||||||
|
SetupDiGetDeviceInterfaceDetail(hDevInfo, &
|
||||||
|
spdid, pspdidd,
|
||||||
|
dwSize, &dwSize,
|
||||||
|
&spdd);
|
||||||
|
if ( res ) {
|
||||||
|
hDrive = CreateFile(pspdidd->DevicePath,0,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||||
|
NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
if ( hDrive != INVALID_HANDLE_VALUE ) {
|
||||||
|
dwBytesReturned = 0;
|
||||||
|
res = DeviceIoControl(hDrive,
|
||||||
|
IOCTL_STORAGE_GET_DEVICE_NUMBER,
|
||||||
|
NULL, 0, &sdn, sizeof(sdn),
|
||||||
|
&dwBytesReturned, NULL);
|
||||||
|
if ( res ) {
|
||||||
|
if ( DeviceNumber == (long)sdn.DeviceNumber ) {
|
||||||
|
CloseHandle(hDrive);
|
||||||
|
SetupDiDestroyDeviceInfoList(hDevInfo);
|
||||||
|
return spdd.DevInst;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CloseHandle(hDrive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
dwIndex++;
|
||||||
|
}
|
||||||
|
|
||||||
|
SetupDiDestroyDeviceInfoList(hDevInfo);
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Invalid device number");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
static BOOL
|
||||||
|
eject_drive_letter(char DriveLetter) {
|
||||||
|
char szRootPath[4], szDevicePath[3], szVolumeAccessPath[7], szDosDeviceName[MAX_PATH];
|
||||||
|
long DeviceNumber, res, tries;
|
||||||
|
HANDLE hVolume;
|
||||||
|
STORAGE_DEVICE_NUMBER sdn;
|
||||||
|
DWORD dwBytesReturned;
|
||||||
|
DEVINST DevInst;
|
||||||
|
ULONG Status;
|
||||||
|
ULONG ProblemNumber;
|
||||||
|
PNP_VETO_TYPE VetoType;
|
||||||
|
WCHAR VetoNameW[MAX_PATH];
|
||||||
|
BOOL bSuccess;
|
||||||
|
DEVINST DevInstParent;
|
||||||
|
|
||||||
|
szRootPath[0] = DriveLetter; szRootPath[1] = ':'; szRootPath[2] = '\\'; szRootPath[3] = (char)0;
|
||||||
|
szDevicePath[0] = DriveLetter; szDevicePath[1] = ':'; szDevicePath[2] = (char)0;
|
||||||
|
szVolumeAccessPath[0] = '\\'; szVolumeAccessPath[1] = '\\'; szVolumeAccessPath[2] = '.';
|
||||||
|
szVolumeAccessPath[3] = '\\'; szVolumeAccessPath[4] = DriveLetter; szVolumeAccessPath[5] = ':';
|
||||||
|
szVolumeAccessPath[6] = (char)0;
|
||||||
|
|
||||||
|
|
||||||
|
DeviceNumber = -1;
|
||||||
|
|
||||||
|
hVolume = CreateFile(szVolumeAccessPath, 0,
|
||||||
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||||||
|
NULL, OPEN_EXISTING, 0, NULL);
|
||||||
|
if (hVolume == INVALID_HANDLE_VALUE) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Invalid handle value for drive letter");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
dwBytesReturned = 0;
|
||||||
|
res = DeviceIoControl(hVolume,
|
||||||
|
IOCTL_STORAGE_GET_DEVICE_NUMBER,
|
||||||
|
NULL, 0, &sdn, sizeof(sdn),
|
||||||
|
&dwBytesReturned, NULL);
|
||||||
|
if ( res ) {
|
||||||
|
DeviceNumber = sdn.DeviceNumber;
|
||||||
|
}
|
||||||
|
CloseHandle(hVolume);
|
||||||
|
|
||||||
|
if ( DeviceNumber == -1 ) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Can't find drive number");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = QueryDosDevice(szDevicePath, szDosDeviceName, MAX_PATH);
|
||||||
|
if ( !res ) {
|
||||||
|
PyErr_SetString(PyExc_ValueError, "Can't find dos device");
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber,
|
||||||
|
DriveType, szDosDeviceName);
|
||||||
|
if (DevInst == 0) return FALSE;
|
||||||
|
|
||||||
|
DevInstParent = 0;
|
||||||
|
Status = 0;
|
||||||
|
ProblemNumber = 0;
|
||||||
|
PNP_VETO_TYPE VetoType;
|
||||||
|
bSuccess = FALSE;
|
||||||
|
|
||||||
|
res = CM_Get_Parent(&DevInstParent, DevInst, 0);
|
||||||
|
|
||||||
|
for ( tries = 0; tries < 3; tries++ ) {
|
||||||
|
VetoNameW[0] = 0;
|
||||||
|
|
||||||
|
res = CM_Request_Device_EjectW(DevInstParent,
|
||||||
|
&VetoType, VetoNameW, MAX_PATH, 0);
|
||||||
|
|
||||||
|
bSuccess = (res==CR_SUCCESS &&
|
||||||
|
VetoType==PNP_VetoTypeUnknown);
|
||||||
|
if ( bSuccess ) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Sleep(500); // required to give the next tries a chance!
|
||||||
|
}
|
||||||
|
if (!bSuccess) PyErr_SetString(PyExc_ValueError, "Failed to eject drive after three tries");
|
||||||
|
return bSuccess;
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject *
|
||||||
|
winutil_eject_drive(PyObject *self, PyObject *args) {
|
||||||
|
char DriveLetter;
|
||||||
|
|
||||||
|
if (!PyArg_ParseTuple(args, "c", &DriveLetter)) return NULL;
|
||||||
|
|
||||||
|
if (!eject_drive_letter(DriveLetter)) return NULL;
|
||||||
|
Py_RETURN_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
PSP_DEVICE_INTERFACE_DETAIL_DATA
|
PSP_DEVICE_INTERFACE_DETAIL_DATA
|
||||||
get_device_grandparent(HDEVINFO hDevInfo, DWORD index, PWSTR buf, PWSTR volume_id,
|
get_device_grandparent(HDEVINFO hDevInfo, DWORD index, PWSTR buf, PWSTR volume_id,
|
||||||
BOOL *iterate) {
|
BOOL *iterate) {
|
||||||
@ -697,6 +904,10 @@ is not present, current time as returned by localtime() is used. format must\n\
|
|||||||
be a unicode string. Returns unicode strings."
|
be a unicode string. Returns unicode strings."
|
||||||
},
|
},
|
||||||
|
|
||||||
|
{"eject_drive", winutil_eject_drive, METH_VARARGS,
|
||||||
|
"eject_drive(drive_letter)\n\nEject a drive. Raises an exception on failure."
|
||||||
|
},
|
||||||
|
|
||||||
{NULL, NULL, 0, NULL}
|
{NULL, NULL, 0, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user