diff options
| author | trialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38> | 2014-03-09 10:13:49 +0000 |
|---|---|---|
| committer | trialuser02 <trialuser02@90c681e8-e032-0410-971d-27865f9a5e38> | 2014-03-09 10:13:49 +0000 |
| commit | f9b488772eafffe966972575241d0ff779783bec (patch) | |
| tree | 67f775a8bf84c95fa6923729126b8ac24aee24ad /src/qmmpui | |
| parent | b843d305c6433a2935713ac6e8fdf69848869f2e (diff) | |
| download | qmmp-f9b488772eafffe966972575241d0ff779783bec.tar.gz qmmp-f9b488772eafffe966972575241d0ff779783bec.tar.bz2 qmmp-f9b488772eafffe966972575241d0ff779783bec.zip | |
added winfileaccos.h and winfileaccos.cpp
git-svn-id: http://svn.code.sf.net/p/qmmp-dev/code/trunk/qmmp@4137 90c681e8-e032-0410-971d-27865f9a5e38
Diffstat (limited to 'src/qmmpui')
| -rw-r--r-- | src/qmmpui/qmmpui.pro | 6 | ||||
| -rw-r--r-- | src/qmmpui/winfileassoc.cpp | 371 | ||||
| -rw-r--r-- | src/qmmpui/winfileassoc.h | 51 |
3 files changed, 426 insertions, 2 deletions
diff --git a/src/qmmpui/qmmpui.pro b/src/qmmpui/qmmpui.pro index 82b14259e..d4a4832e9 100644 --- a/src/qmmpui/qmmpui.pro +++ b/src/qmmpui/qmmpui.pro @@ -26,8 +26,10 @@ unix { win32 { TARGET = ../../../bin/qmmpui LIBS += -L../../bin -lqmmp0 - HEADERS += prefassociations.h - SOURCES += prefassociations.cpp + HEADERS += prefassociations.h \ + winfileaccos.h + SOURCES += prefassociations.cpp \ + winfileaccos.cpp } HEADERS += general.h \ diff --git a/src/qmmpui/winfileassoc.cpp b/src/qmmpui/winfileassoc.cpp new file mode 100644 index 000000000..f8a5a0f85 --- /dev/null +++ b/src/qmmpui/winfileassoc.cpp @@ -0,0 +1,371 @@ +/* smplayer, GUI front-end for mplayer. + Copyright (C) 2006-2014 Ricardo Villalba <rvm@users.sourceforge.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + Winfileassoc.cpp + + Handles file associations in Windows 7/Vista/XP/2000. + We assume that the code is run without administrator privileges, so the associations are done for current user only. + System-wide associations require writing to HKEY_CLASSES_ROOT and we don't want to get our hands dirty with that. + Each user on the computer can configure his own set of file associations for SMPlayer, which is extremely cool. + + Optionally, during uninstall, it would be a good idea to call RestoreFileAssociations for all media types so + that we can clean up the registry and restore the old associations for current user. + + Vista: + The code can only register the app as default program for selected extensions and check if it is the default. + It cannot restore 'old' default application, since this doesn't seem to be possible with the current Vista API. + + Add libole32.a library if compiling with MinGW. In smplayer.pro, under 'win32 {': LIBS += libole32 + + Tested on: WinXP, Vista, Win7. + + Author: Florin Braghis (florin@libertv.ro) +*/ + +#include "winfileassoc.h" +#include <QSettings> +#include <QApplication> +#include <QFileInfo> +#include <windows.h> + +WinFileAssoc::WinFileAssoc(const QString ClassId, const QString AppName) +{ + m_ClassId = ClassId; + m_AppName = AppName; + m_ClassId2 = QFileInfo(QApplication::applicationFilePath()).fileName(); +} + +// Associates all extensions in the fileExtensions list with current app. +// Returns number of extensions processed successfully. +int WinFileAssoc::CreateFileAssociations(const QStringList &fileExtensions) +{ + if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA) { + return VistaSetAppsAsDefault(fileExtensions); + } + + QSettings RegCR("HKEY_CLASSES_ROOT", QSettings::NativeFormat); //Read only on NT+ + QSettings RegCU("HKEY_CURRENT_USER", QSettings::NativeFormat); + + if (!RegCU.isWritable() || RegCU.status() != QSettings::NoError) + return 0; + + if (RegCR.status() != QSettings::NoError) + return 0; + + // Check if classId exists in the registry + if (!RegCR.contains(m_ClassId) && !RegCU.contains("Software/Classes/" + m_ClassId)) { + // If doesn't exist (user didn't run the setup program), try to create the ClassId for current user. + if (!CreateClassId(QApplication::applicationFilePath(), "SMPlayer Media Player")) + return 0; + } + + int count = 0; + foreach(const QString & fileExtension, fileExtensions) { + QString ExtKeyName = QString("Software/Microsoft/Windows/CurrentVersion/Explorer/FileExts/.%1").arg(fileExtension); + QString ClassesKeyName = m_ClassId; + + QString BackupKeyName = ClassesKeyName + "/" + fileExtension; + QString CUKeyName = "Software/Classes/." + fileExtension; + + // Save current ClassId for current user + QString KeyVal = RegCU.value(CUKeyName + "/.").toString(); + + if (KeyVal.length() == 0 || KeyVal == m_ClassId) { + // No registered app for this extension for current user. + // Check the system-wide (HKEY_CLASSES_ROOT) ClassId for this extension + KeyVal = RegCR.value("." + fileExtension + "/.").toString(); + } + + if (KeyVal != m_ClassId) + RegCU.setValue(CUKeyName + "/MPlayer_Backup", KeyVal); + + // Save last ProgId and Application values from the Exts key + KeyVal = RegCU.value(ExtKeyName + "/Progid").toString(); + + if (KeyVal != m_ClassId && KeyVal != m_ClassId2) + RegCU.setValue(ExtKeyName + "/MPlayer_Backup_ProgId", KeyVal); + + KeyVal = RegCU.value(ExtKeyName + "/Application").toString(); + + if (KeyVal != m_ClassId || KeyVal != m_ClassId2) + RegCU.setValue(ExtKeyName + "/MPlayer_Backup_Application", KeyVal); + + // Create the associations + RegCU.setValue(CUKeyName + "/.", m_ClassId); // Extension class + RegCU.setValue(ExtKeyName + "/Progid", m_ClassId); // Explorer FileExt association + + if (RegCU.status() == QSettings::NoError && RegCR.status() == QSettings::NoError) + count++; + } + + return count; +} + +// Checks if extensions in extensionsToCheck are registered with this application. Returns a list of registered extensions. +// Returns false if there was an error accessing the registry. +// Returns true and 0 elements in registeredExtensions if no extension is associated with current app. +bool WinFileAssoc::GetRegisteredExtensions(const QStringList &extensionsToCheck, QStringList ®isteredExtensions) +{ + registeredExtensions.clear(); + + if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA) { + return VistaGetDefaultApps(extensionsToCheck, registeredExtensions); + } + + QSettings RegCR("HKEY_CLASSES_ROOT", QSettings::NativeFormat); + QSettings RegCU("HKEY_CURRENT_USER", QSettings::NativeFormat); + + if (RegCR.status() != QSettings::NoError) + return false; + + if (RegCU.status() != QSettings::NoError) + return false; + + foreach(const QString & fileExtension, extensionsToCheck) { + bool bRegistered = false; + // Check the Explorer extension (Always use this program to open this kind of file...) + + QString FileExtsKey = QString("Software/Microsoft/Windows/CurrentVersion/Explorer/FileExts/.%1").arg(fileExtension); + QString CurClassId = RegCU.value(FileExtsKey + "/Progid").toString(); + QString CurAppId = RegCU.value(FileExtsKey + "/Application").toString(); + + if (CurClassId.size()) { // Registered with Open With... / ProgId ? + bRegistered = (CurClassId == m_ClassId) || (0 == CurClassId.compare(m_ClassId2, Qt::CaseInsensitive)); + } else if (CurAppId.size()) { + // If user uses Open With..., explorer creates it's own ClassId under Application, usually "smplayer.exe" + bRegistered = (CurAppId == m_ClassId) || (0 == CurAppId.compare(m_ClassId2, Qt::CaseInsensitive)); + } else { + // No classId means that no associations exists in Default Programs or Explorer + // Check the default per-user association + bRegistered = RegCU.value("Software/Classes/." + fileExtension + "/.").toString() == m_ClassId; + } + + // Finally, check the system-wide association + if (!bRegistered) + bRegistered = RegCR.value("." + fileExtension + "/.").toString() == m_ClassId; + + if (bRegistered) + registeredExtensions.append(fileExtension); + } + + return true; +} + +// Restores file associations to old defaults (if any) for all extensions in the fileExtensions list. +// Cleans up our backup keys from the registry. +// Returns number of extensions successfully processed (error if fileExtensions.count() != return value && count > 0). +int WinFileAssoc::RestoreFileAssociations(const QStringList &fileExtensions) +{ + if (QSysInfo::WindowsVersion >= QSysInfo::WV_VISTA) + return 0; // Not supported by the API + + QSettings RegCR("HKEY_CLASSES_ROOT", QSettings::NativeFormat); + QSettings RegCU("HKEY_CURRENT_USER", QSettings::NativeFormat); + + if (!RegCU.isWritable() || RegCU.status() != QSettings::NoError) + return 0; + + if (RegCR.status() != QSettings::NoError) + return 0; + + int count = 0; + foreach(const QString & fileExtension, fileExtensions) { + QString ExtKeyName = QString("Software/Microsoft/Windows/CurrentVersion/Explorer/FileExts/.%1").arg(fileExtension); + QString OldProgId = RegCU.value(ExtKeyName + "/MPlayer_Backup_ProgId").toString(); + QString OldApp = RegCU.value(ExtKeyName + "/MPlayer_Backup_Application").toString(); + QString OldClassId = RegCU.value("Software/Classes/." + fileExtension + "/MPlayer_Backup").toString(); + + // Restore old explorer ProgId + if (!OldProgId.isEmpty() && OldProgId != m_ClassId) + RegCU.setValue(ExtKeyName + "/Progid", OldProgId); + else { + QString CurProgId = RegCU.value(ExtKeyName + "/Progid").toString(); + + if ((CurProgId == m_ClassId) || (0 == CurProgId.compare(m_ClassId2, Qt::CaseInsensitive))) //Only remove if we own it + RegCU.remove(ExtKeyName + "/Progid"); + } + + // Restore old explorer Application + if (!OldApp.isEmpty() && OldApp != m_ClassId) + RegCU.setValue(ExtKeyName + "/Application", OldApp); + else { + QString CurApp = RegCU.value(ExtKeyName + "/Application").toString(); + + if ((CurApp == m_ClassId) || (0 == CurApp.compare(m_ClassId2, Qt::CaseInsensitive))) //Only remove if we own it + RegCU.remove(ExtKeyName + "/Application"); + } + + // Restore old association for current user + if (!OldClassId.isEmpty() && OldClassId != m_ClassId) + RegCU.setValue("Software/Classes/." + fileExtension + "/.", OldClassId); + else { + if (RegCU.value("Software/Classes/." + fileExtension + "/.").toString() == m_ClassId) //Only remove if we own it + RegCU.remove("Software/Classes/." + fileExtension); + } + + // Remove our keys: + // CurrentUserClasses/.ext/MPlayerBackup + // Explorer: Backup_Application and Backup_ProgId + RegCU.remove("Software/Classes/." + fileExtension + "/MPlayer_Backup"); + RegCU.remove(ExtKeyName + "/MPlayer_Backup_Application"); + RegCU.remove(ExtKeyName + "/MPlayer_Backup_ProgId"); + } + return count; +} + +// Creates a ClassId for current application. +// Note: It's better to create the classId from the installation program. +bool WinFileAssoc::CreateClassId(const QString &executablePath, const QString &friendlyName) +{ + QString RootKeyName; + QString classId; + + classId = "Software/Classes/" + m_ClassId; + RootKeyName = "HKEY_CURRENT_USER"; + + QSettings Reg(RootKeyName, QSettings::NativeFormat); + + if (!Reg.isWritable() || Reg.status() != QSettings::NoError) + return false; + + QString appPath = executablePath; + appPath.replace('/', '\\'); // Explorer gives 'Access Denied' if we write the path with forward slashes to the registry + + // Add our ProgId to the HKCR classes + Reg.setValue(classId + "/shell/open/FriendlyAppName", friendlyName); + Reg.setValue(classId + "/shell/open/command/.", QString("\"%1\" \"%2\"").arg(appPath, "%1")); + Reg.setValue(classId + "/DefaultIcon/.", QString("\"%1\",1").arg(appPath)); + + // Add "Enqueue" command + Reg.setValue(classId + "/shell/enqueue/.", QObject::tr("Enqueue in SMPlayer")); + Reg.setValue(classId + "/shell/enqueue/command/.", QString("\"%1\" -add-to-playlist \"%2\"").arg(appPath, "%1")); + return true; +} + +// Remove ClassId from the registry. +// Called when no associations exist. Note: It's better to do this in the Setup program. +bool WinFileAssoc::RemoveClassId() +{ + QString RootKeyName; + QString classId; + + classId = "Software/Classes/" + m_ClassId; + RootKeyName = "HKEY_CURRENT_USER"; + + QSettings RegCU(RootKeyName, QSettings::NativeFormat); + + if (!RegCU.isWritable() || RegCU.status() != QSettings::NoError) + return false; + + RegCU.remove(classId); + return true; +} + +// Windows Vista specific implementation + +#if !defined(IApplicationAssociationRegistration) + +typedef enum tagASSOCIATIONLEVEL { + AL_MACHINE, + AL_EFFECTIVE, + AL_USER +} ASSOCIATIONLEVEL; + +typedef enum tagASSOCIATIONTYPE { + AT_FILEEXTENSION, + AT_URLPROTOCOL, + AT_STARTMENUCLIENT, + AT_MIMETYPE +} ASSOCIATIONTYPE; + +MIDL_INTERFACE("4e530b0a-e611-4c77-a3ac-9031d022281b") +IApplicationAssociationRegistration : +public IUnknown { +public: + virtual HRESULT STDMETHODCALLTYPE QueryCurrentDefault(LPCWSTR pszQuery, + ASSOCIATIONTYPE atQueryType, + ASSOCIATIONLEVEL alQueryLevel, + LPWSTR * ppszAssociation) = 0; + virtual HRESULT STDMETHODCALLTYPE QueryAppIsDefault(LPCWSTR pszQuery, + ASSOCIATIONTYPE atQueryType, + ASSOCIATIONLEVEL alQueryLevel, + LPCWSTR pszAppRegistryName, + BOOL * pfDefault) = 0; + virtual HRESULT STDMETHODCALLTYPE QueryAppIsDefaultAll(ASSOCIATIONLEVEL alQueryLevel, + LPCWSTR pszAppRegistryName, + BOOL * pfDefault) = 0; + virtual HRESULT STDMETHODCALLTYPE SetAppAsDefault(LPCWSTR pszAppRegistryName, + LPCWSTR pszSet, + ASSOCIATIONTYPE atSetType) = 0; + virtual HRESULT STDMETHODCALLTYPE SetAppAsDefaultAll(LPCWSTR pszAppRegistryName) = 0; + virtual HRESULT STDMETHODCALLTYPE ClearUserAssociations(void) = 0; +}; +#endif + +static const CLSID CLSID_ApplicationAssociationReg = {0x591209C7, 0x767B, 0x42B2, {0x9F, 0xBA, 0x44, 0xEE, 0x46, 0x15, 0xF2, 0xC7}}; +static const IID IID_IApplicationAssociationReg = {0x4e530b0a, 0xe611, 0x4c77, {0xa3, 0xac, 0x90, 0x31, 0xd0, 0x22, 0x28, 0x1b}}; + +int WinFileAssoc::VistaSetAppsAsDefault(const QStringList &fileExtensions) +{ + IApplicationAssociationRegistration *pAAR; + HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationReg, + NULL, CLSCTX_INPROC, IID_IApplicationAssociationReg, (void **)&pAAR); + + int count = 0; + + if (SUCCEEDED(hr) && (pAAR != NULL)) { + foreach(const QString & fileExtension, fileExtensions) { + hr = pAAR->SetAppAsDefault((const WCHAR *)m_AppName.utf16(), + (const WCHAR *)QString("." + fileExtension).utf16(), + AT_FILEEXTENSION); + + if (SUCCEEDED(hr)) + count++; + } + pAAR->Release(); + } + + return count; +} + +bool WinFileAssoc::VistaGetDefaultApps(const QStringList &extensions, QStringList ®isteredExt) +{ + IApplicationAssociationRegistration *pAAR; + + HRESULT hr = CoCreateInstance(CLSID_ApplicationAssociationReg, + NULL, CLSCTX_INPROC, IID_IApplicationAssociationReg, (void **)&pAAR); + + if (SUCCEEDED(hr) && (pAAR != NULL)) { + foreach(const QString & fileExtension, extensions) { + BOOL bIsDefault = false; + hr = pAAR->QueryAppIsDefault((const WCHAR *)QString("." + fileExtension).utf16(), + AT_FILEEXTENSION, + AL_EFFECTIVE, + (const WCHAR *)m_AppName.utf16(), + &bIsDefault); + + if (SUCCEEDED(hr) && bIsDefault) { + registeredExt.append(fileExtension); + } + } + + pAAR->Release(); + return true; + } + + return false; +} diff --git a/src/qmmpui/winfileassoc.h b/src/qmmpui/winfileassoc.h new file mode 100644 index 000000000..e3c34d97d --- /dev/null +++ b/src/qmmpui/winfileassoc.h @@ -0,0 +1,51 @@ +/* smplayer, GUI front-end for mplayer. + Copyright (C) 2006-2014 Ricardo Villalba <rvm@users.sourceforge.net> + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + + Winfileassoc.h + Handles file associations in Windows + Author: Florin Braghis (florin@libertv.ro) +*/ + +#ifndef WINFILEASSOC_H +#define WINFILEASSOC_H + +#include <QString> +#include <QStringList> + +class WinFileAssoc +{ +protected: + QString m_ClassId; //Application ClassId + QString m_ClassId2; //The ClassId created by explorer if user selects 'Open With...', usually smplayer.exe + QString m_AppName; + +protected: + bool CreateClassId(const QString& executablePath, const QString& friendlyName); + bool RemoveClassId(); + bool VistaGetDefaultApps(const QStringList &extensions, QStringList& registeredExt); + int VistaSetAppsAsDefault(const QStringList& extensions); +public: + WinFileAssoc(const QString ClassId = "MPlayerFileVideo", const QString AppName="SMPlayer"); + //Checks the registry to see which extensions are registered with SMPlayer + bool GetRegisteredExtensions(const QStringList& extensionsToCheck, QStringList& registeredExtensions); + + int CreateFileAssociations(const QStringList& fileExtensions); + int RestoreFileAssociations(const QStringList& fileExtensions); +}; + +#endif |
