Add missing comparison operators for Microsoft::WRL::ComPtr on MINGW

MINGW's implementation of Microsoft::WRL::ComPtr lacks comparison
operators, and calling operator==() on them will implicitly convert them
to bool before comparing the resulting bool values. Two non-zero ComPtr
instances will therefore always compare equal, even if they point to
different interfaces.

This patch adds ComPtr comparison operators if they are missing, and
replaces existing includes to wrl.h or wrl/client.h with
QtCore/private/qcomptr_p.h

Pick-to: 6.8
Change-Id: I8123d9d874ae53ebfd6d381b69097e75527848b6
Reviewed-by: Mårten Nordheim <marten.nordheim@qt.io>
(cherry picked from commit 6d1384034deb681c1c4a656a1582f3e1606b1c1a)
Reviewed-by: Qt Cherry-pick Bot <cherrypick_bot@qt-project.org>
This commit is contained in:
Jøger Hansegård 2025-03-01 22:27:51 +01:00 committed by Qt Cherry-pick Bot
parent d7e2d6b22c
commit 69cdf3cbbb
14 changed files with 308 additions and 32 deletions

View File

@ -24,8 +24,8 @@
#include <QtCore/QAbstractEventDispatcher>
#include <QtCore/QElapsedTimer>
#include <QtCore/qt_windows.h>
#include <QtCore/private/qcomptr_p.h>
#include <wrl.h>
#include <windows.foundation.h>
// Convenience macros for handling HRESULT values
@ -68,10 +68,10 @@ enum AwaitStyle
using EarlyExitConditionFunction = std::function<bool(void)>;
template<typename T>
static inline HRESULT _await_impl(const Microsoft::WRL::ComPtr<T> &asyncOp, AwaitStyle awaitStyle,
uint timeout, EarlyExitConditionFunction func)
static inline HRESULT _await_impl(const ComPtr<T> &asyncOp, AwaitStyle awaitStyle, uint timeout,
EarlyExitConditionFunction func)
{
Microsoft::WRL::ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
ComPtr<ABI::Windows::Foundation::IAsyncInfo> asyncInfo;
HRESULT hr = asyncOp.As(&asyncInfo);
if (FAILED(hr))
return hr;
@ -127,9 +127,8 @@ static inline HRESULT _await_impl(const Microsoft::WRL::ComPtr<T> &asyncOp, Awai
}
template<typename T>
static inline HRESULT await(const Microsoft::WRL::ComPtr<T> &asyncOp,
AwaitStyle awaitStyle = YieldThread, uint timeout = 0,
EarlyExitConditionFunction func = nullptr)
static inline HRESULT await(const ComPtr<T> &asyncOp, AwaitStyle awaitStyle = YieldThread,
uint timeout = 0, EarlyExitConditionFunction func = nullptr)
{
HRESULT hr = _await_impl(asyncOp, awaitStyle, timeout, func);
if (FAILED(hr))
@ -139,7 +138,7 @@ static inline HRESULT await(const Microsoft::WRL::ComPtr<T> &asyncOp,
}
template<typename T, typename U>
static inline HRESULT await(const Microsoft::WRL::ComPtr<T> &asyncOp, U *results,
static inline HRESULT await(const ComPtr<T> &asyncOp, U *results,
AwaitStyle awaitStyle = YieldThread, uint timeout = 0,
EarlyExitConditionFunction func = nullptr)
{

View File

@ -16,11 +16,136 @@
//
#include <QtCore/private/qglobal_p.h>
#include <QtCore/qtconfigmacros.h>
#include <type_traits>
#if defined(Q_OS_WIN) || defined(Q_QDOC)
// clang-format off
#include <QtCore/qt_windows.h>
#include <wrl/client.h>
// clang-format on
QT_BEGIN_NAMESPACE
namespace Detail {
template <typename, typename, typename = void>
struct has_equal_to_operator : std::false_type
{
};
template <typename T, typename U>
struct has_equal_to_operator<
T, U, std::void_t<decltype(operator==(std::declval<T>(), std::declval<U>()))>>
: std::true_type
{
};
template <typename T, typename U>
using EnableIfMissingEqualToOperator = std::enable_if_t<!has_equal_to_operator<T, U>::value, bool>;
template <typename, typename, typename = void>
struct has_not_equal_to_operator : std::false_type
{
};
template <typename T, typename U>
struct has_not_equal_to_operator<
T, U, std::void_t<decltype(operator!=(std::declval<T>(), std::declval<U>()))>>
: std::true_type
{
};
template <typename T, typename U>
using EnableIfMissingNotEqualToOperator =
std::enable_if_t<!has_not_equal_to_operator<T, U>::value, bool>;
template <typename, typename, typename = void>
struct has_less_than_operator : std::false_type
{
};
template <typename T, typename U>
struct has_less_than_operator<
T, U, std::void_t<decltype(operator<(std::declval<T>(), std::declval<U>()))>>
: std::true_type
{
};
template <typename T, typename U>
using EnableIfMissingLessThanOperator =
std::enable_if_t<!has_less_than_operator<T, U>::value, bool>;
} // namespace Detail
QT_END_NAMESPACE
namespace Microsoft {
namespace WRL {
// Add missing comparison operators if MINGW does not provide them
template <typename T, typename U,
QT_PREPEND_NAMESPACE(Detail)::EnableIfMissingEqualToOperator<ComPtr<T>, ComPtr<U>> = true>
bool operator==(const ComPtr<T> &lhs, const ComPtr<U> &rhs) noexcept
{
static_assert(std::is_base_of_v<T, U> || std::is_base_of_v<U, T>);
return lhs.Get() == rhs.Get();
}
template <typename T,
QT_PREPEND_NAMESPACE(Detail)::EnableIfMissingEqualToOperator<ComPtr<T>, std::nullptr_t> =
true>
bool operator==(const ComPtr<T> &lhs, std::nullptr_t) noexcept
{
return lhs.Get() == nullptr;
}
template <typename T,
QT_PREPEND_NAMESPACE(Detail)::EnableIfMissingEqualToOperator<std::nullptr_t, ComPtr<T>> =
true>
bool operator==(std::nullptr_t, const ComPtr<T> &rhs) noexcept
{
return rhs.Get() == nullptr;
}
template <typename T, typename U,
QT_PREPEND_NAMESPACE(Detail)::EnableIfMissingNotEqualToOperator<ComPtr<T>, ComPtr<U>> =
true>
bool operator!=(const ComPtr<T> &a, const ComPtr<U> &b) noexcept
{
static_assert(std::is_base_of_v<T, U> || std::is_base_of_v<U, T>);
return a.Get() != b.Get();
}
template <class T,
QT_PREPEND_NAMESPACE(
Detail)::EnableIfMissingNotEqualToOperator<ComPtr<T>, std::nullptr_t> = true>
bool operator!=(const ComPtr<T> &a, std::nullptr_t) noexcept
{
return a.Get() != nullptr;
}
template <class T,
QT_PREPEND_NAMESPACE(
Detail)::EnableIfMissingNotEqualToOperator<std::nullptr_t, ComPtr<T>> = true>
bool operator!=(std::nullptr_t, const ComPtr<T> &a) noexcept
{
return a.Get() != nullptr;
}
// MSVC WRL only defines operator<, we do not add other variants such as <=, > or >=
template <
class T, class U,
QT_PREPEND_NAMESPACE(Detail)::EnableIfMissingLessThanOperator<ComPtr<T>, ComPtr<U>> = true>
bool operator<(const ComPtr<T> &a, const ComPtr<U> &b) noexcept
{
static_assert(std::is_base_of_v<T, U> || std::is_base_of_v<U, T>);
return a.Get() < b.Get();
}
} // namespace WRL
} // namespace Microsoft
QT_BEGIN_NAMESPACE

View File

@ -15,7 +15,7 @@
#include <objbase.h>
#include <netlistmgr.h>
#include <wrl/client.h>
#include <QtCore/private/qcomptr_p.h>
#include <wrl/wrappers/corewrappers.h>
#include <iphlpapi.h>

View File

@ -16,7 +16,7 @@
#include <objbase.h>
#include <ocidl.h>
#include <netlistmgr.h>
#include <wrl/client.h>
#include <QtCore/private/qcomptr_p.h>
#include <wrl/wrappers/corewrappers.h>
#if QT_CONFIG(cpp_winrt)

View File

@ -6,13 +6,10 @@
#include "qwindowsdirect2dhelpers.h"
#include "qwindowsdirect2ddevicecontext.h"
#include <QtCore/private/qcomptr_p.h>
#include <QtGui/qimage.h>
#include <QtGui/qcolor.h>
#include <wrl.h>
using Microsoft::WRL::ComPtr;
QT_BEGIN_NAMESPACE
class QWindowsDirect2DBitmapPrivate

View File

@ -6,15 +6,14 @@
#include "qwindowsdirect2dhelpers.h"
#include "qwindowsdirect2dintegration.h"
#include <QtCore/private/qcomptr_p.h>
#include <d3d11_1.h>
#include <d2d1_1.h>
#include <d2d1_1helper.h>
#include <dxgi1_2.h>
#include <wrl.h>
#include <dwrite.h>
using Microsoft::WRL::ComPtr;
QT_BEGIN_NAMESPACE
class QWindowsDirect2DContextPrivate

View File

@ -5,9 +5,7 @@
#include "qwindowsdirect2dhelpers.h"
#include "qwindowsdirect2ddevicecontext.h"
#include <wrl.h>
using Microsoft::WRL::ComPtr;
#include <QtCore/private/qcomptr_p.h>
QT_BEGIN_NAMESPACE

View File

@ -15,6 +15,7 @@
#include <QtCore/qmath.h>
#include <QtCore/qstack.h>
#include <QtCore/qsettings.h>
#include <QtCore/private/qcomptr_p.h>
#include <QtGui/private/qpaintengine_p.h>
#include <QtGui/private/qtextengine_p.h>
#include <QtGui/private/qfontengine_p.h>
@ -22,9 +23,6 @@
#include <d2d1_1.h>
#include <dwrite_1.h>
#include <wrl.h>
using Microsoft::WRL::ComPtr;
QT_BEGIN_NAMESPACE

View File

@ -5,7 +5,7 @@
#define QWINDOWSDIRECT2DWINDOW_H
#include "qwindowswindow.h"
#include <wrl.h>
#include <QtCore/private/qcomptr_p.h>
struct IDXGISwapChain1;
struct ID2D1DeviceContext;
@ -34,8 +34,8 @@ private:
void setupBitmap();
private:
Microsoft::WRL::ComPtr<IDXGISwapChain1> m_swapChain;
Microsoft::WRL::ComPtr<ID2D1DeviceContext> m_deviceContext;
ComPtr<IDXGISwapChain1> m_swapChain;
ComPtr<ID2D1DeviceContext> m_deviceContext;
QScopedPointer<QWindowsDirect2DBitmap> m_bitmap;
QScopedPointer<QPixmap> m_pixmap;
bool m_needsFullFlush = true;

View File

@ -50,8 +50,7 @@
#include <QtCore/qoperatingsystemversion.h>
#include <QtCore/private/qfunctions_win_p.h>
#include <wrl.h>
#include <QtCore/private/qcomptr_p.h>
#include <limits.h>
@ -721,8 +720,6 @@ void QWindowsIntegration::setApplicationBadge(const QImage &image)
{
QComHelper comHelper;
using Microsoft::WRL::ComPtr;
ComPtr<ITaskbarList3> taskbarList;
CoCreateInstance(CLSID_TaskbarList, nullptr,
CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&taskbarList));

View File

@ -3,4 +3,5 @@
add_subdirectory(qcomobject)
add_subdirectory(qbstr)
add_subdirectory(qcomptr)
add_subdirectory(qcomvariant)

View File

@ -0,0 +1,19 @@
# Copyright (C) 2025 The Qt Company Ltd.
# SPDX-License-Identifier: BSD-3-Clause
#####################################################################
## tst_qcomptr Test:
#####################################################################
if(NOT QT_BUILD_STANDALONE_TESTS AND NOT QT_BUILDING_QT)
cmake_minimum_required(VERSION 3.16)
project(tst_qcomptr LANGUAGES CXX)
find_package(Qt6BuildInternals REQUIRED COMPONENTS STANDALONE_TEST)
endif()
qt_internal_add_test(tst_qcomptr
SOURCES
tst_qcomptr.cpp
LIBRARIES
Qt::CorePrivate
)

View File

@ -0,0 +1,143 @@
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
#include <QtTest/qtest.h>
#include <QtCore/private/qcomptr_p.h>
#include <QtCore/private/qcomobject_p.h>
MIDL_INTERFACE("8B06D1DA-94F2-45A5-9E86-77E89A51FAC7")
IMyInterface : IUnknown{};
#ifdef __CRT_UUID_DECL
__CRT_UUID_DECL(IMyInterface, 0x8b06d1da, 0x94f2, 0x45a5, 0x9e, 0x86, 0x77, 0xe8, 0x9a, 0x51, 0xfa,
0xc7);
#endif
QT_USE_NAMESPACE
class IMyInterfaceImpl : public QComObject<IMyInterface>
{
};
qsizetype s_instanceCount = 0;
class IUnknownImpl : public QComObject<IUnknown>
{
public:
IUnknownImpl()
{ //
++s_instanceCount;
}
~IUnknownImpl()
{ //
--s_instanceCount;
}
};
class IDispatchImpl : public QComObject<IDispatch>
{
public:
HRESULT GetTypeInfoCount(UINT *) override { return E_FAIL; }
HRESULT GetTypeInfo(UINT, LCID, ITypeInfo **) override { return E_FAIL; }
HRESULT GetIDsOfNames(const IID &, LPOLESTR *, UINT, LCID, DISPID *) override { return E_FAIL; }
HRESULT Invoke(DISPID, const IID &, LCID, WORD, DISPPARAMS *, VARIANT *, EXCEPINFO *,
UINT *) override
{
return E_FAIL;
}
};
class tst_qcomptr : public QObject
{
Q_OBJECT
private slots:
void makeComObject_createsComObject()
{
// See bug report https://sourceforge.net/p/mingw-w64/bugs/892/
{
ComPtr<IUnknown> o = makeComObject<IUnknownImpl>();
QCOMPARE_EQ(o->AddRef(), 2ul);
QCOMPARE_EQ(o->Release(), 1ul);
QVERIFY(o);
}
QCOMPARE_EQ(s_instanceCount, 0);
}
void comparison_returnsTrue_withEqualInstances()
{
{
// MINGW may lack comparison operator
ComPtr<IUnknown> o1 = makeComObject<IUnknownImpl>();
ComPtr<IUnknown> o2 = o1;
QCOMPARE_EQ(o1, o2);
QVERIFY(!(o1 != o2));
}
{
//ComPtr<IMyInterface> o1 = makeComObject<IMyInterfaceImpl>();
//ComPtr<IDispatch> o2 = makeComObject<IDispatchImpl>();
//QVERIFY(!(o1 == o2)); // Intentionally fails to compile
}
}
void comparison_returnsFalse_withUnequalInstances()
{
// MINGW may lack comparison operator
{
ComPtr<IUnknown> o1 = makeComObject<IUnknownImpl>();
ComPtr<IUnknown> o2 = makeComObject<IUnknownImpl>();
QCOMPARE_NE(o1, o2);
QVERIFY(!(o1 == o2));
}
{
ComPtr<IUnknown> o1 = makeComObject<IUnknownImpl>();
ComPtr<IMyInterface> o2 = makeComObject<IMyInterfaceImpl>();
QCOMPARE_NE(o1, o2);
QVERIFY(!(o1 == o2));
}
{
//ComPtr<IMyInterface> o1 = makeComObject<IMyInterfaceImpl>();
//ComPtr<IDispatch> o2 = makeComObject<IDispatchImpl>();
//QCOMPARE_NE(o1, o2); // Intentionally fails to compile
}
}
void comparison_returnsTrue_whenComparingNullWithNullptr()
{
// MINGW may lack comparison operator
ComPtr<IUnknown> o;
QCOMPARE_EQ(o, nullptr);
QCOMPARE_EQ(nullptr, o);
}
void comparison_returnsTrue_whenComparingNotNullWithNullptr()
{
// MINGW may lack comparison operator
ComPtr<IUnknown> o = makeComObject<IUnknownImpl>();
QCOMPARE_NE(o, nullptr);
QCOMPARE_NE(nullptr, o);
}
void lessThan_returnsEqualToRawPointerComparison()
{
// MINGW may lack comparison operator
{
ComPtr<IUnknown> o1 = makeComObject<IUnknownImpl>();
ComPtr<IUnknown> o2 = makeComObject<IUnknownImpl>();
QCOMPARE_EQ(o1 < o2, o1.Get() < o2.Get());
}
{
ComPtr<IUnknown> o1 = makeComObject<IUnknownImpl>();
ComPtr<IMyInterface> o2 = makeComObject<IMyInterfaceImpl>();
QCOMPARE_EQ(o1 < o2, o1.Get() < o2.Get());
}
{
//ComPtr<IMyInterface> o1 = makeComObject<IMyInterfaceImpl>();
//ComPtr<IDispatch> o2 = makeComObject<IDispatchImpl>();
//QCOMPARE_EQ(o1 < o2, o1.Get() < o2.Get()); // Intentionally fails to compile
}
}
};
QTEST_MAIN(tst_qcomptr)
#include "tst_qcomptr.moc"

View File

@ -5,7 +5,7 @@
#include <private/qcomvariant_p.h>
#include <private/qcomobject_p.h>
#include <wrl/client.h>
#include <QtCore/private/qcomptr_p.h>
using Microsoft::WRL::ComPtr;