// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR BSD-3-Clause #include "corecon.h" // Force all versions of CoreCon in scope #ifdef CCAPI_VERSIONED_H #undef CCAPI_VERSIONED_H #endif #ifdef CORECON_VER #undef CORECON_VER #endif #define CORECON_VER 11 #include "ccapi.h" #ifdef CCAPI_VERSIONED_H #undef CCAPI_VERSIONED_H #endif #ifdef CORECON_VER #undef CORECON_VER #endif #define CORECON_VER 12 #include "ccapi.h" #include #include #include #include #include using namespace Microsoft::WRL; QT_USE_NAMESPACE Q_LOGGING_CATEGORY(lcCoreCon, "qt.corecon") #define wchar(str) reinterpret_cast(str.utf16()) template static inline HRESULT collectionFor(const ComPtr &container, ComPtr &collection) { ComPtr objectContainer; HRESULT hr = container.As(&objectContainer); if (FAILED(hr)) return hr; hr = objectContainer->EnumerateObjects(&collection); return hr; } class CoreConDevicePrivate { public: QString name; QString id; bool isEmulator; int version; protected: CoreConDevicePrivate(int version) : version(version) { } }; template class CoreConDevicePrivateVersioned : public CoreConDevicePrivate { public: CoreConDevicePrivateVersioned(int version) : CoreConDevicePrivate(version) { } ComPtr handle; }; CoreConDevice::CoreConDevice(int version) { if (version == 11) d_ptr.reset(new CoreConDevicePrivateVersioned(version)); else if (version == 12) d_ptr.reset(new CoreConDevicePrivateVersioned(version)); else qCCritical(lcCoreCon) << "Invalid CoreCon version specified:" << version; } CoreConDevice::~CoreConDevice() { } QString CoreConDevice::name() const { Q_D(const CoreConDevice); return d->name; } QString CoreConDevice::id() const { Q_D(const CoreConDevice); return d->id; } bool CoreConDevice::isEmulator() const { Q_D(const CoreConDevice); return d->isEmulator; } Qt::HANDLE CoreConDevice::handle() const { Q_D(const CoreConDevice); if (d->version == 11) return static_cast *>(d)->handle.Get(); if (d->version == 12) return static_cast *>(d)->handle.Get(); return 0; } class ComInitializer { protected: ComInitializer() { hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); if (FAILED(hr)) qCDebug(lcCoreCon) << "Failed to initialize COM."; } virtual ~ComInitializer() { if (SUCCEEDED(hr)) CoUninitialize(); } HRESULT hr; }; class CoreConServerPrivate : private ComInitializer { public: CoreConServerPrivate(int version) : version(version), langModule(0) { } ~CoreConServerPrivate() { qDeleteAll(devices); devices.clear(); } virtual bool initialize() = 0; int version; QList devices; HMODULE langModule; template static CoreConDevicePrivateVersioned *deviceHandle(CoreConDevice *device) { return static_cast *>(device->d_ptr.data()); } }; template class CoreConServerPrivateVersioned : public CoreConServerPrivate { public: CoreConServerPrivateVersioned(CoreConServer *server, int version) : CoreConServerPrivate(version) { HRESULT hr = E_FAIL; if (version == 11) hr = CoCreateInstance(CLSID_ConMan_11, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&handle)); else if (version == 12) hr = CoCreateInstance(CLSID_ConMan_12, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&handle)); else qCCritical(lcCoreCon) << "Invalid CoreCon version specified:" << version; if (FAILED(hr)) qCWarning(lcCoreCon) << "Failed to initialize connection server." << server->formatError(hr); // The language module is available as long as the above succeeded langModule = GetModuleHandle(L"conmanui"); } bool initialize() { ComPtr dataStore; HRESULT hr = handle->GetDatastore(GetUserDefaultLCID(), &dataStore); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain the data store. HRESULT: 0x%x", hr); return false; } ComPtr platformContainer; hr = dataStore->get_PlatformContainer(&platformContainer); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain the platform container. HRESULT: 0x%x", hr); return false; } ComPtr platformCollection; hr = collectionFor(platformContainer, platformCollection); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain the platform collection. HRESULT: 0x%x", hr); return false; } long platformCount; hr = platformCollection->get_Count(&platformCount); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain the platform object count. HRESULT: 0x%x", hr); return false; } for (long platformIndex = 0; platformIndex < platformCount; ++platformIndex) { ComPtr platformObject; hr = platformCollection->get_Item(platformIndex, &platformObject); if (FAILED(hr)) { qCDebug(lcCoreCon, "\1: %d", platformIndex); continue; } ComPtr platform; hr = platformObject.As(&platform); if (FAILED(hr)) { qCDebug(lcCoreCon, "\1: %d", platformIndex); continue; } ComPtr deviceContainer; hr = platform->get_DeviceContainer(&deviceContainer); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain the device container.. 0x%x", hr); continue; } ComPtr deviceCollection; hr = collectionFor(deviceContainer, deviceCollection); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain the device object collection.. 0x%x", hr); continue; } long deviceCount; hr = deviceCollection->get_Count(&deviceCount); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain the device object count.. 0x%x", hr); continue; } for (long deviceIndex = 0; deviceIndex < deviceCount; ++deviceIndex) { std::unique_ptr device(new CoreConDevice(version)); ComPtr deviceObject; hr = deviceCollection->get_Item(deviceIndex, &deviceObject); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain the device object at index: %d", deviceIndex); continue; } hr = deviceObject.As(&deviceHandle(device.get())->handle); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to confirm a device from the object at index: %d", deviceIndex); continue; } _bstr_t deviceId; hr = deviceObject->get_ID(deviceId.GetAddress()); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain device id at index: %d", deviceIndex); continue; } deviceHandle(device.get())->id = QString::fromWCharArray(deviceId); _bstr_t deviceName; hr = deviceObject->get_Name(deviceName.GetAddress()); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain device name at index: %d", deviceIndex); continue; } deviceHandle(device.get())->name = QString::fromWCharArray(deviceName); ComPtr propertyContainer; hr = deviceObject->get_PropertyContainer(&propertyContainer); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain a property container at index: %d", deviceIndex); continue; } ComPtr propertyCollection; hr = collectionFor(propertyContainer, propertyCollection); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain property collection of device at index: %d", deviceIndex); continue; } bool isPseudoDevice = false; long propertyCount; hr = propertyCollection->get_Count(&propertyCount); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain property count of device at index: %d", deviceIndex); continue; } for (long propertyIndex = 0; propertyIndex < propertyCount; ++propertyIndex) { ComPtr propertyObject; hr = propertyCollection->get_Item(propertyIndex, &propertyObject); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain property at index: %d", propertyIndex); continue; } _bstr_t id; hr = propertyObject->get_ID(id.GetAddress()); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to obtain property id at index: %d", propertyIndex); continue; } ComPtr property; hr = propertyObject.As(&property); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to cast the property object at index: %d", propertyIndex); continue; } if (id == _bstr_t(L"IsPseudoDevice")) { _bstr_t value; hr = property->get_Value(value.GetAddress()); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to cast the property value at index: %d", propertyIndex); continue; } if (value == _bstr_t(L"true")) { isPseudoDevice = true; break; // No need to look at this device further } } if (id == _bstr_t(L"Emulator")) { _bstr_t value; hr = property->get_Value(value.GetAddress()); if (FAILED(hr)) { qCDebug(lcCoreCon, "Failed to cast the property value at index: %d", propertyIndex); continue; } deviceHandle(device.get())->isEmulator = value == _bstr_t(L"true"); } } if (!isPseudoDevice) devices.append(device.release()); } } return true; } ComPtr handle; }; typedef CoreConServerPrivateVersioned CoreConServerPrivate_11; typedef CoreConServerPrivateVersioned CoreConServerPrivate_12; CoreConServer::CoreConServer(int version) { if (version == 11) d_ptr.reset(new CoreConServerPrivate_11(this, version)); else if (version == 12) d_ptr.reset(new CoreConServerPrivate_12(this, version)); else qCCritical(lcCoreCon) << "Invalid CoreCon version specified:" << version; initialize(); } CoreConServer::~CoreConServer() { } Qt::HANDLE CoreConServer::handle() const { Q_D(const CoreConServer); if (d->version == 11) return static_cast(d)->handle.Get(); if (d->version == 12) return static_cast(d)->handle.Get(); return 0; } QList CoreConServer::devices() const { Q_D(const CoreConServer); return d->devices; } bool CoreConServer::initialize() { Q_D(CoreConServer); if (!d || !handle()) return false; if (!d->devices.isEmpty()) return true; return d->initialize(); } QString CoreConServer::formatError(HRESULT hr) const { Q_D(const CoreConServer); wchar_t error[1024]; HMODULE module = 0; DWORD origin = HRESULT_FACILITY(hr); if (origin == 0x973 || origin == 0x974 || origin == 0x103) module = d->langModule; if (module) { int length = LoadString(module, HRESULT_CODE(hr), error, sizeof(error)/sizeof(wchar_t)); if (length) return QString::fromWCharArray(error, length).trimmed(); } return qt_error_string(hr); }