Start adding RAII to content_enumeration.cpp

This commit is contained in:
Kovid Goyal 2021-04-21 14:00:07 +05:30
parent b4a1b26145
commit 76d6ce3fd2
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
7 changed files with 141 additions and 181 deletions

View File

@ -162,7 +162,7 @@
{
"name": "wpd",
"only": "windows",
"sources": "calibre/devices/mtp/windows/utils.cpp calibre/devices/mtp/windows/device_enumeration.cpp calibre/devices/mtp/windows/content_enumeration.cpp calibre/devices/mtp/windows/device.cpp calibre/devices/mtp/windows/wpd.cpp",
"sources": "calibre/devices/mtp/windows/device_enumeration.cpp calibre/devices/mtp/windows/content_enumeration.cpp calibre/devices/mtp/windows/device.cpp calibre/devices/mtp/windows/wpd.cpp",
"headers": "calibre/utils/cpp_binding.h calibre/devices/mtp/windows/global.h calibre/utils/windows/common.h",
"libraries": "ole32 oleaut32 portabledeviceguids user32",
"cflags": "/X"

View File

@ -12,6 +12,25 @@
#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 {
static int
pump_waiting_messages() {
UINT firstMsg = 0, lastMsg = 0;
MSG msg;
int result = 0;
// Read all of the messages in this next loop,
// removing each message as we read it.
while (PeekMessage(&msg, NULL, firstMsg, lastMsg, PM_REMOVE)) {
// If it's a quit message, we're out of here.
if (msg.message == WM_QUIT) {
result = 1;
break;
}
// Otherwise, dispatch the message.
DispatchMessage(&msg);
} // End of PeekMessage while loop
return result;
}
static IPortableDeviceKeyCollection* create_filesystem_properties_collection() { // {{{
IPortableDeviceKeyCollection *properties = NULL;
@ -42,49 +61,43 @@ static IPortableDeviceKeyCollection* create_filesystem_properties_collection() {
} // }}}
// Convert properties from COM to python {{{
static void set_string_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, IPortableDeviceValues *properties) {
static void
set_string_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, CComPtr<IPortableDeviceValues> &properties) {
HRESULT hr;
wchar_t *property = NULL;
PyObject *val;
hr = properties->GetStringValue(key, &property);
com_wchar_raii property;
hr = properties->GetStringValue(key, property.unsafe_address());
if (SUCCEEDED(hr)) {
val = wchar_to_unicode(property);
if (val != NULL) {
PyDict_SetItemString(dict, pykey, val);
Py_DECREF(val);
}
CoTaskMemFree(property);
}
}
static void set_bool_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, IPortableDeviceValues *properties) {
BOOL ok = 0;
HRESULT hr;
hr = properties->GetBoolValue(key, &ok);
if (SUCCEEDED(hr))
PyDict_SetItemString(dict, pykey, (ok)?Py_True:Py_False);
}
static void set_size_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, IPortableDeviceValues *properties) {
ULONGLONG val = 0;
HRESULT hr;
PyObject *pval;
hr = properties->GetUnsignedLargeIntegerValue(key, &val);
if (SUCCEEDED(hr)) {
pval = PyLong_FromUnsignedLongLong(val);
if (pval != NULL) {
PyDict_SetItemString(dict, pykey, pval);
Py_DECREF(pval);
}
pyobject_raii val(PyUnicode_FromWideChar(property.ptr(), -1));
if (val) if (PyDict_SetItemString(dict, pykey, val.ptr()) != 0) PyErr_Clear();
}
}
static void
set_date_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, IPortableDeviceValues *properties) {
set_bool_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, CComPtr<IPortableDeviceValues> &properties) {
BOOL ok = 0;
HRESULT hr;
hr = properties->GetBoolValue(key, &ok);
if (SUCCEEDED(hr)) {
if (PyDict_SetItemString(dict, pykey, (ok)?Py_True:Py_False) != 0) PyErr_Clear();
}
}
static void
set_size_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, CComPtr<IPortableDeviceValues> &properties) {
ULONGLONG val = 0;
HRESULT hr;
hr = properties->GetUnsignedLargeIntegerValue(key, &val);
if (SUCCEEDED(hr)) {
pyobject_raii pval(PyLong_FromUnsignedLongLong(val));
if (pval) {
if (PyDict_SetItemString(dict, pykey, pval.ptr()) != 0) PyErr_Clear();
} else PyErr_Clear();
}
}
static void
set_date_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, CComPtr<IPortableDeviceValues> &properties) {
PROPVARIANT ts = {0};
if (SUCCEEDED(properties->GetValue(key, &ts))) {
SYSTEMTIME st;
@ -99,15 +112,17 @@ set_date_property(PyObject *dict, REFPROPERTYKEY key, const char *pykey, IPortab
}
}
static void set_content_type_property(PyObject *dict, IPortableDeviceValues *properties) {
static void
set_content_type_property(PyObject *dict, CComPtr<IPortableDeviceValues> &properties) {
GUID guid = GUID_NULL;
BOOL is_folder = 0;
if (SUCCEEDED(properties->GetGuidValue(WPD_OBJECT_CONTENT_TYPE, &guid)) && IsEqualGUID(guid, WPD_CONTENT_TYPE_FOLDER)) is_folder = 1;
PyDict_SetItemString(dict, "is_folder", (is_folder) ? Py_True : Py_False);
if (PyDict_SetItemString(dict, "is_folder", (is_folder) ? Py_True : Py_False) != 0) PyErr_Clear();
}
static void set_properties(PyObject *obj, IPortableDeviceValues *values) {
static void
set_properties(PyObject *obj, CComPtr<IPortableDeviceValues> &values) {
set_content_type_property(obj, values);
set_string_property(obj, WPD_OBJECT_PARENT_ID, "parent_id", values);
@ -171,39 +186,32 @@ public:
HRESULT __stdcall OnProgress(REFGUID Context, IPortableDeviceValuesCollection* values) {
DWORD num = 0, i;
wchar_t *property = NULL;
IPortableDeviceValues *properties = NULL;
PyObject *temp, *obj, *r;
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)) {
hr = properties->GetStringValue(WPD_OBJECT_ID, &property);
com_wchar_raii property;
hr = properties->GetStringValue(WPD_OBJECT_ID, property.unsafe_address());
if (!SUCCEEDED(hr)) continue;
temp = wchar_to_unicode(property);
CoTaskMemFree(property); property = NULL;
if (temp == NULL) continue;
obj = PyDict_GetItem(this->items, temp);
if (obj == NULL) {
obj = Py_BuildValue("{s:O}", "id", temp);
if (obj == NULL) continue;
PyDict_SetItem(this->items, temp, obj);
Py_DECREF(obj); // We want a borrowed reference to obj
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();
}
Py_DECREF(temp);
set_properties(obj, properties);
r = PyObject_CallFunction(callback, "OI", obj, this->level);
if (r != NULL && PyObject_IsTrue(r)) {
PyList_Append(this->subfolders, PyDict_GetItemString(obj, "id"));
}
Py_XDECREF(r);
properties->Release(); properties = NULL;
}
} // end for loop
this->thread_state = PyEval_SaveThread();
@ -322,23 +330,21 @@ end:
// Single get filesystem {{{
static PyObject* get_object_properties(IPortableDeviceProperties *devprops, IPortableDeviceKeyCollection *properties, const wchar_t *object_id) {
IPortableDeviceValues *values = NULL;
static PyObject*
get_object_properties(IPortableDeviceProperties *devprops, IPortableDeviceKeyCollection *properties, const wchar_t *object_id) {
CComPtr<IPortableDeviceValues> values;
HRESULT hr;
PyObject *ans = NULL, *temp = NULL;
Py_BEGIN_ALLOW_THREADS;
hr = devprops->GetValues(object_id, properties, &values);
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to get properties for object", hr); goto end; }
if (FAILED(hr)) { hresult_set_exc("Failed to get properties for object", hr); return NULL; }
ans = Py_BuildValue("{s:N}", "id", wchar_to_unicode(object_id));
if (ans == NULL) goto end;
pyobject_raii id(PyUnicode_FromWideChar(object_id, -1));
if (!id) return NULL;
PyObject *ans = Py_BuildValue("{s:O}", "id", id.ptr());
if (ans == NULL) return NULL;
set_properties(ans, values);
end:
Py_XDECREF(temp);
if (values != NULL) values->Release();
return ans;
}
@ -466,9 +472,9 @@ static bool get_files_and_folders(unsigned int level, IPortableDevice *device, I
if (!ok) goto end;
for (Py_ssize_t i = 0; i < PyList_GET_SIZE(subfolders); i++) {
const wchar_t *child_id = unicode_to_wchar(PyList_GET_ITEM(subfolders, i));
if (child_id == NULL) { ok = false; break; }
ok = get_files_and_folders(level+1, device, content, bulk_properties, child_id, callback, ans);
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:

View File

@ -9,42 +9,6 @@
namespace wpd {
IPortableDeviceValues *get_client_information() { // {{{
HRESULT hr;
ENSURE_WPD(NULL);
CComPtr<IPortableDeviceValues> client_information;
Py_BEGIN_ALLOW_THREADS;
hr = client_information.CoCreateInstance(CLSID_PortableDeviceValues, NULL, CLSCTX_INPROC_SERVER);
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to create IPortableDeviceValues", hr); return NULL; }
Py_BEGIN_ALLOW_THREADS;
hr = client_information->SetStringValue(WPD_CLIENT_NAME, client_info.name.ptr());
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to set client name", hr); return NULL; }
Py_BEGIN_ALLOW_THREADS;
hr = client_information->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, client_info.major_version);
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to set major version", hr); return NULL; }
Py_BEGIN_ALLOW_THREADS;
hr = client_information->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, client_info.minor_version);
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to set minor version", hr); return NULL; }
Py_BEGIN_ALLOW_THREADS;
hr = client_information->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, client_info.revision);
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to set revision", hr); return NULL; }
// Some device drivers need to impersonate the caller in order to function correctly. Since our application does not
// need to restrict its identity, specify SECURITY_IMPERSONATION so that we work with all devices.
Py_BEGIN_ALLOW_THREADS;
hr = client_information->SetUnsignedIntegerValue(WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, SECURITY_IMPERSONATION);
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to set quality of service", hr); return NULL; }
return client_information.Detach();
} // }}}
IPortableDevice*
open_device(const wchar_t *pnp_id, CComPtr<IPortableDeviceValues> &client_information) { // {{{
CComPtr<IPortableDevice> device;
@ -151,7 +115,7 @@ get_storage_info(IPortableDevice *device) { // {{{
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()))) { \
#define A(which, key) { com_wchar_raii buf; if (SUCCEEDED(values->GetStringValue(which, buf.unsafe_address()))) { \
pyobject_raii d(PyUnicode_FromWideChar(buf.ptr(), -1)); \
if (d) PyDict_SetItemString(so.ptr(), key, d.ptr()); \
else PyErr_Clear(); \

View File

@ -27,15 +27,6 @@ extern PyObject *WPDError, *NoWPD, *WPDFileBusy;
// The global device manager
extern CComPtr<IPortableDeviceManager> portable_device_manager;
// Application info
typedef struct {
wchar_raii name;
unsigned int major_version;
unsigned int minor_version;
unsigned int revision;
} ClientInfo;
extern ClientInfo client_info;
// Device type
typedef struct {
PyObject_HEAD
@ -49,10 +40,6 @@ extern PyTypeObject DeviceType;
#define hresult_set_exc(msg, hr) set_error_from_hresult(wpd::WPDError, __FILE__, __LINE__, hr, msg)
wchar_t *unicode_to_wchar(PyObject *o);
PyObject *wchar_to_unicode(const wchar_t *o);
int pump_waiting_messages();
extern IPortableDeviceValues* get_client_information();
extern IPortableDevice* open_device(const wchar_t *pnp_id, CComPtr<IPortableDeviceValues> &client_information);
extern PyObject* get_device_information(CComPtr<IPortableDevice> &device, CComPtr<IPortableDevicePropertiesBulk> &bulk_properties);

View File

@ -1,50 +0,0 @@
/*
* utils.cpp
* Copyright (C) 2012 Kovid Goyal <kovid at kovidgoyal.net>
*
* Distributed under terms of the GPL3 license.
*/
#include "global.h"
using namespace wpd;
wchar_t *wpd::unicode_to_wchar(PyObject *o) {
wchar_t *buf;
Py_ssize_t len;
if (o == NULL) return NULL;
if (!PyUnicode_Check(o)) {PyErr_Format(PyExc_TypeError, "The python object must be a unicode object"); return NULL;}
len = PyUnicode_GET_SIZE(o);
buf = (wchar_t *)calloc(len+2, sizeof(wchar_t));
if (buf == NULL) { PyErr_NoMemory(); return NULL; }
len = PyUnicode_AsWideChar(o, buf, len);
if (len == -1) { free(buf); PyErr_Format(PyExc_TypeError, "Invalid python unicode object."); return NULL; }
return buf;
}
PyObject *wpd::wchar_to_unicode(const wchar_t *o) {
PyObject *ans;
if (o == NULL) return NULL;
ans = PyUnicode_FromWideChar(o, -1);
if (ans == NULL) PyErr_NoMemory();
return ans;
}
int wpd::pump_waiting_messages() {
UINT firstMsg = 0, lastMsg = 0;
MSG msg;
int result = 0;
// Read all of the messages in this next loop,
// removing each message as we read it.
while (PeekMessage(&msg, NULL, firstMsg, lastMsg, PM_REMOVE)) {
// If it's a quit message, we're out of here.
if (msg.message == WM_QUIT) {
result = 1;
break;
}
// Otherwise, dispatch the message.
DispatchMessage(&msg);
} // End of PeekMessage while loop
return result;
}

View File

@ -18,7 +18,51 @@ CComPtr<IPortableDeviceManager> wpd::portable_device_manager = NULL;
// Flag indicating if COM has been initialized
static int _com_initialized = 0;
// Application Info
wpd::ClientInfo wpd::client_info;
class ClientInfo {
public:
wchar_raii name;
unsigned int major_version;
unsigned int minor_version;
unsigned int revision;
ClientInfo() : name(), major_version(0), minor_version(0), revision(0) {}
};
static ClientInfo client_info;
IPortableDeviceValues* wpd::get_client_information() { // {{{
HRESULT hr;
ENSURE_WPD(NULL);
CComPtr<IPortableDeviceValues> client_information;
Py_BEGIN_ALLOW_THREADS;
hr = client_information.CoCreateInstance(CLSID_PortableDeviceValues, NULL, CLSCTX_INPROC_SERVER);
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to create IPortableDeviceValues", hr); return NULL; }
Py_BEGIN_ALLOW_THREADS;
hr = client_information->SetStringValue(WPD_CLIENT_NAME, client_info.name.ptr());
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to set client name", hr); return NULL; }
Py_BEGIN_ALLOW_THREADS;
hr = client_information->SetUnsignedIntegerValue(WPD_CLIENT_MAJOR_VERSION, client_info.major_version);
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to set major version", hr); return NULL; }
Py_BEGIN_ALLOW_THREADS;
hr = client_information->SetUnsignedIntegerValue(WPD_CLIENT_MINOR_VERSION, client_info.minor_version);
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to set minor version", hr); return NULL; }
Py_BEGIN_ALLOW_THREADS;
hr = client_information->SetUnsignedIntegerValue(WPD_CLIENT_REVISION, client_info.revision);
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to set revision", hr); return NULL; }
// Some device drivers need to impersonate the caller in order to function correctly. Since our application does not
// need to restrict its identity, specify SECURITY_IMPERSONATION so that we work with all devices.
Py_BEGIN_ALLOW_THREADS;
hr = client_information->SetUnsignedIntegerValue(WPD_CLIENT_SECURITY_QUALITY_OF_SERVICE, SECURITY_IMPERSONATION);
Py_END_ALLOW_THREADS;
if (FAILED(hr)) { hresult_set_exc("Failed to set quality of service", hr); return NULL; }
return client_information.Detach();
} // }}}
// Module startup/shutdown {{{
static PyObject *

View File

@ -17,10 +17,12 @@
template<typename T, void free_T(void*), T null=reinterpret_cast<T>(NULL)>
class generic_raii {
private:
T handle;
generic_raii( const generic_raii & ) noexcept;
generic_raii & operator=( const generic_raii & ) noexcept ;
protected:
T handle;
public:
explicit generic_raii(T h = null) noexcept : handle(h) {}
~generic_raii() noexcept { release(); }
@ -39,7 +41,14 @@ class generic_raii {
explicit operator bool() const noexcept { return handle != null; }
};
typedef generic_raii<wchar_t*, PyMem_Free> wchar_raii;
class wchar_raii : public generic_raii<wchar_t*, PyMem_Free> {
public:
explicit wchar_raii() noexcept {}
explicit wchar_raii(PyObject *unicode_object) noexcept {
if (!unicode_object || !PyUnicode_Check(unicode_object)) { PyErr_SetString(PyExc_TypeError, "Not a unicode object"); return; }
handle = PyUnicode_AsWideCharString(unicode_object, NULL);
}
};
static inline void python_object_destructor(void *p) { PyObject *x = reinterpret_cast<PyObject*>(p); Py_XDECREF(x); }
typedef generic_raii<PyObject*, python_object_destructor> pyobject_raii;