Krita Source Code Documentation
Loading...
Searching...
No Matches
KisWindowsPackageUtils.cpp
Go to the documentation of this file.
1/*
2 * SPDX-FileCopyrightText: 2022 Alvin Wong <alvin@alvinhc.com>
3 * SPDX-FileCopyrightText: 2022 L. E. Segovia <amy@amyspark.me>
4 *
5 * SPDX-License-Identifier: GPL-3.0-or-later
6 */
7
9
10#include <array>
11
12// XXX: needs to go first because under MinGW
13// clangd gets really confused and errors on missing
14// definition of WINAPI_FAMILY_PARTITION
15#include <windows.h>
16
17#if defined __has_include
18#if __has_include(<appmodel.h>)
19#include <appmodel.h>
20#define HAS_APPMODEL_H
21#endif
22#endif
23
24#if defined HAS_APPMODEL_H
25// ---
26// GetCurrentPackageFamilyName
27// appmodel.h / Kernel32.dll / Windows 8
28// ---
29using pGetCurrentPackageFamilyName_t = decltype(&GetCurrentPackageFamilyName);
30
31// ---
32// GetCurrentPackageFullName
33// appmodel.h / Kernel32.dll / Windows 8
34// ---
35using pGetCurrentPackageFullName_t = decltype(&GetCurrentPackageFullName);
36#else
37// ---
38// GetCurrentPackageFamilyName
39// appmodel.h / Kernel32.dll / Windows 8
40// ---
41using pGetCurrentPackageFamilyName_t = LONG(WINAPI *)(UINT32 *packageFamilyNameLength, PWSTR packageFamilyName);
42
43// ---
44// GetCurrentPackageFullName
45// appmodel.h / Kernel32.dll / Windows 8
46// ---
47using pGetCurrentPackageFullName_t = LONG(WINAPI *)(UINT32 *packageFullNameLength, PWSTR packageFullName);
48#endif
49
50#include <shlobj.h>
51
52#include <QDebug>
53#include <QLibrary>
54#include <QString>
55
56#ifndef PACKAGE_FULL_NAME_MAX_LENGTH
57constexpr int PACKAGE_FULL_NAME_MAX_LENGTH = 127;
58#endif
59
60#ifndef APPMODEL_ERROR_NO_PACKAGE
61constexpr LONG APPMODEL_ERROR_NO_PACKAGE = 15700;
62#endif
63
64// Flag for `KNOWN_FOLDER_FLAG`, introduced in Win 10 ver 1703, which when
65// used within a Desktop Bridge process, will cause the API to return the
66// redirected target of the locations.
67//
68// ---
69// KF_FLAG_RETURN_FILTER_REDIRECTION_TARGET
70// shlobj_core.h / Windows 10 v1703
71// ---
72#ifndef KF_FLAG_RETURN_FILTER_REDIRECTION_TARGET
74#endif
75
79 QLibrary dllKernel32;
80
81 template<typename T, typename U>
82 inline T cast_to_function(U v) noexcept
83 {
84 return reinterpret_cast<T>(reinterpret_cast<void *>(v));
85 }
86
88 {
89 static const AppmodelFunctions s{};
90 return s;
91 }
92
94 : dllKernel32("kernel32.dll")
95 {
97 cast_to_function<pGetCurrentPackageFamilyName_t>(dllKernel32.resolve("GetCurrentPackageFamilyName"));
99 cast_to_function<pGetCurrentPackageFullName_t>(dllKernel32.resolve("GetCurrentPackageFullName"));
100 }
101
103};
104
106{
107
109{
110 return tryGetCurrentPackageFamilyName(nullptr);
111}
112
113bool tryGetCurrentPackageFamilyName(QString *outName)
114{
115 if (!AppmodelFunctions::instance().getCurrentPackageFamilyName) {
116 // We are probably on Windows 7 or earlier.
117 return false;
118 }
119
120 std::array<WCHAR, PACKAGE_FULL_NAME_MAX_LENGTH + 1> name{}; // includes null terminator
121 UINT32 nameLength = name.size();
122 const LONG result = AppmodelFunctions::instance().getCurrentPackageFamilyName(&nameLength, name.data());
123 if (result == APPMODEL_ERROR_NO_PACKAGE) {
124 // Process not running from a package.
125 return false;
126 }
127 if (result == ERROR_INSUFFICIENT_BUFFER) {
128 // This shouldn't happen!
129 qWarning() << "GetCurrentPackageFamilyName returned "
130 "ERROR_INSUFFICIENT_BUFFER, required length is"
131 << nameLength;
132 if (outName) {
133 *outName = QString();
134 }
135 return true;
136 }
137 if (result != ERROR_SUCCESS) {
138 qWarning() << "GetCurrentPackageFamilyName returned unexpected error code:" << result;
139 return false;
140 }
141
142 if (outName) {
143 // Sanity check
144 if (nameLength > name.size()) {
145 qWarning() << "GetCurrentPackageFamilyName returned a length "
146 "exceeding the buffer size:"
147 << nameLength;
148 nameLength = name.size();
149 }
150 // Exclude null terminator
151 if (nameLength > 0 && name.at(nameLength - 1) == L'\0') {
152 nameLength -= 1;
153 }
154 *outName = QString::fromWCharArray(name.data(), static_cast<int>(nameLength));
155 }
156 return true;
157}
158
159bool tryGetCurrentPackageFullName(QString *outName)
160{
161 if (!AppmodelFunctions::instance().getCurrentPackageFullName) {
162 // We are probably on Windows 7 or earlier.
163 return false;
164 }
165
166 std::array<WCHAR, PACKAGE_FULL_NAME_MAX_LENGTH + 1> name{}; // includes null terminator
167 UINT32 nameLength = name.size();
168 const LONG result = AppmodelFunctions::instance().getCurrentPackageFullName(&nameLength, name.data());
169 if (result == APPMODEL_ERROR_NO_PACKAGE) {
170 // Process not running from a package.
171 return false;
172 }
173 if (result == ERROR_INSUFFICIENT_BUFFER) {
174 // This shouldn't happen!
175 qWarning() << "GetCurrentPackageFullName returned "
176 "ERROR_INSUFFICIENT_BUFFER, required length is"
177 << nameLength;
178 if (outName) {
179 *outName = QString();
180 }
181 return true;
182 }
183 if (result != ERROR_SUCCESS) {
184 qWarning() << "GetCurrentPackageFullName returned unexpected error code:" << result;
185 return false;
186 }
187
188 if (outName) {
189 // Sanity check
190 if (nameLength > name.size()) {
191 qWarning() << "GetCurrentPackageFullName returned a length "
192 "exceeding the buffer size:"
193 << nameLength;
194 nameLength = name.size();
195 }
196 // Exclude null terminator
197 if (nameLength > 0 && name.at(nameLength - 1) == L'\0') {
198 nameLength -= 1;
199 }
200 *outName = QString::fromWCharArray(name.data(), static_cast<int>(nameLength));
201 }
202 return true;
203}
204
206{
207 PWSTR path = nullptr;
208 HRESULT result =
209 SHGetKnownFolderPath(FOLDERID_RoamingAppData, KF_FLAG_RETURN_FILTER_REDIRECTION_TARGET, nullptr, &path);
210 if (result != S_OK) {
211 qWarning() << "SHGetKnownFolderPath returned error HRESULT:" << result;
212 return {};
213 }
214 if (!path) {
215 qWarning() << "SHGetKnownFolderPath did not return a path";
216 return {};
217 }
218 QString appData = QString::fromWCharArray(path);
219 CoTaskMemFree(path);
220 return appData;
221}
222
223} /* namespace KisWindowsPackageUtils */
qreal v
LONG(WINAPI *)(UINT32 *packageFamilyNameLength, PWSTR packageFamilyName) pGetCurrentPackageFamilyName_t
LONG(WINAPI *)(UINT32 *packageFullNameLength, PWSTR packageFullName) pGetCurrentPackageFullName_t
constexpr LONG APPMODEL_ERROR_NO_PACKAGE
constexpr int KF_FLAG_RETURN_FILTER_REDIRECTION_TARGET
constexpr int PACKAGE_FULL_NAME_MAX_LENGTH
bool tryGetCurrentPackageFullName(QString *outName)
bool tryGetCurrentPackageFamilyName(QString *outName)
pGetCurrentPackageFamilyName_t getCurrentPackageFamilyName
~AppmodelFunctions()=default
static const AppmodelFunctions & instance()
T cast_to_function(U v) noexcept
pGetCurrentPackageFullName_t getCurrentPackageFullName