summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorkh1 <karsten.heimrich@digia.com>2012-11-05 14:58:25 +0100
committerKarsten Heimrich <karsten.heimrich@digia.com>2012-11-06 12:16:34 +0100
commit83f324972d3e83ea64d399fc31c8d16edc7eee29 (patch)
tree3dfc25456f3cd6dd08594cd9b36f8b16ddcee9ee /src
parente11d59357f48c84cef38d204999bdb2aa1d80152 (diff)
Rewrote file type registering.
Done now as described in MSDN. Make sure we honor the all users flag. Reset the file type only if there has nothing changed inbetween. Remove the ProgID entirely. Add the registered ProgId inside the common "Open with" menu as well. If passed a ProgId, use this one as key inside the classes hive. If not given, fallback to the more hacky *_auto_file naming scheme. Change-Id: I3f1c1a2301ad19b7fe215b65a8dd9c9419022f6c Reviewed-by: Niels Weber <niels.weber@digia.com> Reviewed-by: Tim Jenssen <tim.jenssen@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/libs/installer/registerfiletypeoperation.cpp232
-rw-r--r--src/libs/installer/registerfiletypeoperation.h4
2 files changed, 128 insertions, 108 deletions
diff --git a/src/libs/installer/registerfiletypeoperation.cpp b/src/libs/installer/registerfiletypeoperation.cpp
index ff72b97fd..e7f76be01 100644
--- a/src/libs/installer/registerfiletypeoperation.cpp
+++ b/src/libs/installer/registerfiletypeoperation.cpp
@@ -32,10 +32,51 @@
#include "registerfiletypeoperation.h"
+#include "packagemanagercore.h"
#include "qsettingswrapper.h"
using namespace QInstaller;
+#ifdef Q_OS_WIN
+#include <shlobj.h>
+
+
+struct StartsWithProgId
+{
+ bool operator()(const QString &s)
+ {
+ return s.startsWith(QLatin1String("ProgId="));
+ }
+};
+
+static QString takeProgIdArgument(QStringList &args)
+{
+ // if the arguments contain an option in the form "ProgId=...", find it and consume it
+ QStringList::iterator it = std::find_if (args.begin(), args.end(), StartsWithProgId());
+ if (it == args.end())
+ return QString();
+
+ const QString progId = it->mid(QString::fromLatin1("ProgId=").size());
+ args.erase(it);
+ return progId;
+}
+
+static QVariantHash readHive(QSettingsWrapper *const settings, const QString &hive)
+{
+ QVariantHash keyValues;
+
+ settings->beginGroup(hive);
+ foreach (const QString &key, settings->allKeys())
+ keyValues.insert(key, settings->value(key));
+ settings->endGroup();
+
+ return keyValues;
+}
+#endif
+
+
+// -- RegisterFileTypeOperation
+
RegisterFileTypeOperation::RegisterFileTypeOperation()
{
setName(QLatin1String("RegisterFileType"));
@@ -47,151 +88,128 @@ void RegisterFileTypeOperation::backup()
bool RegisterFileTypeOperation::performOperation()
{
- // extension
- // command
- // (description)
- // (content type)
- // (icon)
#ifdef Q_OS_WIN
- const QStringList args = arguments();
+ QStringList args = arguments();
+ QString progId = takeProgIdArgument(args);
+
if (args.count() < 2 || args.count() > 5) {
setError(InvalidArguments);
setErrorString(tr("Invalid arguments in %0").arg(name()));
return false;
}
+ bool allUsers = false;
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore *>(value(QLatin1String("installer")));
+ if (core && core->value(QLatin1String("AllUsers")) == QLatin1String("true"))
+ allUsers = true;
+
+ QSettingsWrapper settings(QLatin1String(allUsers ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER")
+ , QSettingsWrapper::NativeFormat);
+
const QString extension = args.at(0);
- const QString command = args.at(1);
- const QString description = args.value(2); // optional
- const QString contentType = args.value(3); // optional
- const QString icon = args.value(4); // optional
-
- const QString className = QString::fromLatin1("%1_auto_file").arg(extension);
- const QString settingsPrefix = QString::fromLatin1("Software/Classes/");
-
- //QSettingsWrapper settings(QLatin1String("HKEY_CLASSES_ROOT"), QSettingsWrapper::NativeFormat);
- QSettingsWrapper settings(QLatin1String("HKEY_CURRENT_USER"), QSettingsWrapper::NativeFormat);
- // first backup the old values
- setValue(QLatin1String("oldClass"), settings.value(QString::fromLatin1("%1.%2/Default").arg(settingsPrefix,
- extension)));
- setValue(QLatin1String("oldContentType"), settings.value(QString::fromLatin1("%1.%2/Content Type")
- .arg(settingsPrefix, extension)));
-
- // the file extension was not yet registered. Let's do so now
- settings.setValue(QString::fromLatin1("%1.%2/Default").arg(settingsPrefix, extension), className);
-
- // register the content type, if given
+ if (progId.isEmpty())
+ progId = QString::fromLatin1("%1_auto_file").arg(extension);
+ const QString classesProgId = QString::fromLatin1("Software/Classes/") + progId;
+ const QString classesFileType = QString::fromLatin1("Software/Classes/.%2").arg(extension);
+ const QString classesApplications = QString::fromLatin1("Software/Classes/Applications/") + progId;
+
+ // backup old value
+ setValue(QLatin1String("oldType"), readHive(&settings, classesFileType));
+
+ // register new values
+ settings.setValue(QString::fromLatin1("%1/Default").arg(classesFileType), progId);
+ settings.setValue(QString::fromLatin1("%1/OpenWithProgIds/%2").arg(classesFileType, progId), QString());
+ settings.setValue(QString::fromLatin1("%1/shell/Open/Command/Default").arg(classesProgId), args.at(1));
+ settings.setValue(QString::fromLatin1("%1/shell/Open/Command/Default").arg(classesApplications), args.at(1));
+
+ // content type (optional)
+ const QString contentType = args.value(3);
if (!contentType.isEmpty())
- settings.setValue(QString::fromLatin1("%1.%2/Content Type").arg(settingsPrefix, extension), contentType);
+ settings.setValue(QString::fromLatin1("%1/Content Type").arg(classesFileType), contentType);
- setValue(QLatin1String("className"), className);
- // register the description, if given
+ // description (optional)
+ const QString description = args.value(2);
if (!description.isEmpty())
- settings.setValue(QString::fromLatin1("%1%2/Default").arg(settingsPrefix, className), description);
+ settings.setValue(QString::fromLatin1("%1/Default").arg(classesProgId), description);
- // register the icon, if given
- if (!icon.isEmpty()) {
- settings.setValue(QString::fromLatin1("%1%2/DefaultIcon/Default").arg(settingsPrefix,
- className), icon);
- }
+ // icon (optional)
+ const QString icon = args.value(4);
+ if (!icon.isEmpty())
+ settings.setValue(QString::fromLatin1("%1/DefaultIcon/Default").arg(classesProgId), icon);
+
+ // backup new value
+ setValue(QLatin1String("newType"), readHive(&settings, classesFileType));
- // register the command to open the file
- settings.setValue(QString::fromLatin1("%1%2/shell/Open/Command/Default").arg(settingsPrefix,
- className), command);
+ // force the shell to invalidate its cache
+ SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
return true;
#else
setError(UserDefinedError);
- setErrorString(QObject::tr("Registering file types in only supported on Windows."));
+ setErrorString(QObject::tr("Registering file types is only supported on Windows."));
return false;
#endif
}
bool RegisterFileTypeOperation::undoOperation()
{
- // extension
- // command
- // (description)
- // (content type)
- // (icon)
#ifdef Q_OS_WIN
- const QStringList args = arguments();
+ QStringList args = arguments();
+ QString progId = takeProgIdArgument(args);
+
if (args.count() < 2 || args.count() > 5) {
setErrorString(tr("Register File Type: Invalid arguments"));
return false;
}
- const QString extension = args.at(0);
- const QString command = args.at(1);
- const QString description = args.value(2); // optional
- const QString contentType = args.value(3); // optional
- const QString icon = args.value(4); // optional
-
- const QString className = QString::fromLatin1("%1_auto_file").arg(extension);
- const QString classes = QString::fromLatin1("Software/Classes/%1").arg(className);
- const QString extensions = QString::fromLatin1("Software/Classes/.%1").arg(extension);
- QSettingsWrapper settings(QLatin1String("HKEY_CURRENT_USER"), QSettingsWrapper::NativeFormat);
+ bool allUsers = false;
+ PackageManagerCore *const core = qVariantValue<PackageManagerCore *>(value(QLatin1String("installer")));
+ if (core && core->value(QLatin1String("AllUsers")) == QLatin1String("true"))
+ allUsers = true;
- const QString restoredClassName = value(QLatin1String("oldClassName")).toString();
- // register the command to open the file
- if (settings.value(QString::fromLatin1("%1/shell/Open/Command/Default").arg(classes)).toString() != command)
- return false;
- if (settings.value(QString::fromLatin1("%1/DefaultIcon/Default").arg(classes)).toString() != icon)
- return false;
- if (settings.value(QString::fromLatin1("%1/Default").arg(classes)).toString() != description)
- return false;
- if (settings.value(QString::fromLatin1("%1/Content Type").arg(extensions)).toString() != contentType)
- return false;
- if (settings.value(QString::fromLatin1("%1/Default").arg(extensions)).toString() != className)
- return false;
+ QSettingsWrapper settings(QLatin1String(allUsers ? "HKEY_LOCAL_MACHINE" : "HKEY_CURRENT_USER")
+ , QSettingsWrapper::NativeFormat);
- const QLatin1String settingsPrefix("Software/Classes/");
- const QVariant oldCommand = value(QLatin1String("oldCommand"));
- if (!oldCommand.isNull()) {
- settings.setValue(QString::fromLatin1("%1%2/shell/Open/Command/Default").arg(settingsPrefix,
- restoredClassName), oldCommand);
- } else {
- settings.remove(QString::fromLatin1("%1/shell/Open/Command/Default").arg(classes));
- }
-
- // register the icon, if given
- const QVariant oldIcon = value(QLatin1String("oldIcon"));
- if (!oldIcon.isNull()) {
- settings.setValue(QString::fromLatin1("%1%2/DefaultIcon/Default").arg(settingsPrefix,
- restoredClassName), oldIcon);
- } else {
- settings.remove(QString::fromLatin1("%1%2/DefaultIcon/Default").arg(classes));
- }
-
- // register the description, if given
- const QVariant oldDescription = value(QLatin1String("oldDescription"));
- if (!oldDescription.isNull()) {
- settings.setValue(QString::fromLatin1("%1%2/Default").arg(settingsPrefix, restoredClassName),
- oldDescription);
+ const QString extension = args.at(0);
+ if (progId.isEmpty())
+ progId = QString::fromLatin1("%1_auto_file").arg(extension);
+ const QString classesProgId = QString::fromLatin1("Software/Classes/") + progId;
+ const QString classesFileType = QString::fromLatin1("Software/Classes/.%2").arg(extension);
+ const QString classesApplications = QString::fromLatin1("Software/Classes/Applications/") + progId;
+
+ // Quoting MSDN here: When uninstalling an application, the ProgIDs and most other registry information
+ // associated with that application should be deleted as part of the uninstallation.However, applications
+ // that have taken ownership of a file type (by setting the Default value of the file type's
+ // HKEY...\.extension subkey to the ProgID of the application) should not attempt to remove that value
+ // when uninstalling. Leaving the data in place for the Default value avoids the difficulty of
+ // determining whether another application has taken ownership of the file type and overwritten the
+ // Default value after the original application was installed. Windows respects the Default value only
+ // if the ProgID found there is a registered ProgID. If the ProgID is unregistered, it is ignored.
+
+ // if the hive didn't change since we touched it
+ if (value(QLatin1String("newType")).toHash() == readHive(&settings, classesFileType)) {
+ // reset to the values we found
+ settings.remove(classesFileType);
+ settings.beginGroup(classesFileType);
+ const QVariantHash keyValues = value(QLatin1String("oldType")).toHash();
+ foreach (const QString &key, keyValues.keys())
+ settings.setValue(key, keyValues.value(key));
+ settings.endGroup();
} else {
- settings.remove(QString::fromLatin1("%1%2/Default").arg(classes));
+ // some changes happened, remove the only save value we know about
+ settings.remove(QString::fromLatin1("%1/OpenWithProgIds/%2").arg(classesFileType, progId));
}
- // content type
- settings.remove(classes);
-
- const QVariant oldContentType = value(QLatin1String("oldContentType"));
- if(!oldContentType.isNull())
- settings.setValue(QString::fromLatin1("%1/Content Type").arg(extensions), oldContentType);
- else
- settings.remove(QString::fromLatin1("%1/Content Type").arg(extensions));
-
- // class
+ // remove ProgId and Applications entry
+ settings.remove(classesProgId);
+ settings.remove(classesApplications);
- const QVariant oldClass = value(QLatin1String("oldClass"));
- if(!oldClass.isNull())
- settings.setValue(QString::fromLatin1("%1/Default").arg(extensions), oldClass);
- else
- settings.remove(extensions);
+ // force the shell to invalidate its cache
+ SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
return true;
#else
- setErrorString(QObject::tr("Registering file types in only supported on Windows."));
+ setErrorString(QObject::tr("Registering file types is only supported on Windows."));
return false;
#endif
}
diff --git a/src/libs/installer/registerfiletypeoperation.h b/src/libs/installer/registerfiletypeoperation.h
index 824024892..e1148dc7d 100644
--- a/src/libs/installer/registerfiletypeoperation.h
+++ b/src/libs/installer/registerfiletypeoperation.h
@@ -37,8 +37,10 @@
namespace QInstaller {
-class INSTALLER_EXPORT RegisterFileTypeOperation : public Operation
+class INSTALLER_EXPORT RegisterFileTypeOperation : public QObject, public Operation
{
+ Q_OBJECT
+
public:
RegisterFileTypeOperation();