Add QComHelper class for dealing with COM on Windows

Unifies our approach to calling CoInitializeEx and CoUninitialize,
removing a lot of boilerplate in the process, and also fixes a few
bugs where we would incorrectly balance our calls to CoInitializeEx
and CoUninitialize.

The optimistic approach of qfilesystemengine_win.cpp of calling
CoCreateInstance without initializing the COM library explicitly
has been removed, as calling CoInitializeEx should be a noop in
the situation where it's already been loaded.

Change-Id: I9e2ec101678c2ebb9946504b5e8034e58f1bb56a
Reviewed-by: Oliver Wolff <oliver.wolff@qt.io>
This commit is contained in:
Tor Arne Vestbø 2022-08-25 10:15:16 +02:00
parent 49f9cadb52
commit 1bc7e9e77b
11 changed files with 87 additions and 67 deletions

View File

@ -538,7 +538,7 @@ qt_internal_extend_target(Core CONDITION WIN32
kernel/qcoreapplication_win.cpp kernel/qcoreapplication_win.cpp
kernel/qelapsedtimer_win.cpp kernel/qelapsedtimer_win.cpp
kernel/qeventdispatcher_win.cpp kernel/qeventdispatcher_win_p.h kernel/qeventdispatcher_win.cpp kernel/qeventdispatcher_win_p.h
kernel/qfunctions_win_p.h kernel/qfunctions_winrt_p.h kernel/qfunctions_win.cpp kernel/qfunctions_win_p.h kernel/qfunctions_winrt_p.h
kernel/qsharedmemory_win.cpp kernel/qsharedmemory_win.cpp
kernel/qsystemsemaphore_win.cpp kernel/qsystemsemaphore_win.cpp
kernel/qwineventnotifier.cpp kernel/qwineventnotifier.h kernel/qwineventnotifier_p.h kernel/qwineventnotifier.cpp kernel/qwineventnotifier.h kernel/qwineventnotifier_p.h

View File

@ -37,6 +37,8 @@
#define SECURITY_WIN32 #define SECURITY_WIN32
#include <security.h> #include <security.h>
#include <QtCore/private/qfunctions_win_p.h>
#ifndef SPI_GETPLATFORMTYPE #ifndef SPI_GETPLATFORMTYPE
#define SPI_GETPLATFORMTYPE 257 #define SPI_GETPLATFORMTYPE 257
#endif #endif
@ -668,21 +670,16 @@ static QString readLink(const QFileSystemEntry &link)
#if QT_CONFIG(fslibs) #if QT_CONFIG(fslibs)
QString ret; QString ret;
bool neededCoInit = false;
IShellLink *psl; // pointer to IShellLink i/f IShellLink *psl; // pointer to IShellLink i/f
WIN32_FIND_DATA wfd; WIN32_FIND_DATA wfd;
wchar_t szGotPath[MAX_PATH]; wchar_t szGotPath[MAX_PATH];
QComHelper comHelper;
// Get pointer to the IShellLink interface. // Get pointer to the IShellLink interface.
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink,
(LPVOID *)&psl); (LPVOID *)&psl);
if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized
neededCoInit = true;
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink,
(LPVOID *)&psl);
}
if (SUCCEEDED(hres)) { // Get pointer to the IPersistFile interface. if (SUCCEEDED(hres)) { // Get pointer to the IPersistFile interface.
IPersistFile *ppf; IPersistFile *ppf;
hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf); hres = psl->QueryInterface(IID_IPersistFile, (LPVOID *)&ppf);
@ -698,8 +695,6 @@ static QString readLink(const QFileSystemEntry &link)
} }
psl->Release(); psl->Release();
} }
if (neededCoInit)
CoUninitialize();
return ret; return ret;
#else #else
@ -1661,18 +1656,11 @@ bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSy
QSystemError &error) QSystemError &error)
{ {
bool ret = false; bool ret = false;
QComHelper comHelper;
IShellLink *psl = nullptr; IShellLink *psl = nullptr;
HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink, HRESULT hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink,
reinterpret_cast<void **>(&psl)); reinterpret_cast<void **>(&psl));
bool neededCoInit = false;
if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized
neededCoInit = true;
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
hres = CoCreateInstance(CLSID_ShellLink, nullptr, CLSCTX_INPROC_SERVER, IID_IShellLink,
reinterpret_cast<void **>(&psl));
}
if (SUCCEEDED(hres)) { if (SUCCEEDED(hres)) {
const auto name = QDir::toNativeSeparators(source.filePath()); const auto name = QDir::toNativeSeparators(source.filePath());
const auto pathName = QDir::toNativeSeparators(source.path()); const auto pathName = QDir::toNativeSeparators(source.path());
@ -1692,9 +1680,6 @@ bool QFileSystemEngine::createLink(const QFileSystemEntry &source, const QFileSy
if (!ret) if (!ret)
error = QSystemError(::GetLastError(), QSystemError::NativeError); error = QSystemError(::GetLastError(), QSystemError::NativeError);
if (neededCoInit)
CoUninitialize();
return ret; return ret;
} }
@ -1762,7 +1747,8 @@ bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source,
// we need the "display name" of the file, so can't use nativeAbsoluteFilePath // we need the "display name" of the file, so can't use nativeAbsoluteFilePath
const QString sourcePath = QDir::toNativeSeparators(absoluteName(source).filePath()); const QString sourcePath = QDir::toNativeSeparators(absoluteName(source).filePath());
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); QComHelper comHelper;
IFileOperation *pfo = nullptr; IFileOperation *pfo = nullptr;
IShellItem *deleteItem = nullptr; IShellItem *deleteItem = nullptr;
FileOperationProgressSink *sink = nullptr; FileOperationProgressSink *sink = nullptr;
@ -1775,7 +1761,6 @@ bool QFileSystemEngine::moveFileToTrash(const QFileSystemEntry &source,
deleteItem->Release(); deleteItem->Release();
if (pfo) if (pfo)
pfo->Release(); pfo->Release();
CoUninitialize();
if (!SUCCEEDED(hres)) if (!SUCCEEDED(hres))
error = QSystemError(hres, QSystemError::NativeError); error = QSystemError(hres, QSystemError::NativeError);
}); });

