File indexing completed on 2024-05-12 15:57:04
0001 /* 0002 * SPDX-FileCopyrightText: 2022 Alvin Wong <alvin@alvinhc.com> 0003 * 0004 * SPDX-License-Identifier: GPL-3.0-or-later 0005 */ 0006 0007 // Get Windows Vista API 0008 #if defined(WINVER) && WINVER < 0x0600 0009 # undef WINVER 0010 #endif 0011 #if defined(_WIN32_WINNT) && _WIN32_WINNT < 0x0600 0012 # undef _WIN32_WINNT 0013 #endif 0014 #ifndef WINVER 0015 # define WINVER 0x0600 0016 #endif 0017 #ifndef _WIN32_WINNT 0018 # define _WIN32_WINNT 0x0600 0019 #endif 0020 0021 #include "KisWindowsPackageUtils.h" 0022 0023 #include <windows.h> 0024 #include <Shlobj.h> 0025 0026 #include <QDebug> 0027 #include <QString> 0028 0029 0030 constexpr int appmodel_PACKAGE_FULL_NAME_MAX_LENGTH = 127; 0031 0032 constexpr LONG winerror_APPMODEL_ERROR_NO_PACKAGE = 15700; 0033 0034 // --- 0035 // GetCurrentPackageFamilyName 0036 // appmodel.h / Kernel32.dll / Windows 8 0037 // --- 0038 typedef LONG (WINAPI *pGetCurrentPackageFamilyName_t)( 0039 UINT32 *packageFamilyNameLength, 0040 PWSTR packageFamilyName 0041 ); 0042 0043 // --- 0044 // GetCurrentPackageFullName 0045 // appmodel.h / Kernel32.dll / Windows 8 0046 // --- 0047 typedef LONG (WINAPI *pGetCurrentPackageFullName_t)( 0048 UINT32 *packageFullNameLength, 0049 PWSTR packageFullName 0050 ); 0051 0052 // Flag for `KNOWN_FOLDER_FLAG`, introduced in Win 10 ver 1709, which when 0053 // used with `FOLDERID_LocalAppData` or `FOLDERID_RoamingAppData` when calling 0054 // `SHGetKnownFolderPath`, will return the private app location of the 0055 // packaged app if the current process is a packaged app. 0056 // 0057 // It should return the same values as the `LocalFolder` or `RoamingFolder` 0058 // property of the `Windows.Storage.ApplicationData.Current` UWP API. 0059 // 0060 // --- 0061 // KF_FLAG_FORCE_APP_DATA_REDIRECTION 0062 // shlobj_core.h / Windows 10 v1709 0063 // --- 0064 constexpr int shlobj_KF_FLAG_FORCE_APP_DATA_REDIRECTION = 0x00080000; 0065 0066 // Flag for `KNOWN_FOLDER_FLAG`, introduced in Win 10 ver 1703, which when 0067 // used within a Desktop Bridge process, will cause the API to return the 0068 // redirected target of the locations. 0069 // 0070 // --- 0071 // KF_FLAG_RETURN_FILTER_REDIRECTION_TARGET 0072 // shlobj_core.h / Windows 10 v1703 0073 // --- 0074 constexpr int shlobj_KF_FLAG_RETURN_FILTER_REDIRECTION_TARGET = 0x00040000; 0075 0076 struct AppmodelFunctions { 0077 pGetCurrentPackageFamilyName_t pGetCurrentPackageFamilyName; 0078 pGetCurrentPackageFullName_t pGetCurrentPackageFullName; 0079 0080 AppmodelFunctions() { 0081 HMODULE dllKernel32 = LoadLibraryW(L"kernel32.dll"); 0082 pGetCurrentPackageFamilyName = reinterpret_cast<pGetCurrentPackageFamilyName_t>( 0083 GetProcAddress(dllKernel32, "GetCurrentPackageFamilyName")); 0084 pGetCurrentPackageFullName = reinterpret_cast<pGetCurrentPackageFullName_t>( 0085 GetProcAddress(dllKernel32, "GetCurrentPackageFullName")); 0086 } 0087 }; 0088 0089 static const AppmodelFunctions &f() 0090 { 0091 static AppmodelFunctions s_functions; 0092 return s_functions; 0093 } 0094 0095 namespace KisWindowsPackageUtils 0096 { 0097 0098 bool isRunningInPackage() 0099 { 0100 return tryGetCurrentPackageFamilyName(nullptr); 0101 } 0102 0103 bool tryGetCurrentPackageFamilyName(QString *outName) 0104 { 0105 if (!f().pGetCurrentPackageFamilyName) { 0106 // We are probably on Windows 7 or earlier. 0107 return false; 0108 } 0109 0110 WCHAR name[appmodel_PACKAGE_FULL_NAME_MAX_LENGTH + 1]; // includes null terminator 0111 UINT32 nameLength = sizeof(name) / sizeof(name[0]); 0112 LONG result = f().pGetCurrentPackageFamilyName(&nameLength, name); 0113 if (result == winerror_APPMODEL_ERROR_NO_PACKAGE) { 0114 // Process not running from a package. 0115 return false; 0116 } 0117 if (result == ERROR_INSUFFICIENT_BUFFER) { 0118 // This shouldn't happen! 0119 qWarning() << "GetCurrentPackageFamilyName returned ERROR_INSUFFICIENT_BUFFER, required length is" << nameLength; 0120 if (outName) { 0121 *outName = QString(); 0122 } 0123 return true; 0124 } 0125 if (result != ERROR_SUCCESS) { 0126 qWarning() << "GetCurrentPackageFamilyName returned unexpected error code:" << result; 0127 return false; 0128 } 0129 0130 if (outName) { 0131 // Sanity check 0132 if (nameLength > sizeof(name) / sizeof(name[0])) { 0133 qWarning() << "GetCurrentPackageFamilyName returned a length exceeding the buffer size:" << nameLength; 0134 nameLength = sizeof(name) / sizeof(name[0]); 0135 } 0136 // Exclude null terminator 0137 if (nameLength > 0 && name[nameLength - 1] == L'\0') { 0138 nameLength -= 1; 0139 } 0140 *outName = QString::fromWCharArray(name, nameLength); 0141 } 0142 return true; 0143 } 0144 0145 bool tryGetCurrentPackageFullName(QString *outName) 0146 { 0147 if (!f().pGetCurrentPackageFullName) { 0148 // We are probably on Windows 7 or earlier. 0149 return false; 0150 } 0151 0152 WCHAR name[appmodel_PACKAGE_FULL_NAME_MAX_LENGTH + 1]; // includes null terminator 0153 UINT32 nameLength = sizeof(name) / sizeof(name[0]); 0154 LONG result = f().pGetCurrentPackageFullName(&nameLength, name); 0155 if (result == winerror_APPMODEL_ERROR_NO_PACKAGE) { 0156 // Process not running from a package. 0157 return false; 0158 } 0159 if (result == ERROR_INSUFFICIENT_BUFFER) { 0160 // This shouldn't happen! 0161 qWarning() << "GetCurrentPackageFullName returned ERROR_INSUFFICIENT_BUFFER, required length is" << nameLength; 0162 if (outName) { 0163 *outName = QString(); 0164 } 0165 return true; 0166 } 0167 if (result != ERROR_SUCCESS) { 0168 qWarning() << "GetCurrentPackageFullName returned unexpected error code:" << result; 0169 return false; 0170 } 0171 0172 if (outName) { 0173 // Sanity check 0174 if (nameLength > sizeof(name) / sizeof(name[0])) { 0175 qWarning() << "GetCurrentPackageFullName returned a length exceeding the buffer size:" << nameLength; 0176 nameLength = sizeof(name) / sizeof(name[0]); 0177 } 0178 // Exclude null terminator 0179 if (nameLength > 0 && name[nameLength - 1] == L'\0') { 0180 nameLength -= 1; 0181 } 0182 *outName = QString::fromWCharArray(name, nameLength); 0183 } 0184 return true; 0185 } 0186 0187 QString getPackageRoamingAppDataLocation() 0188 { 0189 PWSTR path = nullptr; 0190 HRESULT result = SHGetKnownFolderPath(FOLDERID_RoamingAppData, shlobj_KF_FLAG_RETURN_FILTER_REDIRECTION_TARGET, NULL, &path); 0191 if (result != S_OK) { 0192 qWarning() << "SHGetKnownFolderPath returned error HRESULT:" << result; 0193 return QString(); 0194 } 0195 if (!path) { 0196 qWarning() << "SHGetKnownFolderPath did not return a path"; 0197 return QString(); 0198 } 0199 QString appData = QString::fromWCharArray(path); 0200 CoTaskMemFree(path); 0201 return appData; 0202 } 0203 0204 } /* namespace KisWindowsPackageUtils */