diff --git a/src/calibre/devices/mtp/windows/device_enumeration.cpp b/src/calibre/devices/mtp/windows/device_enumeration.cpp index ecc085af89..a5f4da5072 100644 --- a/src/calibre/devices/mtp/windows/device_enumeration.cpp +++ b/src/calibre/devices/mtp/windows/device_enumeration.cpp @@ -45,13 +45,13 @@ IPortableDeviceValues *get_client_information() { // {{{ return client_information.Detach(); } // }}} -IPortableDevice *open_device(const wchar_t *pnp_id, CComPtr &client_information) { // {{{ - IPortableDevice *device = NULL; +IPortableDevice* +open_device(const wchar_t *pnp_id, CComPtr &client_information) { // {{{ + CComPtr device; HRESULT hr; Py_BEGIN_ALLOW_THREADS; - hr = CoCreateInstance(CLSID_PortableDevice, NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&device)); + hr = device.CoCreateInstance(CLSID_PortableDevice, NULL, CLSCTX_INPROC_SERVER); Py_END_ALLOW_THREADS; if (FAILED(hr)) { hresult_set_exc("Failed to create IPortableDevice", hr); device = NULL; } else { @@ -60,82 +60,80 @@ IPortableDevice *open_device(const wchar_t *pnp_id, CComPtrRelease(); + device.Release(); Py_END_ALLOW_THREADS; - device = NULL; hresult_set_exc((hr == E_ACCESSDENIED) ? "Read/write access to device is denied": "Failed to open device", hr); } } - return device; + return device.Detach(); } // }}} -PyObject* get_storage_info(IPortableDevice *device) { // {{{ +static PyObject* +get_storage_info(IPortableDevice *device) { // {{{ HRESULT hr, hr2; - IPortableDeviceContent *content = NULL; - IEnumPortableDeviceObjectIDs *objects = NULL; - IPortableDeviceProperties *properties = NULL; - IPortableDeviceKeyCollection *storage_properties = NULL; - IPortableDeviceValues *values = NULL; - PyObject *ans = NULL, *storage = NULL, *so = NULL, *desc = NULL, *soid = NULL; + CComPtr content = NULL; + CComPtr objects = NULL; + CComPtr properties = NULL; + CComPtr storage_properties = NULL; DWORD fetched, i; - PWSTR object_ids[10]; GUID guid; ULONGLONG capacity, free_space, capacity_objects, free_objects; ULONG access, storage_type = WPD_STORAGE_TYPE_UNDEFINED; - LPWSTR storage_desc = NULL, st = NULL; - storage = PyList_New(0); - if (storage == NULL) { PyErr_NoMemory(); goto end; } + pyobject_raii storage(PyList_New(0)); + if (!storage) return NULL; Py_BEGIN_ALLOW_THREADS; hr = device->Content(&content); Py_END_ALLOW_THREADS; - if (FAILED(hr)) {hresult_set_exc("Failed to get content interface from device", hr); goto end;} + if (FAILED(hr)) {hresult_set_exc("Failed to get content interface from device", hr); return NULL;} Py_BEGIN_ALLOW_THREADS; hr = content->Properties(&properties); Py_END_ALLOW_THREADS; - if (FAILED(hr)) {hresult_set_exc("Failed to get properties interface", hr); goto end;} + if (FAILED(hr)) {hresult_set_exc("Failed to get properties interface", hr); return NULL;} Py_BEGIN_ALLOW_THREADS; - hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, NULL, - CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&storage_properties)); + hr = storage_properties.CoCreateInstance(CLSID_PortableDeviceKeyCollection, NULL, CLSCTX_INPROC_SERVER); Py_END_ALLOW_THREADS; - if (FAILED(hr)) {hresult_set_exc("Failed to create storage properties collection", hr); goto end;} + if (FAILED(hr)) {hresult_set_exc("Failed to create storage properties collection", hr); return NULL;} - Py_BEGIN_ALLOW_THREADS; - hr = storage_properties->Add(WPD_OBJECT_CONTENT_TYPE); - hr = storage_properties->Add(WPD_FUNCTIONAL_OBJECT_CATEGORY); - hr = storage_properties->Add(WPD_STORAGE_DESCRIPTION); - hr = storage_properties->Add(WPD_STORAGE_CAPACITY); - hr = storage_properties->Add(WPD_STORAGE_CAPACITY_IN_OBJECTS); - hr = storage_properties->Add(WPD_STORAGE_FREE_SPACE_IN_BYTES); - hr = storage_properties->Add(WPD_STORAGE_FREE_SPACE_IN_OBJECTS); - hr = storage_properties->Add(WPD_STORAGE_ACCESS_CAPABILITY); - hr = storage_properties->Add(WPD_STORAGE_FILE_SYSTEM_TYPE); - hr = storage_properties->Add(WPD_STORAGE_TYPE); - hr = storage_properties->Add(WPD_OBJECT_NAME); - Py_END_ALLOW_THREADS; - if (FAILED(hr)) {hresult_set_exc("Failed to create collection of properties for storage query", hr); goto end; } +#define A(what) hr = storage_properties->Add(what); if (FAILED(hr)) { hresult_set_exc("Failed to add storage property " #what " for storage query", hr); return NULL; } + A(WPD_OBJECT_CONTENT_TYPE); + A(WPD_FUNCTIONAL_OBJECT_CATEGORY); + A(WPD_STORAGE_DESCRIPTION); + A(WPD_STORAGE_CAPACITY); + A(WPD_STORAGE_CAPACITY_IN_OBJECTS); + A(WPD_STORAGE_FREE_SPACE_IN_BYTES); + A(WPD_STORAGE_FREE_SPACE_IN_OBJECTS); + A(WPD_STORAGE_ACCESS_CAPABILITY); + A(WPD_STORAGE_FILE_SYSTEM_TYPE); + A(WPD_STORAGE_TYPE); + A(WPD_OBJECT_NAME); +#undef A Py_BEGIN_ALLOW_THREADS; hr = content->EnumObjects(0, WPD_DEVICE_OBJECT_ID, NULL, &objects); Py_END_ALLOW_THREADS; - if (FAILED(hr)) {hresult_set_exc("Failed to get objects from device", hr); goto end;} + if (FAILED(hr)) {hresult_set_exc("Failed to get objects from device", hr); return NULL;} hr = S_OK; while (hr == S_OK) { + wchar_t* object_ids[16] = {0}; Py_BEGIN_ALLOW_THREADS; - hr = objects->Next(10, object_ids, &fetched); + hr = objects->Next(arraysz(object_ids), object_ids, &fetched); Py_END_ALLOW_THREADS; if (SUCCEEDED(hr)) { + com_wchar_raii cleanup[arraysz(object_ids)]; + for (i = 0; i < arraysz(object_ids); i++) { cleanup[i].set_ptr(object_ids[i]); }; for(i = 0; i < fetched; i++) { + CComPtr values; Py_BEGIN_ALLOW_THREADS; hr2 = properties->GetValues(object_ids[i], storage_properties, &values); Py_END_ALLOW_THREADS; - if SUCCEEDED(hr2) { + if (SUCCEEDED(hr2)) { if ( SUCCEEDED(values->GetGuidValue(WPD_OBJECT_CONTENT_TYPE, &guid)) && IsEqualGUID(guid, WPD_CONTENT_TYPE_FUNCTIONAL_OBJECT) && SUCCEEDED(values->GetGuidValue(WPD_FUNCTIONAL_OBJECT_CATEGORY, &guid)) && IsEqualGUID(guid, WPD_FUNCTIONAL_CATEGORY_STORAGE) @@ -146,28 +144,23 @@ PyObject* get_storage_info(IPortableDevice *device) { // {{{ values->GetUnsignedLargeIntegerValue(WPD_STORAGE_FREE_SPACE_IN_BYTES, &free_space); values->GetUnsignedLargeIntegerValue(WPD_STORAGE_FREE_SPACE_IN_OBJECTS, &free_objects); values->GetUnsignedIntegerValue(WPD_STORAGE_TYPE, &storage_type); - desc = Py_False; - if (SUCCEEDED(values->GetUnsignedIntegerValue(WPD_STORAGE_ACCESS_CAPABILITY, &access)) && access == WPD_STORAGE_ACCESS_CAPABILITY_READWRITE) desc = Py_True; - soid = PyUnicode_FromWideChar(object_ids[i], wcslen(object_ids[i])); - if (soid == NULL) { PyErr_NoMemory(); goto end; } - so = Py_BuildValue("{s:K, s:K, s:K, s:K, s:O, s:N}", - "capacity", capacity, "capacity_objects", capacity_objects, "free_space", free_space, "free_objects", free_objects, "rw", desc, "id", soid); - if (so == NULL) { PyErr_NoMemory(); goto end; } - if (SUCCEEDED(values->GetStringValue(WPD_STORAGE_DESCRIPTION, &storage_desc))) { - desc = PyUnicode_FromWideChar(storage_desc, wcslen(storage_desc)); - if (desc != NULL) { PyDict_SetItemString(so, "description", desc); Py_DECREF(desc);} - CoTaskMemFree(storage_desc); storage_desc = NULL; - } - if (SUCCEEDED(values->GetStringValue(WPD_OBJECT_NAME, &storage_desc))) { - desc = PyUnicode_FromWideChar(storage_desc, wcslen(storage_desc)); - if (desc != NULL) { PyDict_SetItemString(so, "name", desc); Py_DECREF(desc);} - CoTaskMemFree(storage_desc); storage_desc = NULL; - } - if (SUCCEEDED(values->GetStringValue(WPD_STORAGE_FILE_SYSTEM_TYPE, &storage_desc))) { - desc = PyUnicode_FromWideChar(storage_desc, wcslen(storage_desc)); - if (desc != NULL) { PyDict_SetItemString(so, "filesystem", desc); Py_DECREF(desc);} - CoTaskMemFree(storage_desc); storage_desc = NULL; - } + PyObject *paccess = Py_False; + if (SUCCEEDED(values->GetUnsignedIntegerValue(WPD_STORAGE_ACCESS_CAPABILITY, &access)) && access == WPD_STORAGE_ACCESS_CAPABILITY_READWRITE) paccess = Py_True; + pyobject_raii soid(PyUnicode_FromWideChar(object_ids[i], -1)); + if (!soid) return NULL; + pyobject_raii so(Py_BuildValue("{s:K, s:K, s:K, s:K, s:O, s:O}", + "capacity", capacity, "capacity_objects", capacity_objects, "free_space", free_space, "free_objects", free_objects, "rw", paccess, "id", soid.ptr())); + if (!so) return NULL; +#define A(which, key) { com_wchar_raii buf; if (SUCCEEDED(values->GetStringValue(which, buf.address()))) { \ + pyobject_raii d(PyUnicode_FromWideChar(buf.ptr(), -1)); \ + if (d) PyDict_SetItemString(so.ptr(), key, d.ptr()); \ + else PyErr_Clear(); \ + }} + A(WPD_STORAGE_DESCRIPTION, "description"); + A(WPD_OBJECT_NAME, "name"); + A(WPD_STORAGE_FILE_SYSTEM_TYPE, "filesystem"); +#undef A + const wchar_t *st; switch(storage_type) { case WPD_STORAGE_TYPE_REMOVABLE_RAM: st = L"removable_ram"; @@ -184,26 +177,15 @@ PyObject* get_storage_info(IPortableDevice *device) { // {{{ default: st = L"unknown_unknown"; } - desc = PyUnicode_FromWideChar(st, wcslen(st)); - if (desc != NULL) {PyDict_SetItemString(so, "type", desc); Py_DECREF(desc);} - desc = NULL; - PyList_Append(storage, so); - Py_DECREF(so); + pyobject_raii dt(PyUnicode_FromWideChar(st, -1)); + if (dt) PyDict_SetItemString(so.ptr(), "type", dt.ptr()); + if (PyList_Append(storage.ptr(), so.ptr()) != 0) return NULL; } } } - for (i = 0; i < fetched; i ++) { CoTaskMemFree(object_ids[i]); object_ids[i] = NULL;} }// if(SUCCEEDED(hr)) } - ans = storage; - -end: - if (content != NULL) content->Release(); - if (objects != NULL) objects->Release(); - if (properties != NULL) properties->Release(); - if (storage_properties != NULL) storage_properties->Release(); - if (values != NULL) values->Release(); - return ans; + return storage.detach(); } // }}} PyObject* @@ -219,7 +201,7 @@ get_device_information(CComPtr &device, IPortableDeviceProperti DWORD num_of_categories, i; LPWSTR temp; ULONG ti; - PyObject *t, *ans = NULL, *storage = NULL; + PyObject *t, *ans = NULL; const char *type = NULL; Py_BEGIN_ALLOW_THREADS; @@ -286,6 +268,7 @@ get_device_information(CComPtr &device, IPortableDeviceProperti // } if (SUCCEEDED(values->GetUnsignedIntegerValue(WPD_DEVICE_TYPE, &ti))) { + type = "unknown"; switch (ti) { case WPD_DEVICE_TYPE_CAMERA: type = "camera"; break; @@ -299,8 +282,8 @@ get_device_information(CComPtr &device, IPortableDeviceProperti type = "personal information manager"; break; case WPD_DEVICE_TYPE_AUDIO_RECORDER: type = "audio recorder"; break; - default: - type = "unknown"; + case WPD_DEVICE_TYPE_GENERIC: + break; } t = PyUnicode_FromString(type); if (t != NULL) { @@ -353,20 +336,19 @@ get_device_information(CComPtr &device, IPortableDeviceProperti PyDict_SetItemString(ans, "has_storage", t); if (t == Py_True) { - storage = get_storage_info(device); - if (storage == NULL) { - PyObject *exc_type, *exc_value, *exc_tb; - PyErr_Fetch(&exc_type, &exc_value, &exc_tb); - if (exc_type != NULL && exc_value != NULL) { - PyErr_NormalizeException(&exc_type, &exc_value, &exc_tb); - PyDict_SetItemString(ans, "storage_error", exc_value); - Py_DECREF(exc_value); exc_value = NULL; - } - Py_XDECREF(exc_type); Py_XDECREF(exc_value); Py_XDECREF(exc_tb); - goto end; - } - PyDict_SetItemString(ans, "storage", storage); - + pyobject_raii storage(get_storage_info(device)); + if (!storage) { + pyobject_raii exc_type, exc_value, exc_tb; + PyErr_Fetch(exc_type.address(), exc_value.address(), exc_tb.address()); + if (exc_type) { + PyErr_NormalizeException(exc_type.address(), exc_value.address(), exc_tb.address()); + PyDict_SetItemString(ans, "storage_error", exc_value.ptr()); + } else { + PyDict_SetItemString(ans, "storage_error", PyUnicode_FromString("get_storage_info() failed without an error set")); + } + } else { + PyDict_SetItemString(ans, "storage", storage.ptr()); + } } Py_BEGIN_ALLOW_THREADS; diff --git a/src/calibre/utils/windows/common.h b/src/calibre/utils/windows/common.h index a3a845a73f..b152c730af 100644 --- a/src/calibre/utils/windows/common.h +++ b/src/calibre/utils/windows/common.h @@ -45,7 +45,9 @@ class wchar_raii { } wchar_t *ptr() { return handle; } + wchar_t *detach() { wchar_t *ans = handle; handle = NULL; return ans; } void set_ptr(wchar_t *val) { handle = val; } + wchar_t **address() { return &handle; } explicit operator bool() const { return handle != NULL; } }; @@ -67,6 +69,8 @@ class com_wchar_raii { } wchar_t *ptr() { return handle; } + void set_ptr(wchar_t *val) { handle = val; } + wchar_t *detach() { wchar_t *ans = handle; handle = NULL; return ans; } wchar_t **address() { return &handle; } explicit operator bool() const { return handle != NULL; } };