blob: faa0a29712f9f8eff768c199ebe6dde5a0a5630d [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the tools applications of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:BSD$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** BSD License Usage
** Alternatively, you may use this file under the terms of the BSD license
** as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
** * Redistributions of source code must retain the above copyright
** notice, this list of conditions and the following disclaimer.
** * Redistributions in binary form must reproduce the above copyright
** notice, this list of conditions and the following disclaimer in
** the documentation and/or other materials provided with the
** distribution.
** * Neither the name of The Qt Company Ltd nor the names of its
** contributors may be used to endorse or promote products derived
** from this software without specific prior written permission.
**
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
** $QT_END_LICENSE$
**
****************************************************************************/
#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 <QtCore/QString>
#include <QtCore/QMutex>
#include <QtCore/QMutexLocker>
#include <comdef.h>
#include <wrl.h>
using namespace Microsoft::WRL;
QT_USE_NAMESPACE
Q_LOGGING_CATEGORY(lcCoreCon, "qt.corecon")
#define wchar(str) reinterpret_cast<LPCWSTR>(str.utf16())
template <typename ObjectContainerType, typename ContainerType, typename CollectionType>
static inline HRESULT collectionFor(const ComPtr<ContainerType> &container, ComPtr<CollectionType> &collection)
{
ComPtr<ObjectContainerType> 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 <typename DeviceType>
class CoreConDevicePrivateVersioned : public CoreConDevicePrivate
{
public:
CoreConDevicePrivateVersioned(int version) : CoreConDevicePrivate(version) { }
ComPtr<DeviceType> handle;
};
CoreConDevice::CoreConDevice(int version)
{
if (version == 11)
d_ptr.reset(new CoreConDevicePrivateVersioned<ICcDevice_11>(version));
else if (version == 12)
d_ptr.reset(new CoreConDevicePrivateVersioned<ICcDevice_12>(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<const CoreConDevicePrivateVersioned<ICcDevice_11> *>(d)->handle.Get();
if (d->version == 12)
return static_cast<const CoreConDevicePrivateVersioned<ICcDevice_12> *>(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<CoreConDevice *> devices;
HMODULE langModule;
template <typename T>
static CoreConDevicePrivateVersioned<T> *deviceHandle(CoreConDevice *device)
{
return static_cast<CoreConDevicePrivateVersioned<T> *>(device->d_ptr.data());
}
};
template <typename ServerType, typename DataStoreType, typename PlatformType,
typename PlatformContainerType, typename CollectionType, typename DeviceType, typename DeviceContainerType,
typename ObjectType, typename ObjectContainerType, typename PropertyType, typename PropertyContainerType>
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<DataStoreType> 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<PlatformContainerType> platformContainer;
hr = dataStore->get_PlatformContainer(&platformContainer);
if (FAILED(hr)) {
qCDebug(lcCoreCon, "Failed to obtain the platform container. HRESULT: 0x%x", hr);
return false;
}
ComPtr<CollectionType> platformCollection;
hr = collectionFor<ObjectContainerType>(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<ObjectType> platformObject;
hr = platformCollection->get_Item(platformIndex, &platformObject);
if (FAILED(hr)) {
qCDebug(lcCoreCon, "\1: %d", platformIndex);
continue;
}
ComPtr<PlatformType> platform;
hr = platformObject.As(&platform);
if (FAILED(hr)) {
qCDebug(lcCoreCon, "\1: %d", platformIndex);
continue;
}
ComPtr<DeviceContainerType> deviceContainer;
hr = platform->get_DeviceContainer(&deviceContainer);
if (FAILED(hr)) {
qCDebug(lcCoreCon, "Failed to obtain the device container.. 0x%x", hr);
continue;
}
ComPtr<CollectionType> deviceCollection;
hr = collectionFor<ObjectContainerType>(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) {
QScopedPointer<CoreConDevice> device(new CoreConDevice(version));
ComPtr<ObjectType> 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<DeviceType>(device.data())->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<DeviceType>(device.data())->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<DeviceType>(device.data())->name = QString::fromWCharArray(deviceName);
ComPtr<PropertyContainerType> propertyContainer;
hr = deviceObject->get_PropertyContainer(&propertyContainer);
if (FAILED(hr)) {
qCDebug(lcCoreCon, "Failed to obtain a property container at index: %d", deviceIndex);
continue;
}
ComPtr<CollectionType> propertyCollection;
hr = collectionFor<ObjectContainerType>(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<ObjectType> 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<PropertyType> 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<DeviceType>(device.data())->isEmulator = value == _bstr_t(L"true");
}
}
if (!isPseudoDevice)
devices.append(device.take());
}
}
return true;
}
ComPtr<ServerType> handle;
};
typedef CoreConServerPrivateVersioned<ICcServer_11, ICcDatastore_11, ICcPlatform_11,
ICcPlatformContainer_11, ICcCollection_11, ICcDevice_11, ICcDeviceContainer_11,
ICcObject_11, ICcObjectContainer_11,
ICcProperty_11, ICcPropertyContainer_11> CoreConServerPrivate_11;
typedef CoreConServerPrivateVersioned<ICcServer_12, ICcDatastore_12, ICcPlatform_12,
ICcPlatformContainer_12, ICcCollection_12, ICcDevice_12, ICcDeviceContainer_12,
ICcObject_12, ICcObjectContainer_12,
ICcProperty_12, ICcPropertyContainer_12> 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<const CoreConServerPrivate_11 *>(d)->handle.Get();
if (d->version == 12)
return static_cast<const CoreConServerPrivate_12 *>(d)->handle.Get();
return 0;
}
QList<CoreConDevice *> 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);
}