Use RAII for get_storage_info()

This commit is contained in:
Kovid Goyal 2021-04-20 22:37:43 +05:30
parent 014f7fe82a
commit d7150a4b86
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 80 additions and 94 deletions

View File

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

View File

@ -45,7 +45,9 @@ class wchar_raii {
} }
wchar_t *ptr() { return handle; } wchar_t *ptr() { return handle; }
wchar_t *detach() { wchar_t *ans = handle; handle = NULL; return ans; }
void set_ptr(wchar_t *val) { handle = val; } void set_ptr(wchar_t *val) { handle = val; }
wchar_t **address() { return &handle; }
explicit operator bool() const { return handle != NULL; } explicit operator bool() const { return handle != NULL; }
}; };
@ -67,6 +69,8 @@ class com_wchar_raii {
} }
wchar_t *ptr() { return handle; } 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; } wchar_t **address() { return &handle; }
explicit operator bool() const { return handle != NULL; } explicit operator bool() const { return handle != NULL; }
}; };