View File

@ -0,0 +1,28 @@
// Copyright (C) 2022 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include "qfunctions_win_p.h"
#include <combaseapi.h>
#include <objbase.h>
QT_BEGIN_NAMESPACE
QComHelper::QComHelper(COINIT concurrencyModel)
{
// Avoid overhead of initializing and using obsolete technology
concurrencyModel = COINIT(concurrencyModel | COINIT_DISABLE_OLE1DDE);
m_initResult = CoInitializeEx(nullptr, concurrencyModel);
if (FAILED(m_initResult))
qErrnoWarning(m_initResult, "Failed to initialize COM library");
}
QComHelper::~QComHelper()
{
if (SUCCEEDED(m_initResult))
CoUninitialize();
}
QT_END_NAMESPACE

View File

@ -15,9 +15,33 @@
// We mean it. // We mean it.
// //
#include <QtCore/private/qglobal_p.h>
#if defined(Q_OS_WIN) #if defined(Q_OS_WIN)
#if !defined(QT_BOOTSTRAPPED)
#include <QtCore/private/qfunctions_winrt_p.h> #include <QtCore/private/qfunctions_winrt_p.h>
#endif
#include <QtCore/qt_windows.h>
QT_BEGIN_NAMESPACE
class Q_CORE_EXPORT QComHelper
{
Q_DISABLE_COPY_MOVE(QComHelper)
public:
QComHelper(COINIT concurrencyModel = COINIT_APARTMENTTHREADED);
~QComHelper();
bool isValid() const { return SUCCEEDED(m_initResult); }
explicit operator bool() const { return isValid(); }
private:
HRESULT m_initResult = E_FAIL;
};
QT_END_NAMESPACE
#endif // Q_OS_WIN #endif // Q_OS_WIN

View File

