OSDN Git Service

Updated Monkey's Audio binary to v4.11 (2013-01-20), including STDERR flush fix.
[lamexp/LameXP.git] / src / ShellIntegration.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2013 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
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.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License along
16 // with this program; if not, write to the Free Software Foundation, Inc.,
17 // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 //
19 // http://www.gnu.org/licenses/gpl-2.0.txt
20 ///////////////////////////////////////////////////////////////////////////////
21
22 #include "ShellIntegration.h"
23
24 #include <QString>
25 #include <QStringList>
26 #include <QRegExp>
27 #include <QApplication>
28 #include <QFileInfo>
29 #include <QDir>
30 #include <QMutexLocker>
31 #include <QtConcurrentRun>
32
33 #include <Shlobj.h>
34 #include <Shlwapi.h>
35
36 #include "Global.h"
37 #include "Registry_Decoder.h"
38
39 //Const
40 static const char *g_lamexpShellAction = "ConvertWithLameXP";
41 static const char *g_lamexpFileType = "LameXP.SupportedAudioFile";
42
43 //Mutex
44 QMutex ShellIntegration::m_mutex;
45
46 //Macros
47 #define REG_WRITE_STRING(KEY, STR) RegSetValueEx(key, NULL, NULL, REG_SZ, reinterpret_cast<const BYTE*>(STR.utf16()), (STR.size() + 1) * sizeof(wchar_t))
48
49 ////////////////////////////////////////////////////////////
50 // Constructor
51 ////////////////////////////////////////////////////////////
52
53 ShellIntegration::ShellIntegration(void)
54 {
55         throw "Cannot create instance of this class, sorry!";
56 }
57
58 ////////////////////////////////////////////////////////////
59 // Public Functions
60 ////////////////////////////////////////////////////////////
61
62 void ShellIntegration::install(bool async)
63 {
64         //Install asynchronously
65         if(async)
66         {
67                 QFuture<void>(QtConcurrent::run(install, false));
68                 return;
69         }
70         
71         //Serialize
72         QMutexLocker lock(&m_mutex);
73
74         //Registry key
75         HKEY key = NULL;
76         
77         //Init some consts
78         const QString lamexpFileType(g_lamexpFileType);
79         const QString lamexpFileInfo(tr("Audio File supported by LameXP"));
80         const QString lamexpShellText(tr("Convert this file with LameXP v%1").arg(QString().sprintf("%d.%02d", lamexp_version_major(), lamexp_version_minor())));
81         const QString lamexpShellCommand = QString("\"%1\" --add \"%2\"").arg(QDir::toNativeSeparators(QFileInfo(QApplication::applicationFilePath()).canonicalFilePath()), "%1");
82         const QString lamexpShellAction(g_lamexpShellAction);
83
84         //Register the LameXP file type
85         if(RegCreateKeyEx(HKEY_CURRENT_USER, QWCHAR(QString("Software\\Classes\\%1").arg(lamexpFileType)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
86         {
87                 REG_WRITE_STRING(key, lamexpFileInfo);
88                 RegCloseKey(key);
89         }
90         if(RegCreateKeyEx(HKEY_CURRENT_USER, QWCHAR(QString("Software\\Classes\\%1\\shell").arg(lamexpFileType)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
91         {
92                 REG_WRITE_STRING(key, lamexpShellAction);
93                 RegCloseKey(key);
94         }
95         if(RegCreateKeyEx(HKEY_CURRENT_USER, QWCHAR(QString("Software\\Classes\\%1\\shell\\%2").arg(lamexpFileType, lamexpShellAction)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
96         {
97                 REG_WRITE_STRING(key, lamexpShellText);
98                 RegCloseKey(key);
99         }
100         if(RegCreateKeyEx(HKEY_CURRENT_USER, QWCHAR(QString("Software\\Classes\\%1\\shell\\%2\\command").arg(lamexpFileType, lamexpShellAction)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
101         {
102                 REG_WRITE_STRING(key, lamexpShellCommand);
103                 RegCloseKey(key);
104         }
105
106         //Detect supported file types
107         QStringList *types = detectTypes(lamexpFileType, lamexpShellAction);
108
109         //Add LameXP shell action to all supported file types
110         while(types && (!types->isEmpty()))
111         {
112                 QString currentType = types->takeFirst();
113
114                 if(RegCreateKeyEx(HKEY_CURRENT_USER, QWCHAR(QString("Software\\Classes\\%1\\shell\\%2").arg(currentType, lamexpShellAction)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
115                 {
116                         REG_WRITE_STRING(key, lamexpShellText);
117                         RegCloseKey(key);
118                 }
119
120                 if(RegCreateKeyEx(HKEY_CURRENT_USER, QWCHAR(QString("Software\\Classes\\%1\\shell\\%2\\command").arg(currentType, lamexpShellAction)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
121                 {
122                         REG_WRITE_STRING(key, lamexpShellCommand);
123                         RegCloseKey(key);
124                 }
125         }
126         
127         //Shell notification
128         SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
129
130         //Free
131         delete types;
132 }
133
134 void ShellIntegration::remove(bool async)
135 {
136         //Remove asynchronously
137         if(async)
138         {
139                 QFuture<void>(QtConcurrent::run(remove, false));
140                 return;
141         }
142
143         //Serialize
144         QMutexLocker lock(&m_mutex);
145         
146         //Init some consts
147         const QString lamexpFileType(g_lamexpFileType);
148         const QString lamexpShellAction(g_lamexpShellAction);
149
150         //Initialization
151         HKEY key = NULL;
152         QStringList fileTypes;
153
154         //Find all registered file types
155         if(RegOpenKeyEx(HKEY_CURRENT_USER, L"Software\\Classes", NULL, KEY_ENUMERATE_SUB_KEYS ,&key) == ERROR_SUCCESS)
156         {
157                 wchar_t name[256];
158                 
159                 for(DWORD i = 0; true; i++)
160                 {
161                         DWORD size = 256;
162                         if(RegEnumKeyEx(key, i, name, &size, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
163                         {
164                                 fileTypes << QString::fromUtf16(reinterpret_cast<const unsigned short*>(name));
165                                 continue;
166                         }
167                         break;
168                 }
169         }
170
171         //Remove shell action from all file types
172         while(!fileTypes.isEmpty())
173         {
174                 SHDeleteKey(HKEY_CURRENT_USER, QWCHAR(QString("Software\\Classes\\%1\\shell\\%2").arg(fileTypes.takeFirst(), lamexpShellAction)));
175         }
176
177         //Unregister LameXP file type
178         SHDeleteKey(HKEY_CURRENT_USER, QWCHAR(QString("Software\\Classes\\%1").arg(lamexpFileType)));
179
180         //Shell notification
181         SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
182 }
183
184 ////////////////////////////////////////////////////////////
185 // Private Functions
186 ////////////////////////////////////////////////////////////
187
188 QStringList *ShellIntegration::detectTypes(const QString &lamexpFileType, const QString &lamexpShellAction)
189 {
190         HKEY key = NULL;
191
192         QStringList *nativeTypes = new QStringList();
193         QStringList supportedTypes = DecoderRegistry::getSupportedTypes();
194         QStringList extensions;
195
196         QRegExp regExp1("\\((.+)\\)");
197         QRegExp regExp2("(\\.\\w+)");
198
199         //Find all supported file extensions
200         while(!supportedTypes.isEmpty())
201         {
202                 if(regExp1.lastIndexIn(supportedTypes.takeFirst()) > 0)
203                 {
204                         int lastIndex = 0;
205                         while((lastIndex = regExp2.indexIn(regExp1.cap(1), lastIndex) + 1) >= 1)
206                         {
207                                 extensions.append(regExp2.cap(1));
208                         }
209                 }
210         }
211
212         //Map supported extensions to native types
213         while(!extensions.isEmpty())
214         {
215                 QString currentExt = extensions.takeFirst();
216
217                 if(RegOpenKeyEx(HKEY_CLASSES_ROOT, QWCHAR(currentExt), NULL, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
218                 {
219                         wchar_t data[256];
220                         DWORD dataLen = 256 * sizeof(wchar_t);
221                         DWORD type = NULL;
222                                 
223                         if(RegQueryValueEx(key, NULL, NULL, &type, reinterpret_cast<BYTE*>(data), &dataLen) == ERROR_SUCCESS)
224                         {
225                                 if((type == REG_SZ) || (type == REG_EXPAND_SZ))
226                                 {
227                                         QString currentType = QString::fromUtf16(reinterpret_cast<unsigned short*>(data));
228                                         if((currentType.compare(lamexpFileType, Qt::CaseInsensitive) != 0) && !nativeTypes->contains(currentType, Qt::CaseInsensitive))
229                                         {
230                                                 nativeTypes->append(currentType);
231                                         }
232                                 }
233                         }
234                         RegCloseKey(key);
235                 }
236                 else
237                 {
238                         if(RegCreateKeyEx(HKEY_CURRENT_USER, QWCHAR(QString("Software\\Classes\\%1").arg(currentExt)), NULL, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL) == ERROR_SUCCESS)
239                         {
240                                 REG_WRITE_STRING(key, lamexpFileType);
241                                 RegCloseKey(key);
242                         }
243                 }
244
245                 if(RegOpenKeyEx(HKEY_CURRENT_USER, QWCHAR(QString("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%1\\UserChoice").arg(currentExt)), NULL, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
246                 {
247                         wchar_t data[256];
248                         DWORD dataLen = 256 * sizeof(wchar_t);
249                         DWORD type = NULL;
250                                 
251                         if(RegQueryValueEx(key, L"Progid", NULL, &type, reinterpret_cast<BYTE*>(data), &dataLen) == ERROR_SUCCESS)
252                         {
253                                 if((type == REG_SZ) || (type == REG_EXPAND_SZ))
254                                 {
255                                         QString currentType = QString::fromUtf16(reinterpret_cast<unsigned short*>(data));
256                                         if((currentType.compare(lamexpFileType, Qt::CaseInsensitive) != 0) && !nativeTypes->contains(currentType, Qt::CaseInsensitive))
257                                         {
258                                                 nativeTypes->append(currentType);
259                                         }
260                                 }
261                         }
262                         RegCloseKey(key);
263                 }
264
265                 if(RegOpenKeyEx(HKEY_CURRENT_USER, QWCHAR(QString("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\FileExts\\%1\\OpenWithProgids").arg(currentExt)), NULL, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS)
266                 {
267                         wchar_t name[256];
268                         for(DWORD i = 0; true; i++)
269                         {
270                                 DWORD size = 256;
271                                 if(RegEnumValue(key, i, name, &size, NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
272                                 {
273                                         QString currentType = QString::fromUtf16(reinterpret_cast<unsigned short*>(name));
274                                         if((currentType.compare(lamexpFileType, Qt::CaseInsensitive) != 0) && !nativeTypes->contains(currentType, Qt::CaseInsensitive))
275                                         {
276                                                 nativeTypes->append(currentType);
277                                         }
278                                         continue;
279                                 }
280                                 break;
281                         }
282                         RegCloseKey(key);
283                 }
284         }
285
286         return nativeTypes;
287 }