Use RAII to read the filesystem

This commit is contained in:
Kovid Goyal 2021-04-22 21:07:33 +05:30
parent 5d9e2296f3
commit d152d5d62d
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C

View File

@ -9,11 +9,10 @@
#include <new> #include <new>
#define ADDPROP(x) hr = properties->Add(x); if (FAILED(hr)) { hresult_set_exc("Failed to add property " #x " to filesystem properties collection", hr); properties->Release(); return NULL; }
namespace wpd { namespace wpd {
static int static int
pump_waiting_messages() { _pump_waiting_messages() {
UINT firstMsg = 0, lastMsg = 0; UINT firstMsg = 0, lastMsg = 0;
MSG msg; MSG msg;
int result = 0; int result = 0;
@ -32,17 +31,19 @@ pump_waiting_messages() {
return result; return result;
} }
static IPortableDeviceKeyCollection* create_filesystem_properties_collection() { // {{{ static IPortableDeviceKeyCollection*
IPortableDeviceKeyCollection *properties = NULL; create_filesystem_properties_collection() { // {{{
CComPtr<IPortableDeviceKeyCollection> properties;
HRESULT hr; HRESULT hr;
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, NULL, hr = properties.CoCreateInstance(CLSID_PortableDeviceKeyCollection, NULL, CLSCTX_INPROC_SERVER);
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&properties));
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to create filesystem properties collection", hr); return NULL; } if (FAILED(hr)) { hresult_set_exc("Failed to create filesystem properties collection", hr); return NULL; }
#define ADDPROP(x) hr = properties->Add(x); if (FAILED(hr)) { hresult_set_exc("Failed to add property " #x " to filesystem properties collection", hr); return NULL; }
ADDPROP(WPD_OBJECT_CONTENT_TYPE); ADDPROP(WPD_OBJECT_CONTENT_TYPE);
ADDPROP(WPD_OBJECT_PARENT_ID); ADDPROP(WPD_OBJECT_PARENT_ID);
ADDPROP(WPD_OBJECT_PERSISTENT_UNIQUE_ID); ADDPROP(WPD_OBJECT_PERSISTENT_UNIQUE_ID);
@ -55,14 +56,15 @@ static IPortableDeviceKeyCollection* create_filesystem_properties_collection() {
ADDPROP(WPD_OBJECT_SIZE); ADDPROP(WPD_OBJECT_SIZE);
ADDPROP(WPD_OBJECT_DATE_CREATED); ADDPROP(WPD_OBJECT_DATE_CREATED);
ADDPROP(WPD_OBJECT_DATE_MODIFIED); ADDPROP(WPD_OBJECT_DATE_MODIFIED);
#undef ADDPROP
return properties; return properties.Detach();
} // }}} } // }}}
// Convert properties from COM to python {{{ // Convert properties from COM to python {{{
static void static void
set_string_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, CComPtr<IPortableDeviceValues> &properties) { set_string_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, const CComPtr<IPortableDeviceValues> &properties) {
HRESULT hr; HRESULT hr;
com_wchar_raii property; com_wchar_raii property;
hr = properties->GetStringValue(key, property.unsafe_address()); hr = properties->GetStringValue(key, property.unsafe_address());
@ -73,7 +75,7 @@ set_string_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, CComP
} }
static void static void
set_bool_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, CComPtr<IPortableDeviceValues> &properties) { set_bool_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, const CComPtr<IPortableDeviceValues> &properties) {
BOOL ok = 0; BOOL ok = 0;
HRESULT hr; HRESULT hr;
@ -84,7 +86,7 @@ set_bool_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, CComPtr
} }
static void static void
set_size_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, CComPtr<IPortableDeviceValues> &properties) { set_size_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, const CComPtr<IPortableDeviceValues> &properties) {
ULONGLONG val = 0; ULONGLONG val = 0;
HRESULT hr; HRESULT hr;
hr = properties->GetUnsignedLargeIntegerValue(key, &val); hr = properties->GetUnsignedLargeIntegerValue(key, &val);
@ -97,23 +99,23 @@ set_size_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, CComPtr
} }
static void static void
set_date_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, CComPtr<IPortableDeviceValues> &properties) { set_date_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, const CComPtr<IPortableDeviceValues> &properties) {
PROPVARIANT ts = {0}; PROPVARIANT ts = {0};
if (SUCCEEDED(properties->GetValue(key, &ts))) { if (SUCCEEDED(properties->GetValue(key, &ts))) {
SYSTEMTIME st; SYSTEMTIME st;
if (ts.vt == VT_DATE && VariantTimeToSystemTime(ts.date, &st)) { if (ts.vt == VT_DATE && VariantTimeToSystemTime(ts.date, &st)) {
const unsigned int microseconds = 1000 * st.wMilliseconds; const unsigned int microseconds = 1000 * st.wMilliseconds;
PyObject *t = Py_BuildValue("H H H H H H I", (unsigned short)st.wYear, pyobject_raii t(Py_BuildValue("H H H H H H I", (unsigned short)st.wYear,
(unsigned short)st.wMonth, (unsigned short)st.wDay, (unsigned short)st.wMonth, (unsigned short)st.wDay,
(unsigned short)st.wHour, (unsigned short)st.wMinute, (unsigned short)st.wHour, (unsigned short)st.wMinute,
(unsigned short)st.wSecond, microseconds); (unsigned short)st.wSecond, microseconds));
if (t != NULL) { PyDict_SetItemString(dict, pykey, t); Py_DECREF(t); } if (t) if (PyDict_SetItemString(dict, pykey, t.ptr()) != 0) PyErr_Clear();
} }
} }
} }
static void static void
set_content_type_property(PyObject *dict, CComPtr<IPortableDeviceValues> &properties) { set_content_type_property(PyObject *dict, const CComPtr<IPortableDeviceValues> &properties) {
GUID guid = GUID_NULL; GUID guid = GUID_NULL;
BOOL is_folder = 0; BOOL is_folder = 0;
@ -122,7 +124,7 @@ set_content_type_property(PyObject *dict, CComPtr<IPortableDeviceValues> &proper
} }
static void static void
set_properties(PyObject *obj, CComPtr<IPortableDeviceValues> &values) { set_properties(PyObject *obj, const CComPtr<IPortableDeviceValues> &values) {
set_content_type_property(obj, values); set_content_type_property(obj, values);
set_string_property(obj, WPD_OBJECT_PARENT_ID, "parent_id", values); set_string_property(obj, WPD_OBJECT_PARENT_ID, "parent_id", values);
@ -143,9 +145,9 @@ set_properties(PyObject *obj, CComPtr<IPortableDeviceValues> &values) {
// }}} // }}}
// Bulk get filesystem {{{ // Bulk get filesystem {{{
class GetBulkCallback : public IPortableDevicePropertiesBulkCallback {
public: class GetBulkPropertiesCallback : public IPortableDevicePropertiesBulkCallback {
private:
PyObject *items; PyObject *items;
PyObject *subfolders; PyObject *subfolders;
unsigned int level; unsigned int level;
@ -154,21 +156,69 @@ public:
PyThreadState *thread_state; PyThreadState *thread_state;
PyObject *callback; PyObject *callback;
GetBulkCallback(PyObject *items_dict, PyObject *subfolders, unsigned int level, HANDLE ev, PyObject* pycallback) : items(items_dict), subfolders(subfolders), level(level), complete(ev), self_ref(1), thread_state(NULL), callback(pycallback) {} void release_python_gil() { if (thread_state == NULL) thread_state = PyEval_SaveThread(); }
~GetBulkCallback() {} void acquire_python_gil() { PyEval_RestoreThread(thread_state); thread_state = NULL; }
void do_one_object(CComPtr<IPortableDeviceValues> &properties) {
com_wchar_raii property;
if (!SUCCEEDED(properties->GetStringValue(WPD_OBJECT_ID, property.unsafe_address()))) return;
pyobject_raii temp(PyUnicode_FromWideChar(property.ptr(), -1));
if (!temp) { PyErr_Clear(); return; }
pyobject_raii obj(PyDict_GetItem(this->items, temp.ptr()));
if (!obj) {
obj.attach(Py_BuildValue("{s:O}", "id", temp.ptr()));
if (!obj) { PyErr_Clear(); return; }
if (PyDict_SetItem(this->items, temp.ptr(), obj.ptr()) != 0) { PyErr_Clear(); return; }
} else Py_INCREF(obj.ptr());
set_properties(obj.ptr(), properties);
pyobject_raii r(PyObject_CallFunction(callback, "OI", obj.ptr(), this->level));
if (!r) PyErr_Clear();
else if (r && PyObject_IsTrue(r.ptr())) {
PyObject *borrowed = PyDict_GetItemString(obj.ptr(), "id");
if (borrowed) if (PyList_Append(this->subfolders, borrowed) != 0) PyErr_Clear();
}
}
void handle_values(IPortableDeviceValuesCollection* values) {
DWORD num = 0;
if (!items) return;
if (!SUCCEEDED(values->GetCount(&num))) return;
acquire_python_gil();
for (DWORD i = 0; i < num; i++) {
CComPtr<IPortableDeviceValues> properties;
if (SUCCEEDED(values->GetAt(i, &properties))) do_one_object(properties);
}
release_python_gil();
}
public:
GetBulkPropertiesCallback() : items(NULL), subfolders(NULL), level(0), complete(INVALID_HANDLE_VALUE), self_ref(0), thread_state(NULL), callback(NULL) {}
~GetBulkPropertiesCallback() { if (complete != INVALID_HANDLE_VALUE) CloseHandle(complete); complete = INVALID_HANDLE_VALUE; }
bool start_processing(PyObject *items, PyObject *subfolders, unsigned int level, PyObject *callback) {
complete = CreateEvent(NULL, FALSE, FALSE, NULL);
if (complete == NULL || complete == INVALID_HANDLE_VALUE) return false;
this->items = items; this->subfolders = subfolders; this->level = level; this->callback = callback;
self_ref = 0;
return true;
}
void end_processing() {
if (complete != INVALID_HANDLE_VALUE) CloseHandle(complete);
items = NULL; subfolders = NULL; level = 0; complete = INVALID_HANDLE_VALUE; callback = NULL; thread_state = NULL;
}
HRESULT __stdcall OnStart(REFGUID Context) { return S_OK; } HRESULT __stdcall OnStart(REFGUID Context) { return S_OK; }
HRESULT __stdcall OnEnd(REFGUID Context, HRESULT hrStatus) { if (complete != INVALID_HANDLE_VALUE) SetEvent(complete); return S_OK; }
HRESULT __stdcall OnEnd(REFGUID Context, HRESULT hrStatus) { SetEvent(this->complete); return S_OK; }
ULONG __stdcall AddRef() { InterlockedIncrement((long*) &self_ref); return self_ref; } ULONG __stdcall AddRef() { InterlockedIncrement((long*) &self_ref); return self_ref; }
ULONG __stdcall Release() { ULONG __stdcall Release() {
ULONG refcnt = self_ref - 1; ULONG refcnt = self_ref - 1;
if (InterlockedDecrement((long*) &self_ref) == 0) { delete this; return 0; } if (InterlockedDecrement((long*) &self_ref) == 0) { delete this; return 0; }
return refcnt; return refcnt;
} }
HRESULT __stdcall QueryInterface(REFIID riid, LPVOID* obj) { HRESULT __stdcall QueryInterface(REFIID riid, LPVOID* obj) {
HRESULT hr = S_OK; HRESULT hr = S_OK;
if (obj == NULL) { hr = E_INVALIDARG; return hr; } if (obj == NULL) { hr = E_INVALIDARG; return hr; }
@ -183,106 +233,94 @@ public:
} }
return hr; return hr;
} }
HRESULT __stdcall GetBulkPropertiesCallback::OnProgress(REFGUID Context, IPortableDeviceValuesCollection* values) {
HRESULT __stdcall OnProgress(REFGUID Context, IPortableDeviceValuesCollection* values) { handle_values(values);
DWORD num = 0, i;
HRESULT hr;
if (SUCCEEDED(values->GetCount(&num))) {
PyEval_RestoreThread(this->thread_state);
for (i = 0; i < num; i++) {
CComPtr<IPortableDeviceValues> properties;
hr = values->GetAt(i, &properties);
if (SUCCEEDED(hr)) {
com_wchar_raii property;
hr = properties->GetStringValue(WPD_OBJECT_ID, property.unsafe_address());
if (!SUCCEEDED(hr)) continue;
pyobject_raii temp(PyUnicode_FromWideChar(property.ptr(), -1));
if (!temp) { PyErr_Clear(); continue; }
pyobject_raii obj(PyDict_GetItem(this->items, temp.ptr()));
if (!obj) {
obj.attach(Py_BuildValue("{s:O}", "id", temp.ptr()));
if (!obj) { PyErr_Clear(); continue; }
if (PyDict_SetItem(this->items, temp.ptr(), obj.ptr()) != 0) { PyErr_Clear(); continue; }
} else Py_INCREF(obj.ptr());
set_properties(obj.ptr(), properties);
pyobject_raii r(PyObject_CallFunction(callback, "OI", obj.ptr(), this->level));
if (!r) PyErr_Clear();
else if (r && PyObject_IsTrue(r.ptr())) {
PyObject *borrowed = PyDict_GetItemString(obj.ptr(), "id");
if (borrowed) if (PyList_Append(this->subfolders, borrowed) != 0) PyErr_Clear();
}
}
} // end for loop
this->thread_state = PyEval_SaveThread();
}
return S_OK; return S_OK;
} }
DWORD wait_for_messages(int seconds=60) {
release_python_gil();
DWORD wait_result = MsgWaitForMultipleObjects(1, &complete, FALSE, seconds * 1000, QS_ALLEVENTS);
acquire_python_gil();
return wait_result;
}
int pump_waiting_messages() {
release_python_gil();
int pump_result = _pump_waiting_messages();
acquire_python_gil();
return pump_result;
}
}; };
static bool bulk_get_filesystem(unsigned int level, IPortableDevice *device, IPortableDevicePropertiesBulk *bulk_properties, IPortableDevicePropVariantCollection *object_ids, PyObject *pycallback, PyObject *ans, PyObject *subfolders) {
GUID guid_context = GUID_NULL; static bool
HANDLE ev = NULL; bulk_get_filesystem(
IPortableDeviceKeyCollection *properties; unsigned int level, IPortableDevice *device, IPortableDevicePropertiesBulk *bulk_properties,
GetBulkCallback *callback = NULL; CComPtr<IPortableDevicePropVariantCollection> &object_ids,
PyObject *pycallback, PyObject *ans, PyObject *subfolders
) {
CComPtr<IPortableDeviceKeyCollection> properties(create_filesystem_properties_collection());
if (!properties) return false;
GetBulkPropertiesCallback *bulk_properties_callback = new (std::nothrow) GetBulkPropertiesCallback();
if (!bulk_properties_callback) { PyErr_NoMemory(); return false; }
GUID guid_context;
HRESULT hr; HRESULT hr;
DWORD wait_result; if (!bulk_properties_callback->start_processing(ans, subfolders, level, pycallback)) {
int pump_result; delete bulk_properties_callback;
bool ok = true; PyErr_NoMemory();
return false;
ev = CreateEvent(NULL, FALSE, FALSE, NULL); }
if (ev == NULL) {PyErr_NoMemory(); return false; } hr = bulk_properties->QueueGetValuesByObjectList(object_ids, properties, bulk_properties_callback, &guid_context);
if (FAILED(hr)) {
properties = create_filesystem_properties_collection(); bulk_properties_callback->end_processing();
if (properties == NULL) goto end; delete bulk_properties_callback;
hresult_set_exc("Failed to queue bulk property retrieval", hr);
callback = new (std::nothrow) GetBulkCallback(ans, subfolders, level, ev, pycallback); return false;
if (callback == NULL) { PyErr_NoMemory(); goto end; } }
hr = bulk_properties->QueueGetValuesByObjectList(object_ids, properties, callback, &guid_context);
if (FAILED(hr)) { hresult_set_exc("Failed to queue bulk property retrieval", hr); goto end; }
hr = bulk_properties->Start(guid_context); hr = bulk_properties->Start(guid_context);
if (FAILED(hr)) { hresult_set_exc("Failed to start bulk operation", hr); goto end; } if (FAILED(hr)) {
bulk_properties_callback->end_processing();
delete bulk_properties_callback;
hresult_set_exc("Failed to start bulk operation", hr);
return false;
}
while (!PyErr_Occurred()) {
DWORD wait_result = bulk_properties_callback->wait_for_messages();
callback->thread_state = PyEval_SaveThread();
while (TRUE) {
wait_result = MsgWaitForMultipleObjects(1, &(callback->complete), FALSE, 60000, QS_ALLEVENTS);
if (wait_result == WAIT_OBJECT_0) { if (wait_result == WAIT_OBJECT_0) {
break; // Event was signalled, bulk operation complete break; // Event was signalled, bulk operation complete
} else if (wait_result == WAIT_OBJECT_0 + 1) { // Messages need to be dispatched } else if (wait_result == WAIT_OBJECT_0 + 1) { // Messages need to be dispatched
pump_result = pump_waiting_messages(); int pump_result = bulk_properties_callback->pump_waiting_messages();
if (pump_result == 1) { PyErr_SetString(PyExc_RuntimeError, "Application has been asked to quit."); ok = false; break;} if (pump_result == 1) PyErr_SetString(PyExc_RuntimeError, "Application has been asked to quit.");
} else if (wait_result == WAIT_TIMEOUT) { } else if (wait_result == WAIT_TIMEOUT) {
// 60 seconds with no updates, looks bad // 60 seconds with no updates, looks bad
PyErr_SetString(WPDError, "The device seems to have hung."); ok = false; break; PyErr_SetString(WPDError, "The device seems to have hung.");
} else if (wait_result == WAIT_ABANDONED_0) { } else if (wait_result == WAIT_ABANDONED_0) {
// This should never happen // This should never happen
PyErr_SetString(WPDError, "An unknown error occurred (mutex abandoned)"); ok = false; break; PyErr_SetString(WPDError, "An unknown error occurred (mutex abandoned)");
} else { } else {
// The wait failed for some reason // The wait failed for some reason
PyErr_SetFromWindowsErr(0); ok = FALSE; break; PyErr_SetFromWindowsErr(0);
} }
} }
PyEval_RestoreThread(callback->thread_state); bulk_properties_callback->end_processing();
if (!ok) { if (PyErr_Occurred()) {
bulk_properties->Cancel(guid_context); bulk_properties->Cancel(guid_context);
pump_waiting_messages(); bulk_properties_callback->pump_waiting_messages();
} }
end: return PyErr_Occurred() ? false : true;
if (ev != NULL) CloseHandle(ev);
if (properties != NULL) properties->Release();
if (callback != NULL) callback->Release();
return ok;
} }
// }}} // }}}
// find_objects_in() {{{ // find_objects_in() {{{
static bool find_objects_in(IPortableDeviceContent *content, IPortableDevicePropVariantCollection *object_ids, const wchar_t *parent_id) { static bool
find_objects_in(CComPtr<IPortableDeviceContent> &content, CComPtr<IPortableDevicePropVariantCollection> &object_ids, const wchar_t *parent_id) {
/* /*
* Find all children of the object identified by parent_id. * Find all children of the object identified by parent_id.
* The child ids are put into object_ids. Returns False if any errors * The child ids are put into object_ids. Returns False if any errors
@ -348,54 +386,47 @@ get_object_properties(IPortableDeviceProperties *devprops, IPortableDeviceKeyCol
return ans; return ans;
} }
static bool single_get_filesystem(unsigned int level, IPortableDeviceContent *content, IPortableDevicePropVariantCollection *object_ids, PyObject *callback, PyObject *ans, PyObject *subfolders) { static bool
DWORD num, i; single_get_filesystem(unsigned int level, CComPtr<IPortableDeviceContent> &content, CComPtr<IPortableDevicePropVariantCollection> &object_ids, PyObject *callback, PyObject *ans, PyObject *subfolders) {
DWORD num;
PROPVARIANT pv; PROPVARIANT pv;
HRESULT hr; HRESULT hr;
bool ok = true; CComPtr<IPortableDeviceProperties> devprops;
PyObject *item = NULL, *r = NULL, *recurse = NULL;
IPortableDeviceProperties *devprops = NULL;
IPortableDeviceKeyCollection *properties = NULL;
hr = content->Properties(&devprops); hr = content->Properties(&devprops);
if (FAILED(hr)) { hresult_set_exc("Failed to get IPortableDeviceProperties interface", hr); goto end; } if (FAILED(hr)) { hresult_set_exc("Failed to get IPortableDeviceProperties interface", hr); return false; }
properties = create_filesystem_properties_collection(); CComPtr<IPortableDeviceKeyCollection> properties(create_filesystem_properties_collection());
if (properties == NULL) goto end; if (!properties) return false;
hr = object_ids->GetCount(&num); hr = object_ids->GetCount(&num);
if (FAILED(hr)) { hresult_set_exc("Failed to get object id count", hr); goto end; } if (FAILED(hr)) { hresult_set_exc("Failed to get object id count", hr); return false; }
for (i = 0; i < num; i++) { for (DWORD i = 0; i < num; i++) {
ok = false; bool ok = false;
recurse = NULL;
PropVariantInit(&pv); PropVariantInit(&pv);
hr = object_ids->GetAt(i, &pv); hr = object_ids->GetAt(i, &pv);
pyobject_raii recurse;
if (SUCCEEDED(hr) && pv.pwszVal != NULL) { if (SUCCEEDED(hr) && pv.pwszVal != NULL) {
item = get_object_properties(devprops, properties, pv.pwszVal); pyobject_raii item(get_object_properties(devprops, properties, pv.pwszVal));
if (item != NULL) { if (item) {
r = PyObject_CallFunction(callback, "OI", item, level); PyObject_Print(item.ptr(), stdout, 0);
if (r != NULL && PyObject_IsTrue(r)) recurse = item; printf("\n");
Py_XDECREF(r); pyobject_raii r(PyObject_CallFunction(callback, "OI", item.ptr(), level));
PyDict_SetItem(ans, PyDict_GetItemString(item, "id"), item); PyDict_SetItem(ans, PyDict_GetItemString(item.ptr(), "id"), item.ptr());
Py_DECREF(item); item = NULL; if (r && PyObject_IsTrue(r.ptr())) recurse.attach(item.detach());
ok = true; ok = true;
} }
} else hresult_set_exc("Failed to get item from IPortableDevicePropVariantCollection", hr); } else hresult_set_exc("Failed to get item from IPortableDevicePropVariantCollection", hr);
PropVariantClear(&pv); PropVariantClear(&pv);
if (!ok) break; if (!ok) return false;
if (recurse != NULL) { if (recurse) {
if (PyList_Append(subfolders, PyDict_GetItemString(recurse, "id")) == -1) ok = false; if (PyList_Append(subfolders, PyDict_GetItemString(recurse.ptr(), "id")) == -1) ok = false;
} }
if (!ok) break; if (!ok) return false;
} }
return true;
end:
if (devprops != NULL) devprops->Release();
if (properties != NULL) properties->Release();
return ok;
} }
// }}} // }}}
@ -449,60 +480,50 @@ end:
return values; return values;
} // }}} } // }}}
static bool get_files_and_folders(unsigned int level, IPortableDevice *device, IPortableDeviceContent *content, IPortableDevicePropertiesBulk *bulk_properties, const wchar_t *parent_id, PyObject *callback, PyObject *ans) { // {{{ static bool
bool ok = true; get_files_and_folders(unsigned int level, IPortableDevice *device, CComPtr<IPortableDeviceContent> &content, IPortableDevicePropertiesBulk *bulk_properties, const wchar_t *parent_id, PyObject *callback, PyObject *ans) { // {{{
IPortableDevicePropVariantCollection *object_ids = NULL; CComPtr<IPortableDevicePropVariantCollection> object_ids;
PyObject *subfolders = NULL;
HRESULT hr; HRESULT hr;
subfolders = PyList_New(0); pyobject_raii subfolders(PyList_New(0));
if (subfolders == NULL) { ok = false; goto end; } if (!subfolders) return false;
Py_BEGIN_ALLOW_THREADS; Py_BEGIN_ALLOW_THREADS;
hr = CoCreateInstance(CLSID_PortableDevicePropVariantCollection, NULL, hr = object_ids.CoCreateInstance(CLSID_PortableDevicePropVariantCollection, NULL, CLSCTX_INPROC_SERVER);
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&object_ids));
Py_END_ALLOW_THREADS; Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to create propvariantcollection", hr); ok = false; goto end; } if (FAILED(hr)) { hresult_set_exc("Failed to create propvariantcollection", hr); return false; }
ok = find_objects_in(content, object_ids, parent_id); if (!find_objects_in(content, object_ids, parent_id)) return false;
if (!ok) goto end;
if (bulk_properties != NULL) ok = bulk_get_filesystem(level, device, bulk_properties, object_ids, callback, ans, subfolders); if (bulk_properties != NULL) {
else ok = single_get_filesystem(level, content, object_ids, callback, ans, subfolders); if (!bulk_get_filesystem(level, device, bulk_properties, object_ids, callback, ans, subfolders.ptr())) return false;
if (!ok) goto end; } else {
if (!single_get_filesystem(level, content, object_ids, callback, ans, subfolders.ptr())) return false;
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(subfolders); i++) {
wchar_raii child_id(PyList_GET_ITEM(subfolders, i));
if (!child_id) { PyErr_Clear(); ok = false; break; }
ok = get_files_and_folders(level+1, device, content, bulk_properties, child_id.ptr(), callback, ans);
if (!ok) break;
} }
end:
if (object_ids != NULL) object_ids->Release(); for (Py_ssize_t i = 0; i < PyList_GET_SIZE(subfolders.ptr()); i++) {
Py_XDECREF(subfolders); wchar_raii child_id(PyList_GET_ITEM(subfolders.ptr(), i));
return ok; if (!child_id) return false;
if (!get_files_and_folders(level+1, device, content, bulk_properties, child_id.ptr(), callback, ans)) return false;
}
return true;
} // }}} } // }}}
PyObject* wpd::get_filesystem(IPortableDevice *device, const wchar_t *storage_id, IPortableDevicePropertiesBulk *bulk_properties, PyObject *callback) { // {{{ PyObject*
PyObject *ans = NULL; wpd::get_filesystem(IPortableDevice *device, const wchar_t *storage_id, IPortableDevicePropertiesBulk *bulk_properties, PyObject *callback) { // {{{
IPortableDeviceContent *content = NULL; CComPtr<IPortableDeviceContent> content;
HRESULT hr; HRESULT hr;
ans = PyDict_New(); pyobject_raii ans(PyDict_New());
if (ans == NULL) return PyErr_NoMemory(); if (!ans) 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 create content interface", hr); goto end; } if (FAILED(hr)) { hresult_set_exc("Failed to create content interface", hr); return NULL; }
if (!get_files_and_folders(0, device, content, bulk_properties, storage_id, callback, ans)) { if (!get_files_and_folders(0, device, content, bulk_properties, storage_id, callback, ans.ptr())) return NULL;
Py_DECREF(ans); ans = NULL; return ans.detach();
}
end:
if (content != NULL) content->Release();
return ans;
} // }}} } // }}}
PyObject* wpd::get_file(IPortableDevice *device, const wchar_t *object_id, PyObject *dest, PyObject *callback) { // {{{ PyObject* wpd::get_file(IPortableDevice *device, const wchar_t *object_id, PyObject *dest, PyObject *callback) { // {{{