Remove code superseeded by winusb

This commit is contained in:
Kovid Goyal 2016-01-18 12:18:48 +05:30
parent 5d3415dcfb
commit deb72df077
2 changed files with 1 additions and 637 deletions

View File

@ -275,7 +275,7 @@ if iswindows:
extensions.extend([ extensions.extend([
Extension('winutil', Extension('winutil',
['calibre/utils/windows/winutil.c'], ['calibre/utils/windows/winutil.c'],
libraries=['shell32', 'setupapi', 'wininet'], libraries=['shell32', 'wininet'],
cflags=['/X'] cflags=['/X']
), ),
Extension('wpd', Extension('wpd',

View File

@ -13,24 +13,6 @@ 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 compile python. It hasn't been tested with MinGW. We try to use unicode
wherever possible in this module. 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 .. function:: special_folder_path(csidl_id) -> path
Get paths to common system folders. Get paths to common system folders.
See windows documentation of SHGetFolderPath. See windows documentation of SHGetFolderPath.
@ -77,7 +59,6 @@ wherever possible in this module.
#define BUFSIZE 512 #define BUFSIZE 512
#define MAX_DRIVES 26 #define MAX_DRIVES 26
static PyObject *DriveError;
static BOOL DEBUG = FALSE; static BOOL DEBUG = FALSE;
//#define debug(fmt, ...) if DEBUG printf(x, __VA_ARGS__); //#define debug(fmt, ...) if DEBUG printf(x, __VA_ARGS__);
@ -89,12 +70,6 @@ debug(const char *fmt, ...) {
va_end(argList); va_end(argList);
} }
struct tagDrives
{
WCHAR letter;
WCHAR volume[BUFSIZE];
};
static void console_out(LPCWSTR fmt, LPCWSTR arg) { static void console_out(LPCWSTR fmt, LPCWSTR arg) {
char *bfmt, *barg; char *bfmt, *barg;
int sz; int sz;
@ -187,593 +162,6 @@ winutil_set_debug(PyObject *self, PyObject *args) {
return Py_None; return Py_None;
} }
/* Obsolete device code {{{ */
static LPWSTR
get_registry_property(HDEVINFO hDevInfo, DWORD index, DWORD property, BOOL *iterate) {
/* Get 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;
LPWSTR buffer = NULL;
DWORD buffersize = 0;
DeviceInfoData.cbSize = sizeof(SP_DEVINFO_DATA);
if (!SetupDiEnumDeviceInfo(hDevInfo, index, &DeviceInfoData)) {
*iterate = FALSE;
return NULL;
}
while(!SetupDiGetDeviceRegistryPropertyW(
hDevInfo,
&DeviceInfoData,
property,
&DataT,
(PBYTE)buffer,
buffersize,
&buffersize)) {
if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
if (buffer != NULL) { PyMem_Free(buffer); buffer = NULL; }
buffer = (LPWSTR)PyMem_Malloc(2*buffersize); // Twice for bug in Win2k
} else {
if (buffer != NULL) { PyMem_Free(buffer); buffer = NULL; }
PyErr_SetFromWindowsErr(0);
break;
}
} //while
return buffer;
}
static BOOL
check_device_id(LPWSTR buffer, unsigned int vid, unsigned int pid) {
WCHAR xVid[9], dVid[9], xPid[9], dPid[9];
unsigned int j;
_snwprintf_s(xVid, 9, _TRUNCATE, L"vid_%4.4x", vid);
_snwprintf_s(dVid, 9, _TRUNCATE, L"vid_%4.4d", vid);
_snwprintf_s(xPid, 9, _TRUNCATE, L"pid_%4.4x", pid);
_snwprintf_s(dPid, 9, _TRUNCATE, 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 (we cannot ignore the A and B drives as there
// are people out there that think mapping devices to use those letters
// is a good idea, sigh)
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_s(g_drives[g_count].volume, BUFSIZE, 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;
}
static DEVINST
GetDrivesDevInstByDeviceNumber(long DeviceNumber,
UINT DriveType, LPWSTR 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;
SP_DEVICE_INTERFACE_DATA spdid;
SP_DEVINFO_DATA spdd;
DWORD dwSize;
IsFloppy = (wcsstr(szDosDeviceName, L"\\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;
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(WCHAR DriveLetter) {
LPWSTR szRootPath = L"X:\\",
szDevicePath = L"X:",
szVolumeAccessPath = L"\\\\.\\X:";
WCHAR szDosDeviceName[MAX_PATH];
long DeviceNumber, res, tries;
HANDLE hVolume;
STORAGE_DEVICE_NUMBER sdn;
DWORD dwBytesReturned;
DEVINST DevInst;
ULONG Status;
ULONG ProblemNumber;
UINT DriveType;
PNP_VETO_TYPE VetoType;
WCHAR VetoNameW[MAX_PATH];
BOOL bSuccess;
DEVINST DevInstParent;
szRootPath[0] = DriveLetter;
szDevicePath[0] = DriveLetter;
szVolumeAccessPath[4] = DriveLetter;
DeviceNumber = -1;
hVolume = CreateFileW(szVolumeAccessPath, 0,
FILE_SHARE_READ | FILE_SHARE_WRITE,
NULL, OPEN_EXISTING, 0, NULL);
if (hVolume == INVALID_HANDLE_VALUE) {
PyErr_SetFromWindowsErr(0);
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;
}
DriveType = GetDriveType(szRootPath);
DevInst = GetDrivesDevInstByDeviceNumber(DeviceNumber,
DriveType, szDosDeviceName);
if (DevInst == 0) return FALSE;
DevInstParent = 0;
Status = 0;
ProblemNumber = 0;
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 letter = '0';
WCHAR DriveLetter = L'0';
if (!PyArg_ParseTuple(args, "c", &letter)) return NULL;
if (MultiByteToWideChar(CP_UTF8, MB_ERR_INVALID_CHARS, &letter, 1, &DriveLetter, 1) == 0) {
PyErr_SetFromWindowsErr(0);
return NULL;
}
if (!eject_drive_letter(DriveLetter)) return NULL;
Py_RETURN_NONE;
}
PSP_DEVICE_INTERFACE_DETAIL_DATA
get_device_ancestors(HDEVINFO hDevInfo, DWORD index, PyObject *candidates, BOOL *iterate, BOOL ddebug) {
SP_DEVICE_INTERFACE_DATA interfaceData;
SP_DEVINFO_DATA devInfoData;
BOOL status;
PSP_DEVICE_INTERFACE_DETAIL_DATA interfaceDetailData;
DWORD interfaceDetailDataSize,
reqSize;
DEVINST parent, pos;
wchar_t temp[BUFSIZE];
int i;
PyObject *devid;
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+50);
if ( interfaceDetailData == NULL ) {
PyErr_NoMemory();
return NULL;
}
interfaceDetailData->cbSize = sizeof (SP_INTERFACE_DEVICE_DETAIL_DATA);
devInfoData.cbSize = sizeof(SP_DEVINFO_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 (ddebug) printf("Getting ancestors\n"); fflush(stdout);
if ( status == FALSE ) {PyErr_SetFromWindowsErr(0); PyMem_Free(interfaceDetailData); return NULL;}
pos = devInfoData.DevInst;
for(i = 0; i < 10; i++) {
// Get the device instance of parent.
if (CM_Get_Parent(&parent, pos, 0) != CR_SUCCESS) break;
if (CM_Get_Device_ID(parent, temp, BUFSIZE, 0) == CR_SUCCESS) {
if (ddebug) console_out(L"device id: %s\n", temp);
devid = PyUnicode_FromWideChar(temp, wcslen(temp));
if (devid) {
PyList_Append(candidates, devid);
Py_DECREF(devid);
}
}
pos = parent;
}
return interfaceDetailData;
}
static PyObject *
winutil_get_removable_drives(PyObject *self, PyObject *args) {
HDEVINFO hDevInfo;
BOOL iterate = TRUE, ddebug = FALSE;
PSP_DEVICE_INTERFACE_DETAIL_DATA interfaceDetailData;
DWORD i;
unsigned int j;
size_t length;
WCHAR volume[BUFSIZE];
struct tagDrives g_drives[MAX_DRIVES];
PyObject *volumes, *key, *candidates, *pdebug = Py_False, *temp;
if (!PyArg_ParseTuple(args, "|O", &pdebug)) {
return NULL;
}
// Find all removable drives
for (j = 0; j < MAX_DRIVES; j++) g_drives[j].letter = 0;
if (!get_all_removable_disks(g_drives)) return NULL;
volumes = PyDict_New();
if (volumes == NULL) return PyErr_NoMemory();
ddebug = PyObject_IsTrue(pdebug);
hDevInfo = create_device_info_set((LPGUID)&GUID_DEVINTERFACE_VOLUME,
NULL, NULL, DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
if (hDevInfo == INVALID_HANDLE_VALUE) { Py_DECREF(volumes); return NULL; }
// Enumerate through the set
for (i=0; iterate; i++) {
candidates = PyList_New(0);
if (candidates == NULL) { Py_DECREF(volumes); return PyErr_NoMemory();}
interfaceDetailData = get_device_ancestors(hDevInfo, i, candidates, &iterate, ddebug);
if (interfaceDetailData == NULL) {
PyErr_Print();
Py_DECREF(candidates); candidates = NULL;
continue;
}
length = wcslen(interfaceDetailData->DevicePath);
interfaceDetailData->DevicePath[length] = L'\\';
interfaceDetailData->DevicePath[length+1] = 0;
if (ddebug) console_out(L"Device path: %s\n", interfaceDetailData->DevicePath);
// On Vista+ DevicePath contains the information we need.
temp = PyUnicode_FromWideChar(interfaceDetailData->DevicePath, length);
if (temp == NULL) return PyErr_NoMemory();
PyList_Append(candidates, temp);
Py_DECREF(temp);
if(GetVolumeNameForVolumeMountPointW(interfaceDetailData->DevicePath, volume, BUFSIZE)) {
if (ddebug) console_out(L"Volume: %s\n", volume);
for(j = 0; j < MAX_DRIVES; j++) {
if(g_drives[j].letter != 0 && wcscmp(g_drives[j].volume, volume)==0) {
if (ddebug) printf("Found drive: %c\n", (char)g_drives[j].letter); fflush(stdout);
key = PyBytes_FromFormat("%c", (char)g_drives[j].letter);
if (key == NULL) return PyErr_NoMemory();
PyDict_SetItem(volumes, key, candidates);
Py_DECREF(key); key = NULL;
break;
}
}
}
Py_XDECREF(candidates); candidates = NULL;
PyMem_Free(interfaceDetailData);
} //for
SetupDiDestroyDeviceInfoList(hDevInfo);
return volumes;
}
static PyObject *
winutil_get_usb_devices(PyObject *self, PyObject *args) {
unsigned int j;
size_t buffersize;
HDEVINFO hDevInfo;
DWORD i; BOOL iterate = TRUE;
PyObject *devices, *temp = (PyObject *)1;
LPWSTR buffer;
BOOL ok = 1;
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) {
Py_DECREF(devices);
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] = towlower(buffer[j]);
temp = PyUnicode_FromWideChar(buffer, buffersize);
PyMem_Free(buffer);
if (temp == NULL) {
PyErr_NoMemory();
ok = 0;
break;
}
PyList_Append(devices, temp); Py_DECREF(temp); temp = NULL;
} //for
if (!ok) { 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;
LPWSTR 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 int static int
gettmarg(PyObject *args, struct tm *p) gettmarg(PyObject *args, struct tm *p)
{ {
@ -951,23 +339,6 @@ static PyMethodDef WinutilMethods[] = {
"script being run. So to replace sys.argv, you should use " "script being run. So to replace sys.argv, you should use "
"sys.argv[1:] = argv()[1:]."}, "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_removable_drives", winutil_get_removable_drives, METH_VARARGS,
"get_removable_drives(debug=False) -> dict\n\n"
"Return mapping of all removable drives in the system. Maps drive letters "
"to a list of device id strings, atleast one of which will carry the information "
"needed for device matching. On Vista+ it is always the last string in the list. "
"Note that you should upper case all strings."},
{"set_debug", winutil_set_debug, METH_VARARGS, {"set_debug", winutil_set_debug, METH_VARARGS,
"set_debug(bool)\n\nSet debugging mode." "set_debug(bool)\n\nSet debugging mode."
}, },
@ -981,10 +352,6 @@ 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."
},
{"internet_connected", winutil_internet_connected, METH_VARARGS, {"internet_connected", winutil_internet_connected, METH_VARARGS,
"internet_connected()\n\nReturn True if there is an active internet connection" "internet_connected()\n\nReturn True if there is an active internet connection"
}, },
@ -1003,9 +370,6 @@ initwinutil(void) {
"Defines utility methods to interface with windows." "Defines utility methods to interface with windows."
); );
if (m == NULL) return; if (m == NULL) return;
DriveError = PyErr_NewException("winutil.DriveError", NULL, NULL);
if (DriveError == NULL) return;
PyModule_AddObject(m, "DriveError", DriveError);
PyModule_AddIntConstant(m, "CSIDL_ADMINTOOLS", CSIDL_ADMINTOOLS); PyModule_AddIntConstant(m, "CSIDL_ADMINTOOLS", CSIDL_ADMINTOOLS);
PyModule_AddIntConstant(m, "CSIDL_APPDATA", CSIDL_APPDATA); PyModule_AddIntConstant(m, "CSIDL_APPDATA", CSIDL_APPDATA);