@ -8,6 +8,8 @@
#include <QtCore/quuid.h> #include <QtCore/quuid.h>
#include <QtCore/qmetaobject.h> #include <QtCore/qmetaobject.h>
#include <QtCore/private/qfunctions_win_p.h>
#include <QtNetwork/qnetworkinterface.h> #include <QtNetwork/qnetworkinterface.h>
#include <objbase.h> #include <objbase.h>
@ -124,6 +126,8 @@ public:
void setConnectivity(NLM_CONNECTIVITY newConnectivity); void setConnectivity(NLM_CONNECTIVITY newConnectivity);
private: private:
QComHelper comHelper;
ComPtr<QNetworkConnectionEvents> connectionEvents; ComPtr<QNetworkConnectionEvents> connectionEvents;
// We can assume we have access to internet/subnet when this class is created because // We can assume we have access to internet/subnet when this class is created because
// connection has already been established to the peer: // connection has already been established to the peer:
@ -136,7 +140,6 @@ private:
bool sameSubnet = false; bool sameSubnet = false;
bool isLinkLocal = false; bool isLinkLocal = false;
bool monitoring = false; bool monitoring = false;
bool comInitFailed = false;
bool remoteIsIPv6 = false; bool remoteIsIPv6 = false;
}; };
@ -299,30 +302,25 @@ bool QNetworkConnectionEvents::stopMonitoring()
QNetworkConnectionMonitorPrivate::QNetworkConnectionMonitorPrivate() QNetworkConnectionMonitorPrivate::QNetworkConnectionMonitorPrivate()
{ {
auto hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); if (!comHelper.isValid())
if (FAILED(hr)) {
qCDebug(lcNetMon) << "Failed to initialize COM:" << errorStringFromHResult(hr);
comInitFailed = true;
return; return;
}
connectionEvents = new QNetworkConnectionEvents(this); connectionEvents = new QNetworkConnectionEvents(this);
} }
QNetworkConnectionMonitorPrivate::~QNetworkConnectionMonitorPrivate() QNetworkConnectionMonitorPrivate::~QNetworkConnectionMonitorPrivate()
{ {
if (comInitFailed) if (!comHelper.isValid())
return; return;
if (monitoring) if (monitoring)
stopMonitoring(); stopMonitoring();
connectionEvents.Reset(); connectionEvents.Reset();
CoUninitialize();
} }
bool QNetworkConnectionMonitorPrivate::setTargets(const QHostAddress &local, bool QNetworkConnectionMonitorPrivate::setTargets(const QHostAddress &local,
const QHostAddress &remote) const QHostAddress &remote)
{ {
if (comInitFailed) if (!comHelper.isValid())
return false; return false;
QNetworkInterface iface = getInterfaceFromHostAddress(local); QNetworkInterface iface = getInterfaceFromHostAddress(local);

View File

@ -9,6 +9,8 @@
#include <QtCore/private/qobject_p.h> #include <QtCore/private/qobject_p.h>
#include <QtCore/qscopeguard.h> #include <QtCore/qscopeguard.h>
#include <QtCore/private/qfunctions_win_p.h>
QT_BEGIN_NAMESPACE QT_BEGIN_NAMESPACE
// Declared in qnetworklistmanagerevents.h // Declared in qnetworklistmanagerevents.h
@ -82,12 +84,13 @@ private:
void setConnectivity(NLM_CONNECTIVITY newConnectivity); void setConnectivity(NLM_CONNECTIVITY newConnectivity);
void checkCaptivePortal(); void checkCaptivePortal();
QComHelper comHelper;
ComPtr<QNetworkListManagerEvents> managerEvents; ComPtr<QNetworkListManagerEvents> managerEvents;
NLM_CONNECTIVITY connectivity = NLM_CONNECTIVITY_DISCONNECTED; NLM_CONNECTIVITY connectivity = NLM_CONNECTIVITY_DISCONNECTED;
bool monitoring = false; bool monitoring = false;
bool comInitFailed = false;
}; };
class QNetworkListManagerNetworkInformationBackendFactory : public QNetworkInformationBackendFactory class QNetworkListManagerNetworkInformationBackendFactory : public QNetworkInformationBackendFactory
@ -121,12 +124,9 @@ public:
QNetworkListManagerNetworkInformationBackend::QNetworkListManagerNetworkInformationBackend() QNetworkListManagerNetworkInformationBackend::QNetworkListManagerNetworkInformationBackend()
{ {
auto hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); if (!comHelper.isValid())
if (FAILED(hr)) {
qCWarning(lcNetInfoNLM) << "Failed to initialize COM:" << errorStringFromHResult(hr);
comInitFailed = true;
return; return;
}
managerEvents = new QNetworkListManagerEvents(); managerEvents = new QNetworkListManagerEvents();
connect(managerEvents.Get(), &QNetworkListManagerEvents::connectivityChanged, this, connect(managerEvents.Get(), &QNetworkListManagerEvents::connectivityChanged, this,
&QNetworkListManagerNetworkInformationBackend::setConnectivity); &QNetworkListManagerNetworkInformationBackend::setConnectivity);
@ -140,10 +140,7 @@ QNetworkListManagerNetworkInformationBackend::QNetworkListManagerNetworkInformat
QNetworkListManagerNetworkInformationBackend::~QNetworkListManagerNetworkInformationBackend() QNetworkListManagerNetworkInformationBackend::~QNetworkListManagerNetworkInformationBackend()
{ {
if (comInitFailed)
return;
stop(); stop();
CoUninitialize();
} }
void QNetworkListManagerNetworkInformationBackend::setConnectivity(NLM_CONNECTIVITY newConnectivity) void QNetworkListManagerNetworkInformationBackend::setConnectivity(NLM_CONNECTIVITY newConnectivity)
@ -175,7 +172,7 @@ bool QNetworkListManagerNetworkInformationBackend::start()
{ {
Q_ASSERT(!monitoring); Q_ASSERT(!monitoring);
if (comInitFailed) if (!comHelper.isValid())
return false; return false;
if (!managerEvents) if (!managerEvents)

View File

@ -12,6 +12,7 @@
#include <QtCore/qthread.h> #include <QtCore/qthread.h>
#include <QtCore/private/qwinregistry_p.h> #include <QtCore/private/qwinregistry_p.h>
#include <QtCore/private/qfunctions_win_p.h>
#include <shlobj.h> #include <shlobj.h>
#include <shlwapi.h> #include <shlwapi.h>
@ -34,11 +35,10 @@ public:
void run() override void run() override
{ {
if (SUCCEEDED(CoInitializeEx(NULL, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE))) { QComHelper comHelper;
if (comHelper.isValid())
m_result = ShellExecute(nullptr, m_operation, m_file, m_parameters, nullptr, m_result = ShellExecute(nullptr, m_operation, m_file, m_parameters, nullptr,
SW_SHOWNORMAL); SW_SHOWNORMAL);
CoUninitialize();
}
} }
HINSTANCE result() const { return m_result; } HINSTANCE result() const { return m_result; }

View File

@ -43,6 +43,7 @@
#include <private/qhighdpiscaling_p.h> #include <private/qhighdpiscaling_p.h>
#include <private/qsystemlibrary_p.h> #include <private/qsystemlibrary_p.h>
#include <private/qwinregistry_p.h> #include <private/qwinregistry_p.h>
#include <QtCore/private/qfunctions_win_p.h>
#include <algorithm> #include <algorithm>
@ -145,7 +146,7 @@ public:
void run() override void run() override
{ {
m_init = CoInitializeEx(nullptr, COINIT_MULTITHREADED); QComHelper comHelper(COINIT_MULTITHREADED);
QMutexLocker readyLocker(&m_readyMutex); QMutexLocker readyLocker(&m_readyMutex);
while (!m_cancelled.loadRelaxed()) { while (!m_cancelled.loadRelaxed()) {
@ -170,9 +171,6 @@ public:
m_doneMutex.unlock(); m_doneMutex.unlock();
} }
} }
if (m_init != S_FALSE)
CoUninitialize();
} }
bool runWithParams(QShGetFileInfoParams *params, qint64 timeOutMSecs) bool runWithParams(QShGetFileInfoParams *params, qint64 timeOutMSecs)
@ -195,7 +193,6 @@ public:
} }
private: private:
HRESULT m_init;
QShGetFileInfoParams *m_params; QShGetFileInfoParams *m_params;
QAtomicInt m_cancelled; QAtomicInt m_cancelled;
QWaitCondition m_readyCondition; QWaitCondition m_readyCondition;
@ -980,10 +977,7 @@ QString QWindowsFileIconEngine::cacheKey() const
QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon::State) QPixmap QWindowsFileIconEngine::filePixmap(const QSize &size, QIcon::Mode, QIcon::State)
{ {
/* We don't use the variable, but by storing it statically, we QComHelper comHelper;
* ensure CoInitialize is only called once. */
static HRESULT comInit = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
Q_UNUSED(comInit);
static QCache<QString, FakePointer<int> > dirIconEntryCache(1000); static QCache<QString, FakePointer<int> > dirIconEntryCache(1000);
static QMutex mx; static QMutex mx;

View File

@ -136,6 +136,7 @@ qt_internal_extend_target(Bootstrap CONDITION WIN32
../../corelib/kernel/qcoreapplication_win.cpp ../../corelib/kernel/qcoreapplication_win.cpp
../../corelib/kernel/qwinregistry.cpp ../../corelib/kernel/qwinregistry.cpp
../../corelib/plugin/qsystemlibrary.cpp ../../corelib/plugin/qsystemlibrary.cpp
../../corelib/kernel/qfunctions_win.cpp
PUBLIC_LIBRARIES PUBLIC_LIBRARIES
advapi32 advapi32
netapi32 netapi32

View File

@ -22,6 +22,7 @@
#include <private/qabstractfileengine_p.h> #include <private/qabstractfileengine_p.h>
#include <private/qfsfileengine_p.h> #include <private/qfsfileengine_p.h>
#include <private/qfilesystemengine_p.h> #include <private/qfilesystemengine_p.h>
#include <QtCore/private/qfunctions_win_p.h>
#include <QtTest/private/qemulationdetector_p.h> #include <QtTest/private/qemulationdetector_p.h>
@ -1540,13 +1541,9 @@ static QString getWorkingDirectoryForLink(const QString &linkFileName)
bool neededCoInit = false; bool neededCoInit = false;
QString ret; QString ret;
QComHelper comHelper;
IShellLink *psl; IShellLink *psl;
HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl); HRESULT hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
if (hres == CO_E_NOTINITIALIZED) { // COM was not initialized
neededCoInit = true;
CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE);
hres = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, (void **)&psl);
}
if (SUCCEEDED(hres)) { // Get pointer to the IPersistFile interface. if (SUCCEEDED(hres)) { // Get pointer to the IPersistFile interface.
IPersistFile *ppf; IPersistFile *ppf;
@ -1565,10 +1562,6 @@ static QString getWorkingDirectoryForLink(const QString &linkFileName)
psl->Release(); psl->Release();
} }
if (neededCoInit) {
CoUninitialize();
}
return ret; return ret;
} }
#endif #endif

