File indexing completed on 2024-05-12 04:58:12
0001 /* ============================================================ 0002 * Copyright (C) 2012-2017 S. Razi Alavizadeh <s.r.alavizadeh@gmail.com> 0003 * This file is part of Falkon - Qt web browser 2010-2014 0004 * by David Rosca <nowrep@gmail.com> 0005 * 0006 * This program is free software: you can redistribute it and/or modify 0007 * it under the terms of the GNU General Public License as published by 0008 * the Free Software Foundation, either version 3 of the License, or 0009 * (at your option) any later version. 0010 * 0011 * This program is distributed in the hope that it will be useful, 0012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 * GNU General Public License for more details. 0015 * 0016 * You should have received a copy of the GNU General Public License 0017 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0018 * ============================================================ */ 0019 0020 #include "registerqappassociation.h" 0021 #include "mainapplication.h" 0022 #include "browserwindow.h" 0023 0024 #include "ShlObj.h" 0025 #include <QMessageBox> 0026 #include <QStringList> 0027 #include <QSettings> 0028 #include <QDir> 0029 0030 RegisterQAppAssociation::RegisterQAppAssociation(QObject* parent) : 0031 QObject(parent) 0032 { 0033 setPerMachineRegisteration(false); 0034 } 0035 0036 RegisterQAppAssociation::RegisterQAppAssociation(const QString &appRegisteredName, const QString &appPath, const QString &appIcon, 0037 const QString &appDesc, QObject* parent) 0038 : QObject(parent) 0039 { 0040 setPerMachineRegisteration(false); 0041 setAppInfo(appRegisteredName, appPath, appIcon, appDesc); 0042 } 0043 0044 RegisterQAppAssociation::~RegisterQAppAssociation() 0045 { 0046 } 0047 0048 void RegisterQAppAssociation::addCapability(const QString &assocName, const QString &progId, 0049 const QString &desc, const QString &iconPath, AssociationType type) 0050 { 0051 _assocDescHash.insert(progId, QPair<QString, QString>(desc, QDir::toNativeSeparators(iconPath))); 0052 switch (type) { 0053 case FileAssociation: 0054 _fileAssocHash.insert(assocName, progId); 0055 break; 0056 case UrlAssociation: 0057 _urlAssocHash.insert(assocName, progId); 0058 break; 0059 0060 default: 0061 break; 0062 } 0063 } 0064 0065 void RegisterQAppAssociation::removeCapability(const QString &assocName) 0066 { 0067 _fileAssocHash.remove(assocName); 0068 _urlAssocHash.remove(assocName); 0069 } 0070 0071 void RegisterQAppAssociation::setAppInfo(const QString &appRegisteredName, const QString &appPath, 0072 const QString &appIcon, const QString &appDesc) 0073 { 0074 _appRegisteredName = appRegisteredName; 0075 _appPath = QDir::toNativeSeparators(appPath); 0076 _appIcon = QDir::toNativeSeparators(appIcon); 0077 _appDesc = appDesc; 0078 } 0079 0080 bool RegisterQAppAssociation::isPerMachineRegisteration() 0081 { 0082 return (_UserRootKey == QSL("HKEY_LOCAL_MACHINE")); 0083 } 0084 0085 void RegisterQAppAssociation::setPerMachineRegisteration(bool enable) 0086 { 0087 if (enable) { 0088 _UserRootKey = QSL("HKEY_LOCAL_MACHINE"); 0089 } 0090 else { 0091 _UserRootKey = QSL("HKEY_CURRENT_USER"); 0092 } 0093 } 0094 0095 bool RegisterQAppAssociation::registerAppCapabilities() 0096 { 0097 if (!isVistaOrNewer()) { 0098 return true; 0099 } 0100 // Vista and newer 0101 QSettings regLocalMachine(QSL("HKEY_LOCAL_MACHINE"), QSettings::NativeFormat); 0102 QString capabilitiesKey = regLocalMachine.value(QSL("Software/RegisteredApplications/") + _appRegisteredName).toString(); 0103 0104 if (capabilitiesKey.isEmpty()) { 0105 regLocalMachine.setValue(QSL("Software/RegisteredApplications/") + _appRegisteredName, 0106 QString(QSL("Software\\") + _appRegisteredName + QSL("\\Capabilities"))); 0107 capabilitiesKey = regLocalMachine.value(QSL("Software/RegisteredApplications/") + _appRegisteredName).toString(); 0108 0109 if (capabilitiesKey.isEmpty()) { 0110 QMessageBox::warning(mApp->getWindow(), tr("Warning!"), 0111 tr("There are some problems. Please, reinstall Falkon.\n" 0112 "Maybe relaunch with administrator right do a magic for you! ;)")); 0113 return false; 0114 } 0115 } 0116 0117 capabilitiesKey.replace(QSL("\\"), QSL("/")); 0118 0119 QHash<QString, QPair<QString, QString> >::const_iterator it = _assocDescHash.constBegin(); 0120 while (it != _assocDescHash.constEnd()) { 0121 createProgId(it.key()); 0122 ++it; 0123 } 0124 0125 regLocalMachine.setValue(capabilitiesKey + QSL("/ApplicationDescription"), _appDesc); 0126 regLocalMachine.setValue(capabilitiesKey + QSL("/ApplicationIcon"), _appIcon); 0127 regLocalMachine.setValue(capabilitiesKey + QSL("/ApplicationName"), _appRegisteredName); 0128 0129 QHash<QString, QString>::const_iterator i = _fileAssocHash.constBegin(); 0130 while (i != _fileAssocHash.constEnd()) { 0131 regLocalMachine.setValue(capabilitiesKey + QSL("/FileAssociations/") + i.key(), i.value()); 0132 ++i; 0133 } 0134 0135 i = _urlAssocHash.constBegin(); 0136 while (i != _urlAssocHash.constEnd()) { 0137 regLocalMachine.setValue(capabilitiesKey + QSL("/URLAssociations/") + i.key(), i.value()); 0138 ++i; 0139 } 0140 regLocalMachine.setValue(capabilitiesKey + QSL("/Startmenu/StartMenuInternet"), _appPath); 0141 0142 return true; 0143 } 0144 0145 bool RegisterQAppAssociation::isVistaOrNewer() 0146 { 0147 return (QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA && 0148 QSysInfo::windowsVersion() <= QSysInfo::WV_NT_based); 0149 } 0150 0151 bool RegisterQAppAssociation::isWin10OrNewer() 0152 { 0153 return (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS10 && 0154 QSysInfo::windowsVersion() <= QSysInfo::WV_NT_based); 0155 } 0156 0157 void RegisterQAppAssociation::registerAssociation(const QString &assocName, AssociationType type) 0158 { 0159 if (isVistaOrNewer()) { // Vista and newer 0160 #ifndef __MINGW32__ 0161 IApplicationAssociationRegistration* pAAR; 0162 0163 HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration, 0164 NULL, 0165 CLSCTX_INPROC, 0166 __uuidof(IApplicationAssociationRegistration), 0167 (void**)&pAAR); 0168 if (SUCCEEDED(hr)) { 0169 switch (type) { 0170 case FileAssociation: 0171 hr = pAAR->SetAppAsDefault(_appRegisteredName.toStdWString().c_str(), 0172 assocName.toStdWString().c_str(), 0173 AT_FILEEXTENSION); 0174 break; 0175 case UrlAssociation: { 0176 QSettings regCurrentUserRoot(QSL("HKEY_CURRENT_USER"), QSettings::NativeFormat); 0177 QString currentUrlDefault = 0178 regCurrentUserRoot.value(QSL("Software/Microsoft/Windows/Shell/Associations/UrlAssociations/") 0179 + assocName + QSL("/UserChoice/Progid")).toString(); 0180 hr = pAAR->SetAppAsDefault(_appRegisteredName.toStdWString().c_str(), 0181 assocName.toStdWString().c_str(), 0182 AT_URLPROTOCOL); 0183 if (SUCCEEDED(hr) && 0184 !currentUrlDefault.isEmpty() && 0185 currentUrlDefault != _urlAssocHash.value(assocName) 0186 ) { 0187 regCurrentUserRoot.setValue(QSL("Software/Classes") 0188 + assocName 0189 + QSL("/shell/open/command/backup_progid"), currentUrlDefault); 0190 } 0191 } 0192 break; 0193 0194 default: 0195 break; 0196 } 0197 0198 pAAR->Release(); 0199 } 0200 #endif // #ifndef __MINGW32__ 0201 } 0202 else { // Older than Vista 0203 QSettings regUserRoot(_UserRootKey, QSettings::NativeFormat); 0204 regUserRoot.beginGroup(QSL("Software/Classes")); 0205 QSettings regClassesRoot(QSL("HKEY_CLASSES_ROOT"), QSettings::NativeFormat); 0206 switch (type) { 0207 case FileAssociation: { 0208 QString progId = _fileAssocHash.value(assocName); 0209 createProgId(progId); 0210 QString currentDefault = regClassesRoot.value(assocName + QSL("/Default")).toString(); 0211 if (!currentDefault.isEmpty() && 0212 currentDefault != progId && 0213 regUserRoot.value(assocName + QSL("/backup_val")).toString() != progId 0214 ) { 0215 regUserRoot.setValue(assocName + QSL("/backup_val"), currentDefault); 0216 } 0217 regUserRoot.setValue(assocName + QSL("/."), progId); 0218 } 0219 break; 0220 case UrlAssociation: { 0221 QString progId = _urlAssocHash.value(assocName); 0222 createProgId(progId); 0223 QString currentDefault = regClassesRoot.value(assocName + QSL("/shell/open/command/Default")).toString(); 0224 QString command = QSL("\"") + _appPath + QSL("\" \"%1\""); 0225 if (!currentDefault.isEmpty() && 0226 currentDefault != command && 0227 regUserRoot.value(assocName + QSL("/shell/open/command/backup_val")).toString() != command 0228 ) { 0229 regUserRoot.setValue(assocName + QSL("/shell/open/command/backup_val"), currentDefault); 0230 } 0231 0232 regUserRoot.setValue(assocName + QSL("/shell/open/command/."), command); 0233 regUserRoot.setValue(assocName + QSL("/URL Protocol"), QSL("")); 0234 break; 0235 } 0236 default: 0237 break; 0238 } 0239 regUserRoot.endGroup(); 0240 } 0241 } 0242 0243 void RegisterQAppAssociation::registerAllAssociation() 0244 { 0245 if (isVistaOrNewer() && !registerAppCapabilities()) { 0246 return; 0247 } 0248 0249 QHash<QString, QString>::const_iterator i = _fileAssocHash.constBegin(); 0250 while (i != _fileAssocHash.constEnd()) { 0251 registerAssociation(i.key(), FileAssociation); 0252 ++i; 0253 } 0254 0255 i = _urlAssocHash.constBegin(); 0256 while (i != _urlAssocHash.constEnd()) { 0257 registerAssociation(i.key(), UrlAssociation); 0258 ++i; 0259 } 0260 0261 if (!isVistaOrNewer()) { 0262 #ifndef __MINGW32__ 0263 // On Windows Vista or newer for updating icons 'pAAR->SetAppAsDefault()' 0264 // calls 'SHChangeNotify()'. Thus, we just need care about older Windows. 0265 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSHNOWAIT, 0 , 0); 0266 #endif 0267 } 0268 } 0269 0270 bool RegisterQAppAssociation::showNativeDefaultAppSettingsUi() 0271 { 0272 if (!isVistaOrNewer()) { 0273 return false; 0274 } 0275 0276 #ifdef _WIN32_WINNT_WIN8 0277 IApplicationActivationManager* pActivator; 0278 HRESULT hr = CoCreateInstance(CLSID_ApplicationActivationManager, 0279 nullptr, 0280 CLSCTX_INPROC, 0281 IID_IApplicationActivationManager, 0282 (void**)&pActivator); 0283 0284 if (!SUCCEEDED(hr)) { 0285 return false; 0286 } 0287 0288 DWORD pid; 0289 hr = pActivator->ActivateApplication( 0290 L"windows.immersivecontrolpanel_cw5n1h2txyewy" // appUserModelId of "Settings" 0291 L"!microsoft.windows.immersivecontrolpanel", // in Windows Store 0292 L"page=SettingsPageAppsDefaults", AO_NONE, &pid); 0293 0294 if (!SUCCEEDED(hr)) { 0295 return false; 0296 } 0297 0298 // Do not check error because we could at least open 0299 // the "Default apps" setting. 0300 pActivator->ActivateApplication( 0301 L"windows.immersivecontrolpanel_cw5n1h2txyewy" 0302 L"!microsoft.windows.immersivecontrolpanel", 0303 L"page=SettingsPageAppsDefaults" 0304 L"&target=SystemSettings_DefaultApps_Browser", AO_NONE, &pid); 0305 0306 pActivator->Release(); 0307 #else // Vista or Win7 0308 IApplicationAssociationRegistrationUI* pAARUI = NULL; 0309 0310 HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistrationUI, 0311 NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistrationUI), 0312 reinterpret_cast< void** > (&pAARUI)); 0313 0314 if (!SUCCEEDED(hr)) { 0315 return false; 0316 } 0317 0318 hr = pAARUI->LaunchAdvancedAssociationUI(reinterpret_cast<LPCWSTR>(_appRegisteredName.utf16())); 0319 pAARUI->Release(); 0320 #endif // _WIN32_WINNT_WIN8 0321 0322 return true; 0323 } 0324 0325 void RegisterQAppAssociation::createProgId(const QString &progId) 0326 { 0327 QSettings regUserRoot(_UserRootKey, QSettings::NativeFormat); 0328 regUserRoot.beginGroup(QSL("Software/Classes")); 0329 QPair<QString, QString> pair = _assocDescHash.value(progId); 0330 regUserRoot.setValue(progId + QSL("/."), pair.first); 0331 regUserRoot.setValue(progId + QSL("/shell/."), QSL("open")); 0332 regUserRoot.setValue(progId + QSL("/DefaultIcon/."), pair.second); 0333 regUserRoot.setValue(progId + QSL("/shell/open/command/."), QString(QSL("\"") + _appPath + QSL("\" \"%1\""))); 0334 regUserRoot.endGroup(); 0335 } 0336 0337 bool RegisterQAppAssociation::isDefaultApp(const QString &assocName, AssociationType type) 0338 { 0339 if (isVistaOrNewer()) { 0340 QSettings regCurrentUserRoot(QSL("HKEY_CURRENT_USER"), QSettings::NativeFormat); 0341 switch (type) { 0342 case FileAssociation: { 0343 regCurrentUserRoot.beginGroup(QSL("Software/Microsoft/Windows/CurrentVersion/Explorer/FileExts")); 0344 if (regCurrentUserRoot.childGroups().contains(assocName, Qt::CaseInsensitive)) { 0345 return (_fileAssocHash.value(assocName) 0346 == regCurrentUserRoot.value(assocName + QSL("/UserChoice/Progid"))); 0347 } 0348 else { 0349 regCurrentUserRoot.endGroup(); 0350 return false; 0351 } 0352 break; 0353 } 0354 case UrlAssociation: { 0355 regCurrentUserRoot.beginGroup(QSL("Software/Microsoft/Windows/Shell/Associations/UrlAssociations")); 0356 if (regCurrentUserRoot.childGroups().contains(assocName, Qt::CaseInsensitive)) { 0357 return (_urlAssocHash.value(assocName) 0358 == regCurrentUserRoot.value(assocName + QSL("/UserChoice/Progid"))); 0359 } 0360 else { 0361 regCurrentUserRoot.endGroup(); 0362 return false; 0363 } 0364 } 0365 break; 0366 0367 default: 0368 break; 0369 } 0370 } 0371 else { 0372 QSettings regClassesRoot(QSL("HKEY_CLASSES_ROOT"), QSettings::NativeFormat); 0373 { 0374 if (!regClassesRoot.childGroups().contains(assocName, Qt::CaseInsensitive)) { 0375 return false; 0376 } 0377 } 0378 switch (type) { 0379 case FileAssociation: { 0380 return (_fileAssocHash.value(assocName) 0381 == regClassesRoot.value(assocName + QSL("/Default"))); 0382 } 0383 break; 0384 case UrlAssociation: { 0385 QString currentDefault = regClassesRoot.value(assocName + QSL("/shell/open/command/Default")).toString(); 0386 currentDefault.remove(QSL("\"")); 0387 currentDefault.remove(QSL("%1")); 0388 currentDefault = currentDefault.trimmed(); 0389 return (_appPath == currentDefault); 0390 } 0391 break; 0392 0393 default: 0394 break; 0395 } 0396 } 0397 0398 return false; 0399 } 0400 0401 bool RegisterQAppAssociation::isDefaultForAllCapabilities() 0402 { 0403 bool result = true; 0404 QHash<QString, QString>::const_iterator i = _fileAssocHash.constBegin(); 0405 while (i != _fileAssocHash.constEnd()) { 0406 bool res = isDefaultApp(i.key(), FileAssociation); 0407 result &= res; 0408 ++i; 0409 } 0410 0411 i = _urlAssocHash.constBegin(); 0412 while (i != _urlAssocHash.constEnd()) { 0413 bool res = isDefaultApp(i.key(), UrlAssociation); 0414 result &= res; 0415 ++i; 0416 } 0417 return result; 0418 }