Falkon Develop
Cross-platform Qt-based web browser
registerqappassociation.cpp
Go to the documentation of this file.
1/* ============================================================
2* Copyright (C) 2012-2017 S. Razi Alavizadeh <s.r.alavizadeh@gmail.com>
3* This file is part of Falkon - Qt web browser 2010-2014
4* by David Rosca <nowrep@gmail.com>
5*
6* This program is free software: you can redistribute it and/or modify
7* it under the terms of the GNU General Public License as published by
8* the Free Software Foundation, either version 3 of the License, or
9* (at your option) any later version.
10*
11* This program is distributed in the hope that it will be useful,
12* but WITHOUT ANY WARRANTY; without even the implied warranty of
13* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14* GNU General Public License for more details.
15*
16* You should have received a copy of the GNU General Public License
17* along with this program. If not, see <http://www.gnu.org/licenses/>.
18* ============================================================ */
19
21#include "mainapplication.h"
22#include "browserwindow.h"
23
24#include "ShlObj.h"
25#include <QMessageBox>
26#include <QStringList>
27#include <QSettings>
28#include <QDir>
29
31 QObject(parent)
32{
34}
35
36RegisterQAppAssociation::RegisterQAppAssociation(const QString &appRegisteredName, const QString &appPath, const QString &appIcon,
37 const QString &appDesc, QObject* parent)
38 : QObject(parent)
39{
41 setAppInfo(appRegisteredName, appPath, appIcon, appDesc);
42}
43
45{
46}
47
48void RegisterQAppAssociation::addCapability(const QString &assocName, const QString &progId,
49 const QString &desc, const QString &iconPath, AssociationType type)
50{
51 _assocDescHash.insert(progId, QPair<QString, QString>(desc, QDir::toNativeSeparators(iconPath)));
52 switch (type) {
53 case FileAssociation:
54 _fileAssocHash.insert(assocName, progId);
55 break;
56 case UrlAssociation:
57 _urlAssocHash.insert(assocName, progId);
58 break;
59
60 default:
61 break;
62 }
63}
64
65void RegisterQAppAssociation::removeCapability(const QString &assocName)
66{
67 _fileAssocHash.remove(assocName);
68 _urlAssocHash.remove(assocName);
69}
70
71void RegisterQAppAssociation::setAppInfo(const QString &appRegisteredName, const QString &appPath,
72 const QString &appIcon, const QString &appDesc)
73{
74 _appRegisteredName = appRegisteredName;
75 _appPath = QDir::toNativeSeparators(appPath);
76 _appIcon = QDir::toNativeSeparators(appIcon);
77 _appDesc = appDesc;
78}
79
81{
82 return (_UserRootKey == QSL("HKEY_LOCAL_MACHINE"));
83}
84
86{
87 if (enable) {
88 _UserRootKey = QSL("HKEY_LOCAL_MACHINE");
89 }
90 else {
91 _UserRootKey = QSL("HKEY_CURRENT_USER");
92 }
93}
94
96{
97 if (!isVistaOrNewer()) {
98 return true;
99 }
100 // Vista and newer
101 QSettings regLocalMachine(QSL("HKEY_LOCAL_MACHINE"), QSettings::NativeFormat);
102 QString capabilitiesKey = regLocalMachine.value(QSL("Software/RegisteredApplications/") + _appRegisteredName).toString();
103
104 if (capabilitiesKey.isEmpty()) {
105 regLocalMachine.setValue(QSL("Software/RegisteredApplications/") + _appRegisteredName,
106 QString(QSL("Software\\") + _appRegisteredName + QSL("\\Capabilities")));
107 capabilitiesKey = regLocalMachine.value(QSL("Software/RegisteredApplications/") + _appRegisteredName).toString();
108
109 if (capabilitiesKey.isEmpty()) {
110 QMessageBox::warning(mApp->getWindow(), tr("Warning!"),
111 tr("There are some problems. Please, reinstall Falkon.\n"
112 "Maybe relaunch with administrator right do a magic for you! ;)"));
113 return false;
114 }
115 }
116
117 capabilitiesKey.replace(QSL("\\"), QSL("/"));
118
119 QHash<QString, QPair<QString, QString> >::const_iterator it = _assocDescHash.constBegin();
120 while (it != _assocDescHash.constEnd()) {
121 createProgId(it.key());
122 ++it;
123 }
124
125 regLocalMachine.setValue(capabilitiesKey + QSL("/ApplicationDescription"), _appDesc);
126 regLocalMachine.setValue(capabilitiesKey + QSL("/ApplicationIcon"), _appIcon);
127 regLocalMachine.setValue(capabilitiesKey + QSL("/ApplicationName"), _appRegisteredName);
128
129 QHash<QString, QString>::const_iterator i = _fileAssocHash.constBegin();
130 while (i != _fileAssocHash.constEnd()) {
131 regLocalMachine.setValue(capabilitiesKey + QSL("/FileAssociations/") + i.key(), i.value());
132 ++i;
133 }
134
135 i = _urlAssocHash.constBegin();
136 while (i != _urlAssocHash.constEnd()) {
137 regLocalMachine.setValue(capabilitiesKey + QSL("/URLAssociations/") + i.key(), i.value());
138 ++i;
139 }
140 regLocalMachine.setValue(capabilitiesKey + QSL("/Startmenu/StartMenuInternet"), _appPath);
141
142 return true;
143}
144
146{
147 return (QSysInfo::windowsVersion() >= QSysInfo::WV_VISTA &&
148 QSysInfo::windowsVersion() <= QSysInfo::WV_NT_based);
149}
150
152{
153 return (QSysInfo::windowsVersion() >= QSysInfo::WV_WINDOWS10 &&
154 QSysInfo::windowsVersion() <= QSysInfo::WV_NT_based);
155}
156
158{
159 if (isVistaOrNewer()) { // Vista and newer
160#ifndef __MINGW32__
161 IApplicationAssociationRegistration* pAAR;
162
163 HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistration,
164 NULL,
165 CLSCTX_INPROC,
166 __uuidof(IApplicationAssociationRegistration),
167 (void**)&pAAR);
168 if (SUCCEEDED(hr)) {
169 switch (type) {
170 case FileAssociation:
171 hr = pAAR->SetAppAsDefault(_appRegisteredName.toStdWString().c_str(),
172 assocName.toStdWString().c_str(),
173 AT_FILEEXTENSION);
174 break;
175 case UrlAssociation: {
176 QSettings regCurrentUserRoot(QSL("HKEY_CURRENT_USER"), QSettings::NativeFormat);
177 QString currentUrlDefault =
178 regCurrentUserRoot.value(QSL("Software/Microsoft/Windows/Shell/Associations/UrlAssociations/")
179 + assocName + QSL("/UserChoice/Progid")).toString();
180 hr = pAAR->SetAppAsDefault(_appRegisteredName.toStdWString().c_str(),
181 assocName.toStdWString().c_str(),
182 AT_URLPROTOCOL);
183 if (SUCCEEDED(hr) &&
184 !currentUrlDefault.isEmpty() &&
185 currentUrlDefault != _urlAssocHash.value(assocName)
186 ) {
187 regCurrentUserRoot.setValue(QSL("Software/Classes")
188 + assocName
189 + QSL("/shell/open/command/backup_progid"), currentUrlDefault);
190 }
191 }
192 break;
193
194 default:
195 break;
196 }
197
198 pAAR->Release();
199 }
200#endif // #ifndef __MINGW32__
201 }
202 else { // Older than Vista
203 QSettings regUserRoot(_UserRootKey, QSettings::NativeFormat);
204 regUserRoot.beginGroup(QSL("Software/Classes"));
205 QSettings regClassesRoot(QSL("HKEY_CLASSES_ROOT"), QSettings::NativeFormat);
206 switch (type) {
207 case FileAssociation: {
208 QString progId = _fileAssocHash.value(assocName);
209 createProgId(progId);
210 QString currentDefault = regClassesRoot.value(assocName + QSL("/Default")).toString();
211 if (!currentDefault.isEmpty() &&
212 currentDefault != progId &&
213 regUserRoot.value(assocName + QSL("/backup_val")).toString() != progId
214 ) {
215 regUserRoot.setValue(assocName + QSL("/backup_val"), currentDefault);
216 }
217 regUserRoot.setValue(assocName + QSL("/."), progId);
218 }
219 break;
220 case UrlAssociation: {
221 QString progId = _urlAssocHash.value(assocName);
222 createProgId(progId);
223 QString currentDefault = regClassesRoot.value(assocName + QSL("/shell/open/command/Default")).toString();
224 QString command = QSL("\"") + _appPath + QSL("\" \"%1\"");
225 if (!currentDefault.isEmpty() &&
226 currentDefault != command &&
227 regUserRoot.value(assocName + QSL("/shell/open/command/backup_val")).toString() != command
228 ) {
229 regUserRoot.setValue(assocName + QSL("/shell/open/command/backup_val"), currentDefault);
230 }
231
232 regUserRoot.setValue(assocName + QSL("/shell/open/command/."), command);
233 regUserRoot.setValue(assocName + QSL("/URL Protocol"), QSL(""));
234 break;
235 }
236 default:
237 break;
238 }
239 regUserRoot.endGroup();
240 }
241}
242
244{
246 return;
247 }
248
249 QHash<QString, QString>::const_iterator i = _fileAssocHash.constBegin();
250 while (i != _fileAssocHash.constEnd()) {
252 ++i;
253 }
254
255 i = _urlAssocHash.constBegin();
256 while (i != _urlAssocHash.constEnd()) {
258 ++i;
259 }
260
261 if (!isVistaOrNewer()) {
262#ifndef __MINGW32__
263 // On Windows Vista or newer for updating icons 'pAAR->SetAppAsDefault()'
264 // calls 'SHChangeNotify()'. Thus, we just need care about older Windows.
265 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_FLUSHNOWAIT, 0 , 0);
266#endif
267 }
268}
269
271{
272 if (!isVistaOrNewer()) {
273 return false;
274 }
275
276#ifdef _WIN32_WINNT_WIN8
277 IApplicationActivationManager* pActivator;
278 HRESULT hr = CoCreateInstance(CLSID_ApplicationActivationManager,
279 nullptr,
280 CLSCTX_INPROC,
281 IID_IApplicationActivationManager,
282 (void**)&pActivator);
283
284 if (!SUCCEEDED(hr)) {
285 return false;
286 }
287
288 DWORD pid;
289 hr = pActivator->ActivateApplication(
290 L"windows.immersivecontrolpanel_cw5n1h2txyewy" // appUserModelId of "Settings"
291 L"!microsoft.windows.immersivecontrolpanel", // in Windows Store
292 L"page=SettingsPageAppsDefaults", AO_NONE, &pid);
293
294 if (!SUCCEEDED(hr)) {
295 return false;
296 }
297
298 // Do not check error because we could at least open
299 // the "Default apps" setting.
300 pActivator->ActivateApplication(
301 L"windows.immersivecontrolpanel_cw5n1h2txyewy"
302 L"!microsoft.windows.immersivecontrolpanel",
303 L"page=SettingsPageAppsDefaults"
304 L"&target=SystemSettings_DefaultApps_Browser", AO_NONE, &pid);
305
306 pActivator->Release();
307#else // Vista or Win7
308 IApplicationAssociationRegistrationUI* pAARUI = NULL;
309
310 HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationRegistrationUI,
311 NULL, CLSCTX_INPROC, __uuidof(IApplicationAssociationRegistrationUI),
312 reinterpret_cast< void** > (&pAARUI));
313
314 if (!SUCCEEDED(hr)) {
315 return false;
316 }
317
318 hr = pAARUI->LaunchAdvancedAssociationUI(reinterpret_cast<LPCWSTR>(_appRegisteredName.utf16()));
319 pAARUI->Release();
320#endif // _WIN32_WINNT_WIN8
321
322 return true;
323}
324
325void RegisterQAppAssociation::createProgId(const QString &progId)
326{
327 QSettings regUserRoot(_UserRootKey, QSettings::NativeFormat);
328 regUserRoot.beginGroup(QSL("Software/Classes"));
329 QPair<QString, QString> pair = _assocDescHash.value(progId);
330 regUserRoot.setValue(progId + QSL("/."), pair.first);
331 regUserRoot.setValue(progId + QSL("/shell/."), QSL("open"));
332 regUserRoot.setValue(progId + QSL("/DefaultIcon/."), pair.second);
333 regUserRoot.setValue(progId + QSL("/shell/open/command/."), QString(QSL("\"") + _appPath + QSL("\" \"%1\"")));
334 regUserRoot.endGroup();
335}
336
337bool RegisterQAppAssociation::isDefaultApp(const QString &assocName, AssociationType type)
338{
339 if (isVistaOrNewer()) {
340 QSettings regCurrentUserRoot(QSL("HKEY_CURRENT_USER"), QSettings::NativeFormat);
341 switch (type) {
342 case FileAssociation: {
343 regCurrentUserRoot.beginGroup(QSL("Software/Microsoft/Windows/CurrentVersion/Explorer/FileExts"));
344 if (regCurrentUserRoot.childGroups().contains(assocName, Qt::CaseInsensitive)) {
345 return (_fileAssocHash.value(assocName)
346 == regCurrentUserRoot.value(assocName + QSL("/UserChoice/Progid")));
347 }
348 else {
349 regCurrentUserRoot.endGroup();
350 return false;
351 }
352 break;
353 }
354 case UrlAssociation: {
355 regCurrentUserRoot.beginGroup(QSL("Software/Microsoft/Windows/Shell/Associations/UrlAssociations"));
356 if (regCurrentUserRoot.childGroups().contains(assocName, Qt::CaseInsensitive)) {
357 return (_urlAssocHash.value(assocName)
358 == regCurrentUserRoot.value(assocName + QSL("/UserChoice/Progid")));
359 }
360 else {
361 regCurrentUserRoot.endGroup();
362 return false;
363 }
364 }
365 break;
366
367 default:
368 break;
369 }
370 }
371 else {
372 QSettings regClassesRoot(QSL("HKEY_CLASSES_ROOT"), QSettings::NativeFormat);
373 {
374 if (!regClassesRoot.childGroups().contains(assocName, Qt::CaseInsensitive)) {
375 return false;
376 }
377 }
378 switch (type) {
379 case FileAssociation: {
380 return (_fileAssocHash.value(assocName)
381 == regClassesRoot.value(assocName + QSL("/Default")));
382 }
383 break;
384 case UrlAssociation: {
385 QString currentDefault = regClassesRoot.value(assocName + QSL("/shell/open/command/Default")).toString();
386 currentDefault.remove(QSL("\""));
387 currentDefault.remove(QSL("%1"));
388 currentDefault = currentDefault.trimmed();
389 return (_appPath == currentDefault);
390 }
391 break;
392
393 default:
394 break;
395 }
396 }
397
398 return false;
399}
400
402{
403 bool result = true;
404 QHash<QString, QString>::const_iterator i = _fileAssocHash.constBegin();
405 while (i != _fileAssocHash.constEnd()) {
406 bool res = isDefaultApp(i.key(), FileAssociation);
407 result &= res;
408 ++i;
409 }
410
411 i = _urlAssocHash.constBegin();
412 while (i != _urlAssocHash.constEnd()) {
413 bool res = isDefaultApp(i.key(), UrlAssociation);
414 result &= res;
415 ++i;
416 }
417 return result;
418}
bool isDefaultApp(const QString &assocName, AssociationType type)
void setAppInfo(const QString &appRegisteredName, const QString &appPath, const QString &appIcon=QString(), const QString &appDesc=QString())
void removeCapability(const QString &assocName)
void addCapability(const QString &assocName, const QString &progId, const QString &desc, const QString &iconPath, AssociationType type)
void registerAssociation(const QString &assocName, AssociationType type)
void setPerMachineRegisteration(bool enable)
RegisterQAppAssociation(QObject *parent=nullptr)
void createProgId(const QString &progId)
#define mApp
i
Definition: i18n.py:23
#define QSL(x)
Definition: qzcommon.h:40