View File

@ -18,6 +18,7 @@
#include <qpa/qplatformnativeinterface.h> #include <qpa/qplatformnativeinterface.h>
#include <qpa/qplatformintegration.h> #include <qpa/qplatformintegration.h>
#include <qpa/qplatformaccessibility.h> #include <qpa/qplatformaccessibility.h>
#include <QtCore/private/qfunctions_win_p.h>
#include <QtGui/private/qguiapplication_p.h> #include <QtGui/private/qguiapplication_p.h>
#include <QtGui/private/qhighdpiscaling_p.h> #include <QtGui/private/qhighdpiscaling_p.h>
@ -3846,14 +3847,14 @@ void tst_QAccessibility::bridgeTest()
POINT pt{nativePos.x(), nativePos.y()}; POINT pt{nativePos.x(), nativePos.y()};
// Initialize COM stuff. // Initialize COM stuff.
HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); QComHelper comHelper;
QVERIFY(SUCCEEDED(hr)); QVERIFY(comHelper.isValid());
// Get UI Automation interface. // Get UI Automation interface.
const GUID CLSID_CUIAutomation_test{0xff48dba4, 0x60ef, 0x4201, const GUID CLSID_CUIAutomation_test{0xff48dba4, 0x60ef, 0x4201,
{0xaa,0x87, 0x54,0x10,0x3e,0xef,0x59,0x4e}}; {0xaa,0x87, 0x54,0x10,0x3e,0xef,0x59,0x4e}};
IUIAutomation *automation = nullptr; IUIAutomation *automation = nullptr;
hr = CoCreateInstance(CLSID_CUIAutomation_test, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&automation)); HRESULT hr = CoCreateInstance(CLSID_CUIAutomation_test, nullptr, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&automation));
QVERIFY(SUCCEEDED(hr)); QVERIFY(SUCCEEDED(hr));
// Get button element from UI Automation using point. // Get button element from UI Automation using point.
@ -3956,7 +3957,6 @@ void tst_QAccessibility::bridgeTest()
controlWalker->Release(); controlWalker->Release();
windowElement->Release(); windowElement->Release();
automation->Release(); automation->Release();
CoUninitialize();
QTestAccessibility::clearEvents(); QTestAccessibility::clearEvents();
#endif #endif