1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2015 LoRd_MuldeR <MuldeR2@GMX.de>
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 2 of the License, or
8 // (at your option) any later version, but always including the *additional*
9 // restrictions defined in the "License.txt" file.
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.
16 // You should have received a copy of the GNU General Public License along
17 // with this program; if not, write to the Free Software Foundation, Inc.,
18 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
23 #include "ShellIntegration.h"
27 #include "Registry_Decoder.h"
30 #include <MUtils/Global.h>
31 #include <MUtils/Exception.h>
35 #include <QStringList>
37 #include <QApplication>
40 #include <QMutexLocker>
41 #include <QtConcurrentRun>
48 static const char *g_lamexpShellAction = "ConvertWithLameXP";
49 static const char *g_lamexpFileType = "LameXP.SupportedAudioFile";
52 QMutex ShellIntegration::m_mutex;
55 #define REG_WRITE_STRING(KEY, STR) RegSetValueEx(key, NULL, NULL, REG_SZ, reinterpret_cast<const BYTE*>(STR.utf16()), (STR.size() + 1) * sizeof(wchar_t))
57 ////////////////////////////////////////////////////////////
59 ////////////////////////////////////////////////////////////
61 ShellIntegration::ShellIntegration(void)
63 MUTILS_THROW("Cannot create instance of this class, sorry!");
66 ////////////////////////////////////////////////////////////
68 ////////////////////////////////////////////////////////////
70 void ShellIntegration::install(bool async)
72 //Install asynchronously
75 QFuture<void>(QtConcurrent::run(install, false));
80 QMutexLocker lock(&m_mutex);
86 const QString lamexpFileType(g_lamexpFileType);
87 const QString lamexpFileInfo(tr("Audio File supported by LameXP"));
88 const QString lamexpShellText(tr("Convert this file with LameXP v%1").arg(QString().sprintf("%d.%02d", lamexp_version_major(), lamexp_version_minor())));
89 const QString lamexpShellCommand = QString("\"%1\" \"--add=%2\"").arg(QDir::toNativeSeparators(QFileInfo(QApplication::applicationFilePath()).canonicalFilePath()), "%1");
90 const QString lamexpShellAction(g_lamexpShellAction);
92 //Register the LameXP file type
93 if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1").arg(lamexpFileType)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
95 REG_WRITE_STRING(key, lamexpFileInfo);
98 if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1\\shell").arg(lamexpFileType)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
100 REG_WRITE_STRING(key, lamexpShellAction);
103 if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1\\shell\\%2").arg(lamexpFileType, lamexpShellAction)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
105 REG_WRITE_STRING(key, lamexpShellText);
108 if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1\\shell\\%2\\command").arg(lamexpFileType, lamexpShellAction)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
110 REG_WRITE_STRING(key, lamexpShellCommand);
114 //Detect supported file types
115 QStringList *types = detectTypes(lamexpFileType, lamexpShellAction);
117 //Add LameXP shell action to all supported file types
118 while(types && (!types->isEmpty()))
120 QString currentType = types->takeFirst();
122 if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1\\shell\\%2").arg(currentType, lamexpShellAction)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
124 REG_WRITE_STRING(key, lamexpShellText);
128 if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1\\shell\\%2\\command").arg(currentType, lamexpShellAction)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
130 REG_WRITE_STRING(key, lamexpShellCommand);
136 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
142 void ShellIntegration::remove(bool async)
144 //Remove asynchronously
147 QFuture<void>(QtConcurrent::run(remove, false));
152 QMutexLocker lock(&m_mutex);
155 const QString lamexpFileType(g_lamexpFileType);
156 const QString lamexpShellAction(g_lamexpShellAction);
160 QStringList fileTypes;
162 //Find all registered file types
163 if(RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Classes", NULL, KEY_ENUMERATE_SUB_KEYS ,&key) == ERROR_SUCCESS)
167 for(DWORD i = 0; true; i++)
170 if(RegEnumKeyEx(key, i, name, &size, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
172 fileTypes << QString::fromUtf16(reinterpret_cast<const unsigned short*>(name));
179 //Remove shell action from all file types
180 while(!fileTypes.isEmpty())
182 SHDeleteKey(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1\\shell\\%2").arg(fileTypes.takeFirst(), lamexpShellAction)));
185 //Unregister LameXP file type
186 SHDeleteKey(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1").arg(lamexpFileType)));
189 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
192 ////////////////////////////////////////////////////////////
194 ////////////////////////////////////////////////////////////
196 QStringList *ShellIntegration::detectTypes(const QString &lamexpFileType, const QString &lamexpShellAction)
200 QStringList *nativeTypes = new QStringList();
201 QStringList supportedTypes = DecoderRegistry::getSupportedTypes();
202 QStringList extensions;
204 QRegExp regExp1("\\((.+)\\)");
205 QRegExp regExp2("(\\.\\w+)");
207 //Find all supported file extensions
208 while(!supportedTypes.isEmpty())
210 if(regExp1.lastIndexIn(supportedTypes.takeFirst()) > 0)
213 while((lastIndex = regExp2.indexIn(regExp1.cap(1), lastIndex) + 1) >= 1)
215 extensions.append(regExp2.cap(1));
220 //Map supported extensions to native types
221 while(!extensions.isEmpty())
223 QString currentExt = extensions.takeFirst();
225 if(RegOpenKeyEx(HKEY_CLASSES_ROOT, MUTILS_WCHR(currentExt), NULL, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
228 DWORD dataLen = 256 * sizeof(wchar_t);
231 if(RegQueryValueEx(key, NULL, NULL, &type, reinterpret_cast<BYTE*>(data), &dataLen) == ERROR_SUCCESS)
233 if((type == REG_SZ) || (type == REG_EXPAND_SZ))
235 QString currentType = QString::fromUtf16(reinterpret_cast<unsigned short*>(data));
236 if((currentType.compare(lamexpFileType, Qt::CaseInsensitive) != 0) && !nativeTypes->contains(currentType, Qt::CaseInsensitive))
238 nativeTypes->append(currentType);
246 if(RegCreateKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Classes\\%1").arg(currentExt)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
248 REG_WRITE_STRING(key, lamexpFileType);
253 if(RegOpenKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%1\\UserChoice").arg(currentExt)), NULL, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
256 DWORD dataLen = 256 * sizeof(wchar_t);
259 if(RegQueryValueEx(key, L"Progid", NULL, &type, reinterpret_cast<BYTE*>(data), &dataLen) == ERROR_SUCCESS)
261 if((type == REG_SZ) || (type == REG_EXPAND_SZ))
263 QString currentType = QString::fromUtf16(reinterpret_cast<unsigned short*>(data));
264 if((currentType.compare(lamexpFileType, Qt::CaseInsensitive) != 0) && !nativeTypes->contains(currentType, Qt::CaseInsensitive))
266 nativeTypes->append(currentType);
273 if(RegOpenKeyEx(HKEY_CURRENT_USER, MUTILS_WCHR(QString("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%1\\OpenWithProgids").arg(currentExt)), NULL, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
276 for(DWORD i = 0; true; i++)
279 if(RegEnumValue(key, i, name, &size, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
281 QString currentType = QString::fromUtf16(reinterpret_cast<unsigned short*>(name));
282 if((currentType.compare(lamexpFileType, Qt::CaseInsensitive) != 0) && !nativeTypes->contains(currentType, Qt::CaseInsensitive))
284 nativeTypes->append(currentType);