From 69cdf3cbbbadc504329b3154eabe5706981d87b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8ger=20Hanseg=C3=A5rd?= Date: Sat, 1 Mar 2025 22:27:51 +0100 Subject: [PATCH] Add missing comparison operators for Microsoft::WRL::ComPtr on MINGW MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 (cherry picked from commit 6d1384034deb681c1c4a656a1582f3e1606b1c1a) Reviewed-by: Qt Cherry-pick Bot --- src/corelib/kernel/qfunctions_winrt_p.h | 15 +- src/corelib/platform/windows/qcomptr_p.h | 127 +++++++++++++++- src/network/kernel/qnetconmonitor_win.cpp | 2 +- .../qnetworklistmanagerevents.h | 2 +- .../direct2d/qwindowsdirect2dbitmap.cpp | 5 +- .../direct2d/qwindowsdirect2dcontext.cpp | 5 +- .../qwindowsdirect2ddevicecontext.cpp | 4 +- .../direct2d/qwindowsdirect2dpaintengine.cpp | 4 +- .../direct2d/qwindowsdirect2dwindow.h | 6 +- .../platforms/windows/qwindowsintegration.cpp | 5 +- .../corelib/platform/windows/CMakeLists.txt | 1 + .../platform/windows/qcomptr/CMakeLists.txt | 19 +++ .../platform/windows/qcomptr/tst_qcomptr.cpp | 143 ++++++++++++++++++ .../windows/qcomvariant/tst_qcomvariant.cpp | 2 +- 14 files changed, 308 insertions(+), 32 deletions(-) create mode 100644 tests/auto/corelib/platform/windows/qcomptr/CMakeLists.txt create mode 100644 tests/auto/corelib/platform/windows/qcomptr/tst_qcomptr.cpp diff --git a/src/corelib/kernel/qfunctions_winrt_p.h b/src/corelib/kernel/qfunctions_winrt_p.h index c895733c935..6c406961fd6 100644 --- a/src/corelib/kernel/qfunctions_winrt_p.h +++ b/src/corelib/kernel/qfunctions_winrt_p.h @@ -24,8 +24,8 @@ #include #include #include +#include -#include #include // Convenience macros for handling HRESULT values @@ -68,10 +68,10 @@ enum AwaitStyle using EarlyExitConditionFunction = std::function; template -static inline HRESULT _await_impl(const Microsoft::WRL::ComPtr &asyncOp, AwaitStyle awaitStyle, - uint timeout, EarlyExitConditionFunction func) +static inline HRESULT _await_impl(const ComPtr &asyncOp, AwaitStyle awaitStyle, uint timeout, + EarlyExitConditionFunction func) { - Microsoft::WRL::ComPtr asyncInfo; + ComPtr asyncInfo; HRESULT hr = asyncOp.As(&asyncInfo); if (FAILED(hr)) return hr; @@ -127,9 +127,8 @@ static inline HRESULT _await_impl(const Microsoft::WRL::ComPtr &asyncOp, Awai } template -static inline HRESULT await(const Microsoft::WRL::ComPtr &asyncOp, - AwaitStyle awaitStyle = YieldThread, uint timeout = 0, - EarlyExitConditionFunction func = nullptr) +static inline HRESULT await(const ComPtr &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 &asyncOp, } template -static inline HRESULT await(const Microsoft::WRL::ComPtr &asyncOp, U *results, +static inline HRESULT await(const ComPtr &asyncOp, U *results, AwaitStyle awaitStyle = YieldThread, uint timeout = 0, EarlyExitConditionFunction func = nullptr) { diff --git a/src/corelib/platform/windows/qcomptr_p.h b/src/corelib/platform/windows/qcomptr_p.h index 7ee286d254e..2a69e7b6038 100644 --- a/src/corelib/platform/windows/qcomptr_p.h +++ b/src/corelib/platform/windows/qcomptr_p.h @@ -16,11 +16,136 @@ // #include - +#include +#include #if defined(Q_OS_WIN) || defined(Q_QDOC) +// clang-format off #include #include +// clang-format on + +QT_BEGIN_NAMESPACE + +namespace Detail { + +template +struct has_equal_to_operator : std::false_type +{ +}; + +template +struct has_equal_to_operator< + T, U, std::void_t(), std::declval()))>> + : std::true_type +{ +}; + +template +using EnableIfMissingEqualToOperator = std::enable_if_t::value, bool>; + +template +struct has_not_equal_to_operator : std::false_type +{ +}; + +template +struct has_not_equal_to_operator< + T, U, std::void_t(), std::declval()))>> + : std::true_type +{ +}; + +template +using EnableIfMissingNotEqualToOperator = + std::enable_if_t::value, bool>; + +template +struct has_less_than_operator : std::false_type +{ +}; + +template +struct has_less_than_operator< + T, U, std::void_t(), std::declval()))>> + : std::true_type +{ +}; + +template +using EnableIfMissingLessThanOperator = + std::enable_if_t::value, bool>; + +} // namespace Detail + +QT_END_NAMESPACE + +namespace Microsoft { +namespace WRL { + +// Add missing comparison operators if MINGW does not provide them + +template , ComPtr> = true> +bool operator==(const ComPtr &lhs, const ComPtr &rhs) noexcept +{ + static_assert(std::is_base_of_v || std::is_base_of_v); + return lhs.Get() == rhs.Get(); +} + +template , std::nullptr_t> = + true> +bool operator==(const ComPtr &lhs, std::nullptr_t) noexcept +{ + return lhs.Get() == nullptr; +} + +template > = + true> +bool operator==(std::nullptr_t, const ComPtr &rhs) noexcept +{ + return rhs.Get() == nullptr; +} + +template , ComPtr> = + true> +bool operator!=(const ComPtr &a, const ComPtr &b) noexcept +{ + static_assert(std::is_base_of_v || std::is_base_of_v); + return a.Get() != b.Get(); +} + +template , std::nullptr_t> = true> +bool operator!=(const ComPtr &a, std::nullptr_t) noexcept +{ + return a.Get() != nullptr; +} + +template > = true> +bool operator!=(std::nullptr_t, const ComPtr &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> = true> +bool operator<(const ComPtr &a, const ComPtr &b) noexcept +{ + static_assert(std::is_base_of_v || std::is_base_of_v); + return a.Get() < b.Get(); +} + +} // namespace WRL +} // namespace Microsoft QT_BEGIN_NAMESPACE diff --git a/src/network/kernel/qnetconmonitor_win.cpp b/src/network/kernel/qnetconmonitor_win.cpp index bf6aff1e468..76299e9a025 100644 --- a/src/network/kernel/qnetconmonitor_win.cpp +++ b/src/network/kernel/qnetconmonitor_win.cpp @@ -15,7 +15,7 @@ #include #include -#include +#include #include #include diff --git a/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.h b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.h index d91cd8a4cc6..1bfe33e91ce 100644 --- a/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.h +++ b/src/plugins/networkinformation/networklistmanager/qnetworklistmanagerevents.h @@ -16,7 +16,7 @@ #include #include #include -#include +#include #include #if QT_CONFIG(cpp_winrt) diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dbitmap.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dbitmap.cpp index 310053eedc3..8f9812e41e4 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dbitmap.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dbitmap.cpp @@ -6,13 +6,10 @@ #include "qwindowsdirect2dhelpers.h" #include "qwindowsdirect2ddevicecontext.h" +#include #include #include -#include - -using Microsoft::WRL::ComPtr; - QT_BEGIN_NAMESPACE class QWindowsDirect2DBitmapPrivate diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dcontext.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dcontext.cpp index 307ca2e550d..a955137cfd6 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dcontext.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dcontext.cpp @@ -6,15 +6,14 @@ #include "qwindowsdirect2dhelpers.h" #include "qwindowsdirect2dintegration.h" +#include + #include #include #include #include -#include #include -using Microsoft::WRL::ComPtr; - QT_BEGIN_NAMESPACE class QWindowsDirect2DContextPrivate diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2ddevicecontext.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2ddevicecontext.cpp index 2eb6e2c36d1..268a6f0188f 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2ddevicecontext.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2ddevicecontext.cpp @@ -5,9 +5,7 @@ #include "qwindowsdirect2dhelpers.h" #include "qwindowsdirect2ddevicecontext.h" -#include - -using Microsoft::WRL::ComPtr; +#include QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp index bc304f78be9..2e80c121920 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dpaintengine.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include @@ -22,9 +23,6 @@ #include #include -#include - -using Microsoft::WRL::ComPtr; QT_BEGIN_NAMESPACE diff --git a/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.h b/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.h index bb7d406a337..0d6bd8bd96d 100644 --- a/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.h +++ b/src/plugins/platforms/direct2d/qwindowsdirect2dwindow.h @@ -5,7 +5,7 @@ #define QWINDOWSDIRECT2DWINDOW_H #include "qwindowswindow.h" -#include +#include struct IDXGISwapChain1; struct ID2D1DeviceContext; @@ -34,8 +34,8 @@ private: void setupBitmap(); private: - Microsoft::WRL::ComPtr m_swapChain; - Microsoft::WRL::ComPtr m_deviceContext; + ComPtr m_swapChain; + ComPtr m_deviceContext; QScopedPointer m_bitmap; QScopedPointer m_pixmap; bool m_needsFullFlush = true; diff --git a/src/plugins/platforms/windows/qwindowsintegration.cpp b/src/plugins/platforms/windows/qwindowsintegration.cpp index a9fcf711edd..25c15861ba1 100644 --- a/src/plugins/platforms/windows/qwindowsintegration.cpp +++ b/src/plugins/platforms/windows/qwindowsintegration.cpp @@ -50,8 +50,7 @@ #include #include - -#include +#include #include @@ -721,8 +720,6 @@ void QWindowsIntegration::setApplicationBadge(const QImage &image) { QComHelper comHelper; - using Microsoft::WRL::ComPtr; - ComPtr taskbarList; CoCreateInstance(CLSID_TaskbarList, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&taskbarList)); diff --git a/tests/auto/corelib/platform/windows/CMakeLists.txt b/tests/auto/corelib/platform/windows/CMakeLists.txt index 2c6d6e9ffee..abcb17471c9 100644 --- a/tests/auto/corelib/platform/windows/CMakeLists.txt +++ b/tests/auto/corelib/platform/windows/CMakeLists.txt @@ -3,4 +3,5 @@ add_subdirectory(qcomobject) add_subdirectory(qbstr) +add_subdirectory(qcomptr) add_subdirectory(qcomvariant) diff --git a/tests/auto/corelib/platform/windows/qcomptr/CMakeLists.txt b/tests/auto/corelib/platform/windows/qcomptr/CMakeLists.txt new file mode 100644 index 00000000000..5530f98a0f6 --- /dev/null +++ b/tests/auto/corelib/platform/windows/qcomptr/CMakeLists.txt @@ -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 +) diff --git a/tests/auto/corelib/platform/windows/qcomptr/tst_qcomptr.cpp b/tests/auto/corelib/platform/windows/qcomptr/tst_qcomptr.cpp new file mode 100644 index 00000000000..219438af620 --- /dev/null +++ b/tests/auto/corelib/platform/windows/qcomptr/tst_qcomptr.cpp @@ -0,0 +1,143 @@ +// Copyright (C) 2025 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only + +#include +#include +#include + +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 +{ +}; + +qsizetype s_instanceCount = 0; + +class IUnknownImpl : public QComObject +{ +public: + IUnknownImpl() + { // + ++s_instanceCount; + } + + ~IUnknownImpl() + { // + --s_instanceCount; + } +}; + +class IDispatchImpl : public QComObject +{ +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 o = makeComObject(); + 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 o1 = makeComObject(); + ComPtr o2 = o1; + QCOMPARE_EQ(o1, o2); + QVERIFY(!(o1 != o2)); + } + { + //ComPtr o1 = makeComObject(); + //ComPtr o2 = makeComObject(); + //QVERIFY(!(o1 == o2)); // Intentionally fails to compile + } + } + + void comparison_returnsFalse_withUnequalInstances() + { + // MINGW may lack comparison operator + { + ComPtr o1 = makeComObject(); + ComPtr o2 = makeComObject(); + QCOMPARE_NE(o1, o2); + QVERIFY(!(o1 == o2)); + } + { + ComPtr o1 = makeComObject(); + ComPtr o2 = makeComObject(); + QCOMPARE_NE(o1, o2); + QVERIFY(!(o1 == o2)); + } + { + //ComPtr o1 = makeComObject(); + //ComPtr o2 = makeComObject(); + //QCOMPARE_NE(o1, o2); // Intentionally fails to compile + } + } + + void comparison_returnsTrue_whenComparingNullWithNullptr() + { + // MINGW may lack comparison operator + ComPtr o; + QCOMPARE_EQ(o, nullptr); + QCOMPARE_EQ(nullptr, o); + } + + void comparison_returnsTrue_whenComparingNotNullWithNullptr() + { + // MINGW may lack comparison operator + ComPtr o = makeComObject(); + QCOMPARE_NE(o, nullptr); + QCOMPARE_NE(nullptr, o); + } + + void lessThan_returnsEqualToRawPointerComparison() + { + // MINGW may lack comparison operator + { + ComPtr o1 = makeComObject(); + ComPtr o2 = makeComObject(); + QCOMPARE_EQ(o1 < o2, o1.Get() < o2.Get()); + } + { + ComPtr o1 = makeComObject(); + ComPtr o2 = makeComObject(); + QCOMPARE_EQ(o1 < o2, o1.Get() < o2.Get()); + } + { + //ComPtr o1 = makeComObject(); + //ComPtr o2 = makeComObject(); + //QCOMPARE_EQ(o1 < o2, o1.Get() < o2.Get()); // Intentionally fails to compile + } + } +}; + +QTEST_MAIN(tst_qcomptr) +#include "tst_qcomptr.moc" diff --git a/tests/auto/corelib/platform/windows/qcomvariant/tst_qcomvariant.cpp b/tests/auto/corelib/platform/windows/qcomvariant/tst_qcomvariant.cpp index 1e781101a85..01d12881f62 100644 --- a/tests/auto/corelib/platform/windows/qcomvariant/tst_qcomvariant.cpp +++ b/tests/auto/corelib/platform/windows/qcomvariant/tst_qcomvariant.cpp @@ -5,7 +5,7 @@ #include #include -#include +#include using Microsoft::WRL::ComPtr;