From 0a2b20333f265577ba6dd0ecae7c24fc31abaa61 Mon Sep 17 00:00:00 2001 From: Christian Heimlich Date: Wed, 9 Oct 2024 11:11:59 -0400 Subject: [PATCH] WIP --- CMakeLists.txt | 2 +- lib/core/src/qx-system_win.cpp | 6 +- lib/windows-gui/src/qx-taskbarbutton.cpp | 5 + lib/windows/CMakeLists.txt | 2 + lib/windows/include/qx_windows.h | 28 ++++- lib/windows/src/qx-common-windows.cpp | 125 ++++++++++------------- lib/windows/src/qx-common-windows_p.cpp | 67 ++++++++++++ lib/windows/src/qx-common-windows_p.h | 37 +++++++ lib/windows/src/qx-filedetails.cpp | 2 +- 9 files changed, 193 insertions(+), 81 deletions(-) create mode 100644 lib/windows/src/qx-common-windows_p.cpp create mode 100644 lib/windows/src/qx-common-windows_p.h diff --git a/CMakeLists.txt b/CMakeLists.txt index d1798826..fdd96290 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,7 +15,7 @@ project(Qx # Get helper scripts include(${CMAKE_CURRENT_SOURCE_DIR}/cmake/FetchOBCMake.cmake) -fetch_ob_cmake("v0.3.7") +fetch_ob_cmake("e7e1e091fd3b95dcd33e168f7faffa9d33d3b2a7") # Initialize project according to standard rules include(OB/Project) diff --git a/lib/core/src/qx-system_win.cpp b/lib/core/src/qx-system_win.cpp index b6fbe6fb..bb930a40 100644 --- a/lib/core/src/qx-system_win.cpp +++ b/lib/core/src/qx-system_win.cpp @@ -71,7 +71,7 @@ quint32 processId(QString processName) PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if(Process32First(snapshot, &entry)) { @@ -100,7 +100,7 @@ QString processName(quint32 processId) PROCESSENTRY32 entry; entry.dwSize = sizeof(PROCESSENTRY32); - HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL); + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if(Process32First(snapshot, &entry)) @@ -138,7 +138,7 @@ QList processChildren(quint32 processId, bool recursive) QList cPids = {processId}; // Initialize snapshot - if(HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, NULL)) + if(HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)) { // Initialize entry data holder PROCESSENTRY32 procEntry; diff --git a/lib/windows-gui/src/qx-taskbarbutton.cpp b/lib/windows-gui/src/qx-taskbarbutton.cpp index 1d99f2f9..8242e842 100644 --- a/lib/windows-gui/src/qx-taskbarbutton.cpp +++ b/lib/windows-gui/src/qx-taskbarbutton.cpp @@ -3,6 +3,11 @@ // Windows Includes #include "qx_windows.h" +#ifdef Q_CC_MINGW // MinGW headers for this are less fine-grained + #include "Shlobj.h" +#else + #include "ShlObj_core.h" +#endif // Intra-component Includes #include "qx/windows-gui/qx-winguievent.h" diff --git a/lib/windows/CMakeLists.txt b/lib/windows/CMakeLists.txt index 71d85c96..22d99349 100644 --- a/lib/windows/CMakeLists.txt +++ b/lib/windows/CMakeLists.txt @@ -6,6 +6,8 @@ qx_add_component("Windows" qx-windefs.h IMPLEMENTATION qx-common-windows.cpp + qx-common-windows_p.h + qx-common-windows_p.cpp qx-filedetails.cpp DOC_ONLY qx-windefs.dox diff --git a/lib/windows/include/qx_windows.h b/lib/windows/include/qx_windows.h index c44e5cb7..907f7648 100644 --- a/lib/windows/include/qx_windows.h +++ b/lib/windows/include/qx_windows.h @@ -1,4 +1,26 @@ -#define WIN32_LEAN_AND_MEAN -#define NOMINMAX +// ifdefs are to avoid macro redefinition warnings +#ifndef WIN32_LEAN_AND_MEAN + #define WIN32_LEAN_AND_MEAN +#endif +#ifndef NOATOM + #define NOATOM +#endif +#ifndef NOGDI + #define NOGDI +#endif +#ifndef NOMINMAX + #define NOMINMAX +#endif +#ifndef NOSOUND + #define NOSOUND +#endif +#ifndef NOHELP + #define NOHELP +#endif +#ifndef NOMCX + #define NOMCX +#endif + #include "windows.h" -#include "ShObjIdl_core.h" \ No newline at end of file + +// See windows.h for more "NOXXX" options diff --git a/lib/windows/src/qx-common-windows.cpp b/lib/windows/src/qx-common-windows.cpp index f9db5222..f95306a6 100644 --- a/lib/windows/src/qx-common-windows.cpp +++ b/lib/windows/src/qx-common-windows.cpp @@ -1,5 +1,6 @@ // Unit Includes #include "qx/windows/qx-common-windows.h" +#include "qx-common-windows_p.h" // Qt Includes #include @@ -9,12 +10,14 @@ // Windows Includes #include "qx_windows.h" #include "TlHelp32.h" -#include "comdef.h" -#include "ShlGuid.h" -#include "atlbase.h" +#ifdef Q_CC_MINGW // MinGW headers for this are less fine-grained + #include "Shlobj.h" +#else + #include "ShlObj_core.h" +#endif +#include // Extra-component Includes -#include "qx/core/qx-bitarray.h" #include "qx/core/qx-system.h" /*! @@ -380,6 +383,7 @@ SystemError getLastError() return SystemError::fromHresult(HRESULT_FROM_WIN32(error)); } + /*! * Creates a shortcut on the user's filesystem at the path @a shortcutPath, with the given * shortcut properties @a sp. @@ -390,83 +394,58 @@ SystemError createShortcut(QString shortcutPath, ShortcutProperties sp) if(sp.target.isEmpty() || shortcutPath.isEmpty() || sp.iconIndex < 0) return SystemError::fromHresult(E_INVALIDARG); - // Working vars - HRESULT hRes; - CComPtr ipShellLink; - - // Get full path of target - QFileInfo targetInfo(sp.target); - QString fullTargetPath = targetInfo.absoluteFilePath(); - - - // Get a pointer to the IShellLink interface - auto getIShellLinkPtr = [](CComPtr& ptrShellLnk){ - return CoCreateInstance(CLSID_ShellLink, - NULL, - CLSCTX_INPROC_SERVER, - IID_IShellLink, - (void**)&ptrShellLnk); - }; - - hRes = getIShellLinkPtr(ipShellLink); - - // Handle the case for if the COM server wasn't already initialized in this thread - QScopeGuard COMGuard([]{ CoUninitialize(); }); - if(hRes == CO_E_NOTINITIALIZED) + // Ensure COM is ready + ScopedCom com; + if(!com) + return com.error(); + + // Access IShelLink interface + using namespace Microsoft::WRL; + ComPtr ipShellLink; + HRESULT hRes = CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&ipShellLink)); + if(FAILED(hRes)) + return SystemError::fromHresult(hRes); + + // Get a pointer to the IPersistFile interface + ComPtr ipPersistFile; + hRes = ipShellLink.As(&ipPersistFile); + if(FAILED(hRes)) + return SystemError::fromHresult(hRes); + + // Set shortcut properties + QString tgtPath = QFileInfo(sp.target).absoluteFilePath(); + hRes = ipShellLink->SetPath((const wchar_t*)tgtPath.utf16()); + if(FAILED(hRes)) return SystemError::fromHresult(hRes); + + if(!sp.targetArgs.isEmpty()) { - CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE); - hRes = getIShellLinkPtr(ipShellLink); + hRes = ipShellLink->SetArguments((const wchar_t*)sp.targetArgs.utf16()); + if (FAILED(hRes)) return SystemError::fromHresult(hRes); } - else - COMGuard.dismiss(); - // Commence shortcut creation if COM server was properly connected to - if(SUCCEEDED(hRes)) + if(!sp.startIn.isEmpty()) { - // Get a pointer to the IPersistFile interface - CComQIPtr ipPersistFile(ipShellLink); - - // Set shortcut properties - hRes = ipShellLink->SetPath((const wchar_t*)fullTargetPath.utf16()); - if(FAILED(hRes)) - return SystemError::fromHresult(hRes); - - if(!sp.targetArgs.isEmpty()) - { - hRes = ipShellLink->SetArguments((const wchar_t*)sp.targetArgs.utf16()); - if (FAILED(hRes)) - return SystemError::fromHresult(hRes); - } - - if(!sp.startIn.isEmpty()) - { - hRes = ipShellLink->SetWorkingDirectory((const wchar_t*)sp.startIn.utf16()); - if (FAILED(hRes)) - return SystemError::fromHresult(hRes); - } - - if(!sp.comment.isEmpty()) - { - hRes = ipShellLink->SetDescription((const wchar_t*)sp.comment.utf16()); - if (FAILED(hRes)) - return SystemError::fromHresult(hRes); - } - - if(!sp.iconFilePath.isEmpty()) - { - hRes = ipShellLink->SetIconLocation((const wchar_t*)sp.iconFilePath.utf16(), sp.iconIndex); - if (FAILED(hRes)) - return SystemError::fromHresult(hRes); - } + hRes = ipShellLink->SetWorkingDirectory((const wchar_t*)sp.startIn.utf16()); + if (FAILED(hRes)) return SystemError::fromHresult(hRes); + } - hRes = ipShellLink->SetShowCmd(nativeShowMode(sp.showMode)); - if(FAILED(hRes)) - return SystemError::fromHresult(hRes); + if(!sp.comment.isEmpty()) + { + hRes = ipShellLink->SetDescription((const wchar_t*)sp.comment.utf16()); + if (FAILED(hRes)) return SystemError::fromHresult(hRes); + } - // Write the shortcut to disk - hRes = ipPersistFile->Save((const wchar_t*)shortcutPath.utf16(), TRUE); + if(!sp.iconFilePath.isEmpty()) + { + hRes = ipShellLink->SetIconLocation((const wchar_t*)sp.iconFilePath.utf16(), sp.iconIndex); + if (FAILED(hRes)) return SystemError::fromHresult(hRes); } + hRes = ipShellLink->SetShowCmd(nativeShowMode(sp.showMode)); + if(FAILED(hRes)) return SystemError::fromHresult(hRes); + + // Write the shortcut to disk + hRes = ipPersistFile->Save((const wchar_t*)shortcutPath.utf16(), TRUE); return SystemError::fromHresult(hRes); } diff --git a/lib/windows/src/qx-common-windows_p.cpp b/lib/windows/src/qx-common-windows_p.cpp new file mode 100644 index 00000000..156d6443 --- /dev/null +++ b/lib/windows/src/qx-common-windows_p.cpp @@ -0,0 +1,67 @@ +// Unit Includes +#include "qx-common-windows_p.h" + +// Qt Includes +#include +#include + +// Windows Includes +#include "combaseapi.h" + +namespace Qx +{ +//=============================================================================================================== +// ScopedCom +//=============================================================================================================== + +//-Constructor-------------------------------------------------------------------------------------------------- +//Public: +ScopedCom::ScopedCom() : + mThreadId(GetCurrentThreadId()), + mCleanup(false) +{ + // Check if COM is initialized (parameter return values are ignored) + APTTYPE aptType; + APTTYPEQUALIFIER aptTypeQualifier; + HRESULT hRes = CoGetApartmentType(&aptType, &aptTypeQualifier); + if(SUCCEEDED(hRes)) + return; // COM is ready, do nothing + else if(hRes != CO_E_NOTINITIALIZED) + { + // True error + mError = SystemError::fromHresult(hRes); + return; + } + + // Init COM (shouldn't ever be needed in the main thread, but we check just in-case) + auto app = QCoreApplication::instance(); + bool inMainThread = app && app->thread() == QThread::currentThread(); // TODO: Use Qt 6.8 isMainThread() + int tm = (inMainThread ? COINIT_APARTMENTTHREADED : COINIT_MULTITHREADED) | COINIT_DISABLE_OLE1DDE; + hRes = CoInitializeEx(NULL, tm); + if(!SUCCEEDED(hRes)) // Should never fail, but hey... + { + mError = SystemError::fromHresult(hRes); + return; + } + + mCleanup = true; +} + +//-Destructor-------------------------------------------------------------------------------------------------- +//Public: +ScopedCom::~ScopedCom() +{ + Q_ASSERT(mThreadId == GetCurrentThreadId()); + if(mCleanup) + CoUninitialize(); +} + +//-Instance Functions-------------------------------------------------------------------------------------------- +//Public: +bool ScopedCom::hasError() const { return mError.isValid(); } +SystemError ScopedCom::error() const { return mError; } + +//-Operators-------------------------------------------------------------------------------------------- +//Public: +ScopedCom::operator bool() const { return !hasError(); } +} diff --git a/lib/windows/src/qx-common-windows_p.h b/lib/windows/src/qx-common-windows_p.h new file mode 100644 index 00000000..09669584 --- /dev/null +++ b/lib/windows/src/qx-common-windows_p.h @@ -0,0 +1,37 @@ +#ifndef QX_WINDOWS_COMMON_P_H +#define QX_WINDOWS_COMMON_P_H + +// Qt Includes +#include + +// Inter-component Includes +#include "qx/windows/qx-windefs.h" + +// Extra-component Includes +#include "qx/core/qx-systemerror.h" + +namespace Qx +{ + +// Makes sure COM is initialized, and cleans up on deletion. +// Based on QComHelper, but slightly more flexible in that it just cares COM is initialized one way or another. +class ScopedCom +{ + Q_DISABLE_COPY_MOVE(ScopedCom); +private: + SystemError mError; + DWORD mThreadId; // For safety check + bool mCleanup; + +public: + ScopedCom(); + ~ScopedCom(); + + bool hasError() const; + SystemError error() const; + explicit operator bool() const; +}; + + +} +#endif // QX_WINDOWS_COMMON_P_H diff --git a/lib/windows/src/qx-filedetails.cpp b/lib/windows/src/qx-filedetails.cpp index ea8abb2e..fd9bd5f7 100644 --- a/lib/windows/src/qx-filedetails.cpp +++ b/lib/windows/src/qx-filedetails.cpp @@ -225,7 +225,7 @@ FileDetails FileDetails::readFileDetails(QString filePath) { DWORD verInfoHandle, verInfoSize = GetFileVersionInfoSize((const wchar_t*)filePath.utf16(), &verInfoHandle); - if (verInfoSize != NULL) + if (verInfoSize != 0) { LPSTR verInfo = new char[verInfoSize];