summaryrefslogtreecommitdiff
path: root/src/winfileassoc.cpp
diff options
context:
space:
mode:
authorMaia Kozheva <sikon@ubuntu.com>2009-11-20 18:22:19 +0600
committerMaia Kozheva <sikon@ubuntu.com>2009-11-20 18:22:19 +0600
commit263b32f108c15cd1c55a8f4eb4704fac6553f1ac (patch)
tree1c49e7848aa2d3d64a9d9b96b8852cb1884da2f7 /src/winfileassoc.cpp
Imported Upstream version 0.6.8
Diffstat (limited to 'src/winfileassoc.cpp')
-rw-r--r--src/winfileassoc.cpp449
1 files changed, 449 insertions, 0 deletions
diff --git a/src/winfileassoc.cpp b/src/winfileassoc.cpp
new file mode 100644
index 0000000..c823415
--- /dev/null
+++ b/src/winfileassoc.cpp
@@ -0,0 +1,449 @@
+/* smplayer, GUI front-end for mplayer.
+ Copyright (C) 2006-2009 Ricardo Villalba <rvm@escomposlinux.org>
+
+ 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 Vista/XP/2000/NT/ME/98/95.
+ 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.
+
+ Tested on: Win98, Win2000, WinXP, Vista.
+ NOT tested on: Win95, ME and NT 4.0 (it should work on 95, ME; Not sure about NT 4.0).
+
+ Author: Florin Braghis (florin@libertv.ro)
+*/
+
+#include "winfileassoc.h"
+#include <QSettings>
+#include <QApplication>
+#include <QFileInfo>
+
+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;
+
+ if (QSysInfo::WindowsVersion < QSysInfo::WV_NT && !RegCR.isWritable()) //Win98
+ 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
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_NT)
+ {
+ RegCU.setValue(CUKeyName + "/.", m_ClassId); //Extension class
+ RegCU.setValue(ExtKeyName + "/Progid", m_ClassId); //Explorer FileExt association
+
+ }
+ else
+ {
+ //Windows ME/98/95 support
+ RegCR.setValue("." + fileExtension + "/.", m_ClassId);
+ }
+
+ 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& registeredExtensions)
+{
+ 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...)
+
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_NT)
+ {
+ 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;
+
+ if (QSysInfo::WindowsVersion < QSysInfo::WV_NT && !RegCR.isWritable()) //Win98
+ 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");
+ }
+
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_NT)
+ {
+ //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);
+ }
+ }
+ else
+ {
+ //Windows 98 ==> Write to HKCR
+ if (!OldClassId.isEmpty() && OldClassId != m_ClassId)
+ RegCR.setValue("." + fileExtension + "/.", OldClassId);
+ else
+ {
+ if (RegCR.value("." + fileExtension + "/.").toString() == m_ClassId)
+ RegCR.remove("." + 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;
+
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_NT)
+ {
+ classId = "Software/Classes/" + m_ClassId;
+ RootKeyName = "HKEY_CURRENT_USER";
+ }
+ else
+ {
+ classId = m_ClassId;
+ RootKeyName = "HKEY_CLASSES_ROOT"; //Windows 95/98/ME
+ }
+
+ 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;
+
+ if (QSysInfo::WindowsVersion >= QSysInfo::WV_NT)
+ {
+ classId = "Software/Classes/" + m_ClassId;
+ RootKeyName = "HKEY_CURRENT_USER";
+ }
+ else
+ {
+ classId = m_ClassId;
+ RootKeyName = "HKEY_CLASSES_ROOT"; //Windows 95/98/ME
+ }
+
+ QSettings RegCU (RootKeyName, QSettings::NativeFormat);
+
+ if (!RegCU.isWritable() || RegCU.status() != QSettings::NoError)
+ return false;
+
+ RegCU.remove(classId);
+ return true;
+}
+
+//Windows Vista specific implementation
+//Add libole32.a library if compiling with mingw.
+//In smplayer.pro, under win32{ :
+// LIBS += libole32
+#ifdef WIN32
+#include <windows.h>
+
+#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& registeredExt)
+{
+ 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;
+}
+#else
+bool WinFileAssoc::VistaGetDefaultApps(const QStringList &extensions, QStringList& registeredExt)
+{
+ return false;
+}
+
+int WinFileAssoc::VistaSetAppsAsDefault(const QStringList& extensions)
+{
+ return 0;
+}
+#endif
+