diff --git a/setup/extensions.py b/setup/extensions.py index bc6e41089e..44a07a34c6 100644 --- a/setup/extensions.py +++ b/setup/extensions.py @@ -169,7 +169,14 @@ if iswindows: cflags=['/X'] ), Extension('wpd', - ['calibre/devices/mtp/windows/wpd.cpp'], + [ + 'calibre/devices/mtp/windows/utils.cpp', + 'calibre/devices/mtp/windows/device_enumeration.cpp', + 'calibre/devices/mtp/windows/wpd.cpp', + ], + headers=[ + 'calibre/devices/mtp/windows/global.h', + ], libraries=['ole32', 'portabledeviceguids'], # needs_ddk=True, cflags=['/X'] diff --git a/src/calibre/devices/mtp/windows/device_enumeration.cpp b/src/calibre/devices/mtp/windows/device_enumeration.cpp new file mode 100644 index 0000000000..9b586c3850 --- /dev/null +++ b/src/calibre/devices/mtp/windows/device_enumeration.cpp @@ -0,0 +1,195 @@ +/* + * device_enumeration.cpp + * Copyright (C) 2012 Kovid Goyal + * + * Distributed under terms of the MIT license. + */ + +#include "global.h" + +namespace wpd { + +IPortableDeviceValues *get_client_information() { // {{{ + IPortableDeviceValues *client_information; + HRESULT hr; + + ENSURE_WPD(NULL); + + Py_BEGIN_ALLOW_THREADS; + hr = CoCreateInstance(CLSID_PortableDeviceValues, NULL, + CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&client_information)); + 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); + 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; +} // }}} + +IPortableDevice *open_device(const wchar_t *pnp_id, IPortableDeviceValues *client_information) { // {{{ + IPortableDevice *device = NULL; + HRESULT hr; + + Py_BEGIN_ALLOW_THREADS; + hr = CoCreateInstance(CLSID_PortableDevice, NULL, CLSCTX_INPROC_SERVER, + IID_PPV_ARGS(&device)); + Py_END_ALLOW_THREADS; + if (FAILED(hr)) hresult_set_exc("Failed to create IPortableDevice", hr); + else { + Py_BEGIN_ALLOW_THREADS; + hr = device->Open(pnp_id, client_information); + Py_END_ALLOW_THREADS; + if FAILED(hr) { + Py_BEGIN_ALLOW_THREADS; + 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; + +} // }}} + +PyObject* get_device_information(IPortableDevice *device) { // {{{ + IPortableDeviceContent *content = NULL; + IPortableDeviceProperties *properties = NULL; + IPortableDeviceKeyCollection *keys = NULL; + IPortableDeviceValues *values = NULL; + HRESULT hr; + LPWSTR temp; + ULONG ti; + PyObject *t, *ans = NULL; + char *type; + + Py_BEGIN_ALLOW_THREADS; + hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, NULL, + CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&keys)); + Py_END_ALLOW_THREADS; + if (FAILED(hr)) {hresult_set_exc("Failed to create IPortableDeviceKeyCollection", hr); goto end;} + + Py_BEGIN_ALLOW_THREADS; + hr = keys->Add(WPD_DEVICE_PROTOCOL); + // Despite the MSDN documentation, this does not exist in PortableDevice.h + // hr = keys->Add(WPD_DEVICE_TRANSPORT); + hr = keys->Add(WPD_DEVICE_FRIENDLY_NAME); + hr = keys->Add(WPD_DEVICE_MANUFACTURER); + hr = keys->Add(WPD_DEVICE_MODEL); + hr = keys->Add(WPD_DEVICE_SERIAL_NUMBER); + hr = keys->Add(WPD_DEVICE_FIRMWARE_VERSION); + hr = keys->Add(WPD_DEVICE_TYPE); + Py_END_ALLOW_THREADS; + if (FAILED(hr)) {hresult_set_exc("Failed to add keys to IPortableDeviceKeyCollection", hr); goto end;} + + Py_BEGIN_ALLOW_THREADS; + hr = device->Content(&content); + Py_END_ALLOW_THREADS; + if (FAILED(hr)) {hresult_set_exc("Failed to get IPortableDeviceContent", hr); goto end; } + + Py_BEGIN_ALLOW_THREADS; + hr = content->Properties(&properties); + Py_END_ALLOW_THREADS; + if (FAILED(hr)) {hresult_set_exc("Failed to get IPortableDeviceProperties", hr); goto end; } + + Py_BEGIN_ALLOW_THREADS; + hr = properties->GetValues(WPD_DEVICE_OBJECT_ID, keys, &values); + Py_END_ALLOW_THREADS; + if(FAILED(hr)) {hresult_set_exc("Failed to get device info", hr); goto end; } + + ans = PyDict_New(); + if (ans == NULL) {PyErr_NoMemory(); goto end;} + + if (SUCCEEDED(values->GetStringValue(WPD_DEVICE_PROTOCOL, &temp))) { + t = PyUnicode_FromWideChar(temp, wcslen(temp)); + if (t != NULL) {PyDict_SetItemString(ans, "protocol", t); Py_DECREF(t);} + CoTaskMemFree(temp); + } + + // if (SUCCEEDED(values->GetUnsignedIntegerValue(WPD_DEVICE_TRANSPORT, &ti))) { + // PyDict_SetItemString(ans, "isusb", (ti == WPD_DEVICE_TRANSPORT_USB) ? Py_True : Py_False); + // t = PyLong_FromUnsignedLong(ti); + // } + + if (SUCCEEDED(values->GetUnsignedIntegerValue(WPD_DEVICE_TYPE, &ti))) { + switch (ti) { + case WPD_DEVICE_TYPE_CAMERA: + type = "camera"; break; + case WPD_DEVICE_TYPE_MEDIA_PLAYER: + type = "media player"; break; + case WPD_DEVICE_TYPE_PHONE: + type = "phone"; break; + case WPD_DEVICE_TYPE_VIDEO: + type = "video"; break; + case WPD_DEVICE_TYPE_PERSONAL_INFORMATION_MANAGER: + type = "personal information manager"; break; + case WPD_DEVICE_TYPE_AUDIO_RECORDER: + type = "audio recorder"; break; + default: + type = "unknown"; + } + t = PyString_FromString(type); + if (t != NULL) { + PyDict_SetItemString(ans, "type", t); Py_DECREF(t); + } + } + + if (SUCCEEDED(values->GetStringValue(WPD_DEVICE_FRIENDLY_NAME, &temp))) { + t = PyUnicode_FromWideChar(temp, wcslen(temp)); + if (t != NULL) {PyDict_SetItemString(ans, "friendly_name", t); Py_DECREF(t);} + CoTaskMemFree(temp); + } + + if (SUCCEEDED(values->GetStringValue(WPD_DEVICE_MANUFACTURER, &temp))) { + t = PyUnicode_FromWideChar(temp, wcslen(temp)); + if (t != NULL) {PyDict_SetItemString(ans, "manufacturer_name", t); Py_DECREF(t);} + CoTaskMemFree(temp); + } + + if (SUCCEEDED(values->GetStringValue(WPD_DEVICE_MODEL, &temp))) { + t = PyUnicode_FromWideChar(temp, wcslen(temp)); + if (t != NULL) {PyDict_SetItemString(ans, "model_name", t); Py_DECREF(t);} + CoTaskMemFree(temp); + } + + if (SUCCEEDED(values->GetStringValue(WPD_DEVICE_SERIAL_NUMBER, &temp))) { + t = PyUnicode_FromWideChar(temp, wcslen(temp)); + if (t != NULL) {PyDict_SetItemString(ans, "serial_number", t); Py_DECREF(t);} + CoTaskMemFree(temp); + } + + if (SUCCEEDED(values->GetStringValue(WPD_DEVICE_FIRMWARE_VERSION, &temp))) { + t = PyUnicode_FromWideChar(temp, wcslen(temp)); + if (t != NULL) {PyDict_SetItemString(ans, "device_version", t); Py_DECREF(t);} + CoTaskMemFree(temp); + } + +end: + if (keys != NULL) keys->Release(); + if (values != NULL) values->Release(); + if (properties != NULL) properties->Release(); + if (content != NULL) content->Release(); + return ans; +} // }}} + +} // namespace wpd diff --git a/src/calibre/devices/mtp/windows/global.h b/src/calibre/devices/mtp/windows/global.h new file mode 100644 index 0000000000..65a3d53718 --- /dev/null +++ b/src/calibre/devices/mtp/windows/global.h @@ -0,0 +1,46 @@ +/* + * global.h + * Copyright (C) 2012 Kovid Goyal + * + * Distributed under terms of the MIT license. + */ + +#pragma once +#define UNICODE +#include +#include + +#include +#include +#include + +#define ENSURE_WPD(retval) \ + if (portable_device_manager == NULL) { PyErr_SetString(NoWPD, "No WPD service available."); return retval; } + +namespace wpd { + +// Module exception types +extern PyObject *WPDError, *NoWPD; + +// The global device manager +extern IPortableDeviceManager *portable_device_manager; + +// Application info +typedef struct { + wchar_t *name; + unsigned int major_version; + unsigned int minor_version; + unsigned int revision; +} ClientInfo; +extern ClientInfo client_info; + +// Utility functions +PyObject *hresult_set_exc(const char *msg, HRESULT hr); +wchar_t *unicode_to_wchar(PyObject *o); + +extern IPortableDeviceValues* get_client_information(); +extern IPortableDevice* open_device(const wchar_t *pnp_id, IPortableDeviceValues *client_information); +extern PyObject* get_device_information(IPortableDevice *device); + +} + diff --git a/src/calibre/devices/mtp/windows/utils.cpp b/src/calibre/devices/mtp/windows/utils.cpp new file mode 100644 index 0000000000..424dae0852 --- /dev/null +++ b/src/calibre/devices/mtp/windows/utils.cpp @@ -0,0 +1,45 @@ +/* + * utils.cpp + * Copyright (C) 2012 Kovid Goyal + * + * Distributed under terms of the MIT license. + */ + +#include "global.h" + +using namespace wpd; + +PyObject *wpd::hresult_set_exc(const char *msg, HRESULT hr) { + PyObject *o = NULL, *mess; + LPWSTR desc = NULL; + + FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&desc, 0, NULL); + if (desc == NULL) { + o = PyUnicode_FromString("No description available."); + } else { + o = PyUnicode_FromWideChar(desc, wcslen(desc)); + LocalFree(desc); + } + if (o == NULL) return PyErr_NoMemory(); + mess = PyUnicode_FromFormat("%s: hr=%lu facility=%u error_code=%u description: %U", msg, hr, HRESULT_FACILITY(hr), HRESULT_CODE(hr), o); + Py_XDECREF(o); + if (mess == NULL) return PyErr_NoMemory(); + PyErr_SetObject(WPDError, mess); + Py_DECREF(mess); + return NULL; +} + +wchar_t *wpd::unicode_to_wchar(PyObject *o) { + wchar_t *buf; + Py_ssize_t len; + if (!PyUnicode_Check(o)) {PyErr_Format(PyExc_TypeError, "The pnp id must be a unicode object"); return NULL;} + len = PyUnicode_GET_SIZE(o); + if (len < 1) {PyErr_Format(PyExc_TypeError, "The pnp id must not be empty."); return NULL;} + buf = (wchar_t *)calloc(len+2, sizeof(wchar_t)); + if (buf == NULL) { PyErr_NoMemory(); return NULL; } + len = PyUnicode_AsWideChar((PyUnicodeObject*)o, buf, len); + if (len == -1) { free(buf); PyErr_Format(PyExc_TypeError, "Invalid pnp id."); return NULL; } + return buf; +} + diff --git a/src/calibre/devices/mtp/windows/wpd.cpp b/src/calibre/devices/mtp/windows/wpd.cpp index 8c227b4a19..76ecea65b8 100644 --- a/src/calibre/devices/mtp/windows/wpd.cpp +++ b/src/calibre/devices/mtp/windows/wpd.cpp @@ -5,248 +5,24 @@ * Distributed under terms of the MIT license. */ +#include "global.h" -#define UNICODE -#include -#include +using namespace wpd; -#include -#include -#include +// Module exception types +PyObject *wpd::WPDError = NULL, *wpd::NoWPD = NULL; -// Utility functions {{{ -#define ENSURE_WPD(retval) \ - if (portable_device_manager == NULL) { PyErr_SetString(NoWPD, "No WPD service available."); return retval; } +// The global device manager +IPortableDeviceManager *wpd::portable_device_manager = NULL; -static PyObject *WPDError = NULL, *NoWPD = NULL; -static IPortableDeviceManager *portable_device_manager = NULL; +// Flag indicating if COM has been initialized static int _com_initialized = 0; -typedef struct { - wchar_t *name; - unsigned int major_version; - unsigned int minor_version; - unsigned int revision; -} ClientInfo; -static ClientInfo client_info = {NULL, 0, 0, 0}; +// Application Info +wpd::ClientInfo wpd::client_info = {NULL, 0, 0, 0}; -static PyObject *hresult_set_exc(const char *msg, HRESULT hr) { - PyObject *o = NULL, *mess; - LPWSTR desc = NULL; - - FormatMessageW(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, hr, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&desc, 0, NULL); - if (desc == NULL) { - o = PyUnicode_FromString("No description available."); - } else { - o = PyUnicode_FromWideChar(desc, wcslen(desc)); - LocalFree(desc); - } - if (o == NULL) return PyErr_NoMemory(); - mess = PyUnicode_FromFormat("%s: hr=%lu facility=%u error_code=%u description: %U", msg, hr, HRESULT_FACILITY(hr), HRESULT_CODE(hr), o); - Py_XDECREF(o); - if (mess == NULL) return PyErr_NoMemory(); - PyErr_SetObject(WPDError, mess); - Py_DECREF(mess); - return NULL; -} - -static wchar_t *unicode_to_wchar(PyObject *o) { - wchar_t *buf; - Py_ssize_t len; - if (!PyUnicode_Check(o)) {PyErr_Format(PyExc_TypeError, "The pnp id must be a unicode object"); return NULL;} - len = PyUnicode_GET_SIZE(o); - if (len < 1) {PyErr_Format(PyExc_TypeError, "The pnp id must not be empty."); return NULL;} - buf = (wchar_t *)calloc(len+2, sizeof(wchar_t)); - if (buf == NULL) { PyErr_NoMemory(); return NULL; } - len = PyUnicode_AsWideChar((PyUnicodeObject*)o, buf, len); - if (len == -1) { free(buf); PyErr_Format(PyExc_TypeError, "Invalid pnp id."); return NULL; } - return buf; -} - -static IPortableDeviceValues *get_client_information() { // {{{ - IPortableDeviceValues *client_information; - HRESULT hr; - - ENSURE_WPD(NULL); - - Py_BEGIN_ALLOW_THREADS; - hr = CoCreateInstance(CLSID_PortableDeviceValues, NULL, - CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&client_information)); - 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); - 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; -} // }}} - -static IPortableDevice *open_device(const wchar_t *pnp_id, IPortableDeviceValues *client_information) { // {{{ - IPortableDevice *device = NULL; - HRESULT hr; - - Py_BEGIN_ALLOW_THREADS; - hr = CoCreateInstance(CLSID_PortableDevice, NULL, CLSCTX_INPROC_SERVER, - IID_PPV_ARGS(&device)); - Py_END_ALLOW_THREADS; - if (FAILED(hr)) hresult_set_exc("Failed to create IPortableDevice", hr); - else { - Py_BEGIN_ALLOW_THREADS; - hr = device->Open(pnp_id, client_information); - Py_END_ALLOW_THREADS; - if FAILED(hr) { - Py_BEGIN_ALLOW_THREADS; - 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; - -} // }}} - -static PyObject* get_device_information(IPortableDevice *device) { // {{{ - IPortableDeviceContent *content = NULL; - IPortableDeviceProperties *properties = NULL; - IPortableDeviceKeyCollection *keys = NULL; - IPortableDeviceValues *values = NULL; - HRESULT hr; - LPWSTR temp; - ULONG ti; - PyObject *t, *ans = NULL; - char *type; - - Py_BEGIN_ALLOW_THREADS; - hr = CoCreateInstance(CLSID_PortableDeviceKeyCollection, NULL, - CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&keys)); - Py_END_ALLOW_THREADS; - if (FAILED(hr)) {hresult_set_exc("Failed to create IPortableDeviceKeyCollection", hr); goto end;} - - Py_BEGIN_ALLOW_THREADS; - hr = keys->Add(WPD_DEVICE_PROTOCOL); - // Despite the MSDN documentation, this does not exist in PortableDevice.h - // hr = keys->Add(WPD_DEVICE_TRANSPORT); - hr = keys->Add(WPD_DEVICE_FRIENDLY_NAME); - hr = keys->Add(WPD_DEVICE_MANUFACTURER); - hr = keys->Add(WPD_DEVICE_MODEL); - hr = keys->Add(WPD_DEVICE_SERIAL_NUMBER); - hr = keys->Add(WPD_DEVICE_FIRMWARE_VERSION); - hr = keys->Add(WPD_DEVICE_TYPE); - Py_END_ALLOW_THREADS; - if (FAILED(hr)) {hresult_set_exc("Failed to add keys to IPortableDeviceKeyCollection", hr); goto end;} - - Py_BEGIN_ALLOW_THREADS; - hr = device->Content(&content); - Py_END_ALLOW_THREADS; - if (FAILED(hr)) {hresult_set_exc("Failed to get IPortableDeviceContent", hr); goto end; } - - Py_BEGIN_ALLOW_THREADS; - hr = content->Properties(&properties); - Py_END_ALLOW_THREADS; - if (FAILED(hr)) {hresult_set_exc("Failed to get IPortableDeviceProperties", hr); goto end; } - - Py_BEGIN_ALLOW_THREADS; - hr = properties->GetValues(WPD_DEVICE_OBJECT_ID, keys, &values); - Py_END_ALLOW_THREADS; - if(FAILED(hr)) {hresult_set_exc("Failed to get device info", hr); goto end; } - - ans = PyDict_New(); - if (ans == NULL) {PyErr_NoMemory(); goto end;} - - if (SUCCEEDED(values->GetStringValue(WPD_DEVICE_PROTOCOL, &temp))) { - t = PyUnicode_FromWideChar(temp, wcslen(temp)); - if (t != NULL) {PyDict_SetItemString(ans, "protocol", t); Py_DECREF(t);} - CoTaskMemFree(temp); - } - - // if (SUCCEEDED(values->GetUnsignedIntegerValue(WPD_DEVICE_TRANSPORT, &ti))) { - // PyDict_SetItemString(ans, "isusb", (ti == WPD_DEVICE_TRANSPORT_USB) ? Py_True : Py_False); - // t = PyLong_FromUnsignedLong(ti); - // } - - if (SUCCEEDED(values->GetUnsignedIntegerValue(WPD_DEVICE_TYPE, &ti))) { - switch (ti) { - case WPD_DEVICE_TYPE_CAMERA: - type = "camera"; break; - case WPD_DEVICE_TYPE_MEDIA_PLAYER: - type = "media player"; break; - case WPD_DEVICE_TYPE_PHONE: - type = "phone"; break; - case WPD_DEVICE_TYPE_VIDEO: - type = "video"; break; - case WPD_DEVICE_TYPE_PERSONAL_INFORMATION_MANAGER: - type = "personal information manager"; break; - case WPD_DEVICE_TYPE_AUDIO_RECORDER: - type = "audio recorder"; break; - default: - type = "unknown"; - } - t = PyString_FromString(type); - if (t != NULL) { - PyDict_SetItemString(ans, "type", t); Py_DECREF(t); - } - } - - if (SUCCEEDED(values->GetStringValue(WPD_DEVICE_FRIENDLY_NAME, &temp))) { - t = PyUnicode_FromWideChar(temp, wcslen(temp)); - if (t != NULL) {PyDict_SetItemString(ans, "friendly_name", t); Py_DECREF(t);} - CoTaskMemFree(temp); - } - - if (SUCCEEDED(values->GetStringValue(WPD_DEVICE_MANUFACTURER, &temp))) { - t = PyUnicode_FromWideChar(temp, wcslen(temp)); - if (t != NULL) {PyDict_SetItemString(ans, "manufacturer_name", t); Py_DECREF(t);} - CoTaskMemFree(temp); - } - - if (SUCCEEDED(values->GetStringValue(WPD_DEVICE_MODEL, &temp))) { - t = PyUnicode_FromWideChar(temp, wcslen(temp)); - if (t != NULL) {PyDict_SetItemString(ans, "model_name", t); Py_DECREF(t);} - CoTaskMemFree(temp); - } - - if (SUCCEEDED(values->GetStringValue(WPD_DEVICE_SERIAL_NUMBER, &temp))) { - t = PyUnicode_FromWideChar(temp, wcslen(temp)); - if (t != NULL) {PyDict_SetItemString(ans, "serial_number", t); Py_DECREF(t);} - CoTaskMemFree(temp); - } - - if (SUCCEEDED(values->GetStringValue(WPD_DEVICE_FIRMWARE_VERSION, &temp))) { - t = PyUnicode_FromWideChar(temp, wcslen(temp)); - if (t != NULL) {PyDict_SetItemString(ans, "device_version", t); Py_DECREF(t);} - CoTaskMemFree(temp); - } - -end: - if (keys != NULL) keys->Release(); - if (values != NULL) values->Release(); - if (properties != NULL) properties->Release(); - if (content != NULL) content->Release(); - return ans; -} // }}} - -// }}} +extern IPortableDeviceValues* wpd::get_client_information(); +extern IPortableDevice* wpd::open_device(const wchar_t *pnp_id, IPortableDeviceValues *client_information); +extern PyObject* wpd::get_device_information(IPortableDevice *device); // Module startup/shutdown {{{ static PyObject *