OSDN Git Service

f476a7bcff4ddbbea850c8b7b1b3f51efc0caf65
[lamexp/LameXP.git] / src / Global_Win32.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // LameXP - Audio Encoder Front-End
3 // Copyright (C) 2004-2014 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, but always including the *additional*
9 // restrictions defined in the "License.txt" file.
10 //
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.
15 //
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.
19 //
20 // http://www.gnu.org/licenses/gpl-2.0.txt
21 ///////////////////////////////////////////////////////////////////////////////
22
23 #include "Global.h"
24
25 //Target version
26 #include "Targetver.h"
27
28 //Visual Leaks Detector
29 #include <vld.h>
30
31 //Windows includes
32 #define NOMINMAX
33 #define WIN32_LEAN_AND_MEAN
34 #include <Windows.h>
35 #include <MMSystem.h>
36 #include <ShellAPI.h>
37 #include <SensAPI.h>
38 #include <Objbase.h>
39 #include <PowrProf.h>
40 #include <Psapi.h>
41 #include <dwmapi.h>
42
43 //Qt includes
44 #include <QApplication>
45 #include <QDate>
46 #include <QDir>
47 #include <QEvent>
48 #include <QIcon>
49 #include <QImageReader>
50 #include <QLibrary>
51 #include <QLibraryInfo>
52 #include <QMap>
53 #include <QMessageBox>
54 #include <QMutex>
55 #include <QPlastiqueStyle>
56 #include <QProcess>
57 #include <QReadWriteLock>
58 #include <QRegExp>
59 #include <QSysInfo>
60 #include <QTextCodec>
61 #include <QTimer>
62 #include <QTranslator>
63 #include <QUuid>
64 #include <QResource>
65
66 //LameXP includes
67 #define LAMEXP_INC_CONFIG
68 #include "Resource.h"
69 #include "Config.h"
70
71 //CRT includes
72 #include <cstdio>
73 #include <iostream>
74 #include <fstream>
75 #include <io.h>
76 #include <fcntl.h>
77 #include <intrin.h>
78 #include <cmath>
79 #include <ctime>
80 #include <process.h>
81 #include <csignal>
82
83 //Initialize static Qt plugins
84 #ifdef QT_NODLL
85 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
86 Q_IMPORT_PLUGIN(qico)
87 Q_IMPORT_PLUGIN(qsvg)
88 #else
89 Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
90 Q_IMPORT_PLUGIN(QICOPlugin)
91 #endif
92 #endif
93
94 ///////////////////////////////////////////////////////////////////////////////
95 // HELPER MACROS
96 ///////////////////////////////////////////////////////////////////////////////
97
98 #define _LAMEXP_MAKE_STR(STR) #STR
99 #define LAMEXP_MAKE_STR(STR) _LAMEXP_MAKE_STR(STR)
100
101 //String helper
102 #define CLEAN_OUTPUT_STRING(STR) do \
103 { \
104         const char CTRL_CHARS[3] = { '\r', '\n', '\t' }; \
105         for(size_t i = 0; i < 3; i++) \
106         { \
107                 while(char *pos = strchr((STR), CTRL_CHARS[i])) *pos = char(0x20); \
108         } \
109 } \
110 while(0)
111
112 //String helper
113 #define TRIM_LEFT(STR) do \
114 { \
115         const char WHITE_SPACE[4] = { char(0x20), '\r', '\n', '\t' }; \
116         for(size_t i = 0; i < 4; i++) \
117         { \
118                 while(*(STR) == WHITE_SPACE[i]) (STR)++; \
119         } \
120 } \
121 while(0)
122
123 ///////////////////////////////////////////////////////////////////////////////
124 // GLOBAL TYPES
125 ///////////////////////////////////////////////////////////////////////////////
126
127 typedef HRESULT (WINAPI *SHGetKnownFolderPath_t)(const GUID &rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath);
128 typedef HRESULT (WINAPI *SHGetFolderPath_t)(HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath);
129
130 ///////////////////////////////////////////////////////////////////////////////
131 // GLOBAL VARS
132 ///////////////////////////////////////////////////////////////////////////////
133
134 //Console attached flag
135 static bool g_lamexp_console_attached = false;
136
137 //Special folders
138 static struct
139 {
140         QMap<size_t, QString> *knownFolders;
141         SHGetKnownFolderPath_t getKnownFolderPath;
142         SHGetFolderPath_t getFolderPath;
143         QReadWriteLock lock;
144 }
145 g_lamexp_known_folder;
146
147 //CLI Arguments
148 static struct
149 {
150         QStringList *list;
151         QReadWriteLock lock;
152 }
153 g_lamexp_argv;
154
155 //OS Version
156 static struct
157 {
158         bool bInitialized;
159         lamexp_os_version_t version;
160         QReadWriteLock lock;
161 }
162 g_lamexp_os_version;
163
164 //Wine detection
165 static struct
166 {
167         bool bInitialized;
168         bool bIsWine;
169         QReadWriteLock lock;
170 }
171 g_lamexp_wine;
172
173 //Win32 Theme support
174 static struct
175 {
176         bool bInitialized;
177         bool bThemesEnabled;
178         QReadWriteLock lock;
179 }
180 g_lamexp_themes_enabled;
181
182 //Win32 DWM API functions
183 static struct
184 {
185         bool bInitialized;
186         HRESULT (__stdcall *dwmIsCompositionEnabled)(BOOL *bEnabled);
187         HRESULT (__stdcall *dwmExtendFrameIntoClientArea)(HWND hWnd, const MARGINS* pMarInset);
188         HRESULT (__stdcall *dwmEnableBlurBehindWindow)(HWND hWnd, const DWM_BLURBEHIND* pBlurBehind);
189         QLibrary *dwmapi_dll;
190         QReadWriteLock lock;
191 }
192 g_lamexp_dwmapi;
193
194 //Sound file cache
195 static struct
196 {
197         QHash<const QString, const unsigned char*> *sound_db;
198         QReadWriteLock lock;
199 }
200 g_lamexp_sounds;
201
202 //Image formats
203 static const char *g_lamexp_imageformats[] = {"bmp", "png", "jpg", "gif", "ico", "xpm", NULL}; //"svg"
204
205 //Global locks
206 static QMutex g_lamexp_message_mutex;
207
208 //Main thread ID
209 static const DWORD g_main_thread_id = GetCurrentThreadId();
210
211 //Log file
212 static FILE *g_lamexp_log_file = NULL;
213
214 //Localization
215 const char* LAMEXP_DEFAULT_LANGID = "en";
216 const char* LAMEXP_DEFAULT_TRANSLATION = "LameXP_EN.qm";
217
218 //Known Windows versions - maps marketing names to the actual Windows NT versions
219 const lamexp_os_version_t lamexp_winver_win2k = {5,0};
220 const lamexp_os_version_t lamexp_winver_winxp = {5,1};
221 const lamexp_os_version_t lamexp_winver_xpx64 = {5,2};
222 const lamexp_os_version_t lamexp_winver_vista = {6,0};
223 const lamexp_os_version_t lamexp_winver_win70 = {6,1};
224 const lamexp_os_version_t lamexp_winver_win80 = {6,2};
225 const lamexp_os_version_t lamexp_winver_win81 = {6,3};
226
227 //GURU MEDITATION
228 static const char *GURU_MEDITATION = "\n\nGURU MEDITATION !!!\n\n";
229
230 ///////////////////////////////////////////////////////////////////////////////
231 // GLOBAL FUNCTIONS
232 ///////////////////////////////////////////////////////////////////////////////
233
234 /*
235  * Verify a specific Windows version
236  */
237 static bool lamexp_verify_os_version(const DWORD major, const DWORD minor)
238 {
239         OSVERSIONINFOEXW osvi;
240         DWORDLONG dwlConditionMask = 0;
241
242         //Initialize the OSVERSIONINFOEX structure
243         memset(&osvi, 0, sizeof(OSVERSIONINFOEXW));
244         osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
245         osvi.dwMajorVersion = major;
246         osvi.dwMinorVersion = minor;
247         osvi.dwPlatformId = VER_PLATFORM_WIN32_NT;
248
249         //Initialize the condition mask
250         VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
251         VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
252         VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);
253
254         // Perform the test
255         const BOOL ret = VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID, dwlConditionMask);
256
257         //Error checking
258         if(!ret)
259         {
260                 if(GetLastError() != ERROR_OLD_WIN_VERSION)
261                 {
262                         qWarning("VerifyVersionInfo() system call has failed!");
263                 }
264         }
265
266         return (ret != FALSE);
267 }
268
269 /*
270  * Determine the *real* Windows version
271  */
272 static bool lamexp_get_real_os_version(unsigned int *major, unsigned int *minor, bool *pbOverride)
273 {
274         *major = *minor = 0;
275         *pbOverride = false;
276         
277         //Initialize local variables
278         OSVERSIONINFOEXW osvi;
279         memset(&osvi, 0, sizeof(OSVERSIONINFOEXW));
280         osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
281
282         //Try GetVersionEx() first
283         if(GetVersionExW((LPOSVERSIONINFOW)&osvi) == FALSE)
284         {
285                 qWarning("GetVersionEx() has failed, cannot detect Windows version!");
286                 return false;
287         }
288
289         //Make sure we are running on NT
290         if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
291         {
292                 *major = osvi.dwMajorVersion;
293                 *minor = osvi.dwMinorVersion;
294         }
295         else
296         {
297                 qWarning("Not running on Windows NT, unsupported operating system!");
298                 return false;
299         }
300
301         //Determine the real *major* version first
302         forever
303         {
304                 const DWORD nextMajor = (*major) + 1;
305                 if(lamexp_verify_os_version(nextMajor, 0))
306                 {
307                         *pbOverride = true;
308                         *major = nextMajor;
309                         *minor = 0;
310                         continue;
311                 }
312                 break;
313         }
314
315         //Now also determine the real *minor* version
316         forever
317         {
318                 const DWORD nextMinor = (*minor) + 1;
319                 if(lamexp_verify_os_version((*major), nextMinor))
320                 {
321                         *pbOverride = true;
322                         *minor = nextMinor;
323                         continue;
324                 }
325                 break;
326         }
327
328         return true;
329 }
330
331 /*
332  * Get the native operating system version
333  */
334 const lamexp_os_version_t &lamexp_get_os_version(void)
335 {
336         QReadLocker readLock(&g_lamexp_os_version.lock);
337
338         //Already initialized?
339         if(g_lamexp_os_version.bInitialized)
340         {
341                 return g_lamexp_os_version.version;
342         }
343         
344         readLock.unlock();
345         QWriteLocker writeLock(&g_lamexp_os_version.lock);
346
347         //Detect OS version
348         if(!g_lamexp_os_version.bInitialized)
349         {
350                 unsigned int major, minor; bool oflag;
351                 if(lamexp_get_real_os_version(&major, &minor, &oflag))
352                 {
353                         g_lamexp_os_version.version.versionMajor = major;
354                         g_lamexp_os_version.version.versionMinor = minor;
355                         g_lamexp_os_version.version.overrideFlag = oflag;
356                         g_lamexp_os_version.bInitialized = true;
357                 }
358                 else
359                 {
360                         qWarning("Failed to determin the operating system version!");
361                 }
362         }
363
364         return g_lamexp_os_version.version;
365 }
366
367 /*
368  * Check if we are running under wine
369  */
370 bool lamexp_detect_wine(void)
371 {
372         QReadLocker readLock(&g_lamexp_wine.lock);
373
374         //Already initialized?
375         if(g_lamexp_wine.bInitialized)
376         {
377                 return g_lamexp_wine.bIsWine;
378         }
379         
380         readLock.unlock();
381         QWriteLocker writeLock(&g_lamexp_wine.lock);
382
383         if(!g_lamexp_wine.bInitialized)
384         {
385                 g_lamexp_wine.bIsWine = false;
386                 QLibrary ntdll("ntdll.dll");
387                 if(ntdll.load())
388                 {
389                         if(ntdll.resolve("wine_nt_to_unix_file_name") != NULL) g_lamexp_wine.bIsWine = true;
390                         if(ntdll.resolve("wine_get_version") != NULL) g_lamexp_wine.bIsWine = true;
391                         ntdll.unload();
392                 }
393                 g_lamexp_wine.bInitialized = true;
394         }
395
396         return g_lamexp_wine.bIsWine;
397 }
398
399 /*
400  * Change console text color
401  */
402 static void lamexp_console_color(FILE* file, WORD attributes)
403 {
404         const HANDLE hConsole = (HANDLE)(_get_osfhandle(_fileno(file)));
405         if((hConsole != NULL) && (hConsole != INVALID_HANDLE_VALUE))
406         {
407                 SetConsoleTextAttribute(hConsole, attributes);
408         }
409 }
410
411 /*
412  * Output logging message to console
413  */
414 static void lamexp_write_console(const int type, const char *msg)
415 {       
416         __try
417         {
418                 if(_isatty(_fileno(stderr)))
419                 {
420                         UINT oldOutputCP = GetConsoleOutputCP();
421                         if(oldOutputCP != CP_UTF8) SetConsoleOutputCP(CP_UTF8);
422
423                         switch(type)
424                         {
425                         case QtCriticalMsg:
426                         case QtFatalMsg:
427                                 lamexp_console_color(stderr, FOREGROUND_RED | FOREGROUND_INTENSITY);
428                                 fprintf(stderr, GURU_MEDITATION);
429                                 fprintf(stderr, "%s\n", msg);
430                                 fflush(stderr);
431                                 break;
432                         case QtWarningMsg:
433                                 lamexp_console_color(stderr, FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY);
434                                 fprintf(stderr, "%s\n", msg);
435                                 fflush(stderr);
436                                 break;
437                         default:
438                                 lamexp_console_color(stderr, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY);
439                                 fprintf(stderr, "%s\n", msg);
440                                 fflush(stderr);
441                                 break;
442                         }
443         
444                         lamexp_console_color(stderr, FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED);
445                         if(oldOutputCP != CP_UTF8) SetConsoleOutputCP(oldOutputCP);
446                 }
447         }
448         __except(1)
449         {
450                 /*ignore any exception that might occur here!*/
451         }
452 }
453
454 /*
455  * Output logging message to debugger
456  */
457 static void lamexp_write_dbg_out(const int type, const char *msg)
458 {       
459         const char *FORMAT = "[LameXP][%c] %s\n";
460
461         __try
462         {
463                 char buffer[512];
464                 const char* input = msg;
465                 TRIM_LEFT(input);
466                 
467                 switch(type)
468                 {
469                 case QtCriticalMsg:
470                 case QtFatalMsg:
471                         _snprintf_s(buffer, 512, _TRUNCATE, FORMAT, 'C', input);
472                         break;
473                 case QtWarningMsg:
474                         _snprintf_s(buffer, 512, _TRUNCATE, FORMAT, 'W', input);
475                         break;
476                 default:
477                         _snprintf_s(buffer, 512, _TRUNCATE, FORMAT, 'I', input);
478                         break;
479                 }
480
481                 char *temp = &buffer[0];
482                 CLEAN_OUTPUT_STRING(temp);
483                 OutputDebugStringA(temp);
484         }
485         __except(1)
486         {
487                 /*ignore any exception that might occur here!*/
488         }
489 }
490
491 /*
492  * Output logging message to logfile
493  */
494 static void lamexp_write_logfile(const int type, const char *msg)
495 {       
496         const char *FORMAT = "[%c][%04u] %s\r\n";
497
498         __try
499         {
500                 if(g_lamexp_log_file)
501                 {
502                         char buffer[512];
503                         strncpy_s(buffer, 512, msg, _TRUNCATE);
504
505                         char *temp = &buffer[0];
506                         TRIM_LEFT(temp);
507                         CLEAN_OUTPUT_STRING(temp);
508                         
509                         const unsigned int timestamp = static_cast<unsigned int>(_time64(NULL) % 3600I64);
510
511                         switch(type)
512                         {
513                         case QtCriticalMsg:
514                         case QtFatalMsg:
515                                 fprintf(g_lamexp_log_file, FORMAT, 'C', timestamp, temp);
516                                 break;
517                         case QtWarningMsg:
518                                 fprintf(g_lamexp_log_file, FORMAT, 'W', timestamp, temp);
519                                 break;
520                         default:
521                                 fprintf(g_lamexp_log_file, FORMAT, 'I', timestamp, temp);
522                                 break;
523                         }
524
525                         fflush(g_lamexp_log_file);
526                 }
527         }
528         __except(1)
529         {
530                 /*ignore any exception that might occur here!*/
531         }
532 }
533
534 /*
535  * Qt message handler
536  */
537 void lamexp_message_handler(QtMsgType type, const char *msg)
538 {
539         if((!msg) || (!(msg[0])))
540         {
541                 return;
542         }
543
544         QMutexLocker lock(&g_lamexp_message_mutex);
545
546         if(g_lamexp_log_file)
547         {
548                 lamexp_write_logfile(type, msg);
549         }
550
551         if(g_lamexp_console_attached)
552         {
553                 lamexp_write_console(type, msg);
554         }
555         else
556         {
557                 lamexp_write_dbg_out(type, msg);
558         }
559
560         if((type == QtCriticalMsg) || (type == QtFatalMsg))
561         {
562                 lock.unlock();
563                 lamexp_fatal_exit(L"The application has encountered a critical error and will exit now!", QWCHAR(QString::fromUtf8(msg)));
564         }
565 }
566
567 /*
568  * Invalid parameters handler
569  */
570 static void lamexp_invalid_param_handler(const wchar_t* exp, const wchar_t* fun, const wchar_t* fil, unsigned int, uintptr_t)
571 {
572         lamexp_fatal_exit(L"Invalid parameter handler invoked, application will exit!");
573 }
574
575 /*
576  * Signal handler
577  */
578 static void lamexp_signal_handler(int signal_num)
579 {
580         signal(signal_num, lamexp_signal_handler);
581         lamexp_fatal_exit(L"Signal handler invoked, application will exit!");
582 }
583
584 /*
585  * Global exception handler
586  */
587 static LONG WINAPI lamexp_exception_handler(struct _EXCEPTION_POINTERS *ExceptionInfo)
588 {
589         lamexp_fatal_exit(L"Unhandeled exception handler invoked, application will exit!");
590         return LONG_MAX;
591 }
592
593 /*
594  * Initialize error handlers
595  */
596 void lamexp_init_error_handlers(void)
597 {
598         SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);
599         SetUnhandledExceptionFilter(lamexp_exception_handler);
600         SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
601         _set_invalid_parameter_handler(lamexp_invalid_param_handler);
602         
603         static const int signal_num[6] = { SIGABRT, SIGFPE, SIGILL, SIGINT, SIGSEGV, SIGTERM };
604
605         for(size_t i = 0; i < 6; i++)
606         {
607                 signal(signal_num[i], lamexp_signal_handler);
608         }
609 }
610
611 /*
612  * Initialize the console
613  */
614 void lamexp_init_console(const QStringList &argv)
615 {
616         bool enableConsole = (LAMEXP_DEBUG) || ((VER_LAMEXP_CONSOLE_ENABLED) && lamexp_version_demo());
617
618         if(_environ)
619         {
620                 wchar_t *logfile = NULL;
621                 size_t logfile_len = 0;
622                 if(!_wdupenv_s(&logfile, &logfile_len, L"LAMEXP_LOGFILE"))
623                 {
624                         if(logfile && (logfile_len > 0))
625                         {
626                                 FILE *temp = NULL;
627                                 if(!_wfopen_s(&temp, logfile, L"wb"))
628                                 {
629                                         fprintf(temp, "%c%c%c", char(0xEF), char(0xBB), char(0xBF));
630                                         g_lamexp_log_file = temp;
631                                 }
632                                 free(logfile);
633                         }
634                 }
635         }
636
637         if(!LAMEXP_DEBUG)
638         {
639                 for(int i = 0; i < argv.count(); i++)
640                 {
641                         if(!argv.at(i).compare("--console", Qt::CaseInsensitive))
642                         {
643                                 enableConsole = true;
644                         }
645                         else if(!argv.at(i).compare("--no-console", Qt::CaseInsensitive))
646                         {
647                                 enableConsole = false;
648                         }
649                 }
650         }
651
652         if(enableConsole)
653         {
654                 if(!g_lamexp_console_attached)
655                 {
656                         if(AllocConsole() != FALSE)
657                         {
658                                 SetConsoleCtrlHandler(NULL, TRUE);
659                                 SetConsoleTitle(L"LameXP - Audio Encoder Front-End | Debug Console");
660                                 SetConsoleOutputCP(CP_UTF8);
661                                 g_lamexp_console_attached = true;
662                         }
663                 }
664                 
665                 if(g_lamexp_console_attached)
666                 {
667                         //-------------------------------------------------------------------
668                         //See: http://support.microsoft.com/default.aspx?scid=kb;en-us;105305
669                         //-------------------------------------------------------------------
670                         const int flags = _O_WRONLY | _O_U8TEXT;
671                         int hCrtStdOut = _open_osfhandle((intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), flags);
672                         int hCrtStdErr = _open_osfhandle((intptr_t) GetStdHandle(STD_ERROR_HANDLE),  flags);
673                         FILE *hfStdOut = (hCrtStdOut >= 0) ? _fdopen(hCrtStdOut, "wb") : NULL;
674                         FILE *hfStdErr = (hCrtStdErr >= 0) ? _fdopen(hCrtStdErr, "wb") : NULL;
675                         if(hfStdOut) { *stdout = *hfStdOut; std::cout.rdbuf(new std::filebuf(hfStdOut)); }
676                         if(hfStdErr) { *stderr = *hfStdErr; std::cerr.rdbuf(new std::filebuf(hfStdErr)); }
677                 }
678
679                 HWND hwndConsole = GetConsoleWindow();
680
681                 if((hwndConsole != NULL) && (hwndConsole != INVALID_HANDLE_VALUE))
682                 {
683                         HMENU hMenu = GetSystemMenu(hwndConsole, 0);
684                         EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | MF_GRAYED);
685                         RemoveMenu(hMenu, SC_CLOSE, MF_BYCOMMAND);
686
687                         SetWindowPos(hwndConsole, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
688                         SetWindowLong(hwndConsole, GWL_STYLE, GetWindowLong(hwndConsole, GWL_STYLE) & (~WS_MAXIMIZEBOX) & (~WS_MINIMIZEBOX));
689                         SetWindowPos(hwndConsole, HWND_TOP, 0, 0, 0, 0, SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
690                 }
691         }
692 }
693
694 /*
695  * Detect CPU features
696  */
697 lamexp_cpu_t lamexp_detect_cpu_features(const QStringList &argv)
698 {
699         typedef BOOL (WINAPI *IsWow64ProcessFun)(__in HANDLE hProcess, __out PBOOL Wow64Process);
700
701         lamexp_cpu_t features;
702         SYSTEM_INFO systemInfo;
703         int CPUInfo[4] = {-1};
704         char CPUIdentificationString[0x40];
705         char CPUBrandString[0x40];
706
707         memset(&features, 0, sizeof(lamexp_cpu_t));
708         memset(&systemInfo, 0, sizeof(SYSTEM_INFO));
709         memset(CPUIdentificationString, 0, sizeof(CPUIdentificationString));
710         memset(CPUBrandString, 0, sizeof(CPUBrandString));
711         
712         __cpuid(CPUInfo, 0);
713         memcpy(CPUIdentificationString, &CPUInfo[1], sizeof(int));
714         memcpy(CPUIdentificationString + 4, &CPUInfo[3], sizeof(int));
715         memcpy(CPUIdentificationString + 8, &CPUInfo[2], sizeof(int));
716         features.intel = (_stricmp(CPUIdentificationString, "GenuineIntel") == 0);
717         strncpy_s(features.vendor, 0x40, CPUIdentificationString, _TRUNCATE);
718
719         if(CPUInfo[0] >= 1)
720         {
721                 __cpuid(CPUInfo, 1);
722                 features.mmx = (CPUInfo[3] & 0x800000) || false;
723                 features.sse = (CPUInfo[3] & 0x2000000) || false;
724                 features.sse2 = (CPUInfo[3] & 0x4000000) || false;
725                 features.ssse3 = (CPUInfo[2] & 0x200) || false;
726                 features.sse3 = (CPUInfo[2] & 0x1) || false;
727                 features.ssse3 = (CPUInfo[2] & 0x200) || false;
728                 features.stepping = CPUInfo[0] & 0xf;
729                 features.model = ((CPUInfo[0] >> 4) & 0xf) + (((CPUInfo[0] >> 16) & 0xf) << 4);
730                 features.family = ((CPUInfo[0] >> 8) & 0xf) + ((CPUInfo[0] >> 20) & 0xff);
731         }
732
733         __cpuid(CPUInfo, 0x80000000);
734         int nExIds = qMax<int>(qMin<int>(CPUInfo[0], 0x80000004), 0x80000000);
735
736         for(int i = 0x80000002; i <= nExIds; ++i)
737         {
738                 __cpuid(CPUInfo, i);
739                 switch(i)
740                 {
741                 case 0x80000002:
742                         memcpy(CPUBrandString, CPUInfo, sizeof(CPUInfo));
743                         break;
744                 case 0x80000003:
745                         memcpy(CPUBrandString + 16, CPUInfo, sizeof(CPUInfo));
746                         break;
747                 case 0x80000004:
748                         memcpy(CPUBrandString + 32, CPUInfo, sizeof(CPUInfo));
749                         break;
750                 }
751         }
752
753         strncpy_s(features.brand, 0x40, CPUBrandString, _TRUNCATE);
754
755         if(strlen(features.brand) < 1) strncpy_s(features.brand, 0x40, "Unknown", _TRUNCATE);
756         if(strlen(features.vendor) < 1) strncpy_s(features.vendor, 0x40, "Unknown", _TRUNCATE);
757
758 #if (!(defined(_M_X64) || defined(_M_IA64)))
759         QLibrary Kernel32Lib("kernel32.dll");
760         if(IsWow64ProcessFun IsWow64ProcessPtr = (IsWow64ProcessFun) Kernel32Lib.resolve("IsWow64Process"))
761         {
762                 BOOL x64flag = FALSE;
763                 if(IsWow64ProcessPtr(GetCurrentProcess(), &x64flag))
764                 {
765                         features.x64 = (x64flag == TRUE);
766                 }
767         }
768 #else
769         features.x64 = true;
770 #endif
771
772         DWORD_PTR procAffinity, sysAffinity;
773         if(GetProcessAffinityMask(GetCurrentProcess(), &procAffinity, &sysAffinity))
774         {
775                 for(DWORD_PTR mask = 1; mask; mask <<= 1)
776                 {
777                         features.count += ((sysAffinity & mask) ? (1) : (0));
778                 }
779         }
780         if(features.count < 1)
781         {
782                 GetNativeSystemInfo(&systemInfo);
783                 features.count = qBound(1UL, systemInfo.dwNumberOfProcessors, 64UL);
784         }
785
786         if(argv.count() > 0)
787         {
788                 bool flag = false;
789                 for(int i = 0; i < argv.count(); i++)
790                 {
791                         if(!argv[i].compare("--force-cpu-no-64bit", Qt::CaseInsensitive)) { flag = true; features.x64 = false; }
792                         if(!argv[i].compare("--force-cpu-no-sse", Qt::CaseInsensitive)) { flag = true; features.sse = features.sse2 = features.sse3 = features.ssse3 = false; }
793                         if(!argv[i].compare("--force-cpu-no-intel", Qt::CaseInsensitive)) { flag = true; features.intel = false; }
794                 }
795                 if(flag) qWarning("CPU flags overwritten by user-defined parameters. Take care!\n");
796         }
797
798         return features;
799 }
800
801 /*
802  * Check for debugger (detect routine)
803  */
804 static __forceinline bool lamexp_check_for_debugger(void)
805 {
806         __try
807         {
808                 CloseHandle((HANDLE)((DWORD_PTR)-3));
809         }
810         __except(1)
811         {
812                 return true;
813         }
814
815         BOOL bHaveDebugger = FALSE;
816         if(CheckRemoteDebuggerPresent(GetCurrentProcess(), &bHaveDebugger))
817         {
818                 if(bHaveDebugger) return true;
819         }
820
821         return IsDebuggerPresent();
822 }
823
824 /*
825  * Check for debugger (thread proc)
826  */
827 static unsigned int __stdcall lamexp_debug_thread_proc(LPVOID lpParameter)
828 {
829         SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_LOWEST);
830         forever
831         {
832                 if(lamexp_check_for_debugger())
833                 {
834                         lamexp_fatal_exit(L"Not a debug build. Please unload debugger and try again!");
835                         return 666;
836                 }
837                 lamexp_sleep(100);
838         }
839 }
840
841 /*
842  * Check for debugger (startup routine)
843  */
844 static HANDLE lamexp_debug_thread_init()
845 {
846         if(lamexp_check_for_debugger())
847         {
848                 lamexp_fatal_exit(L"Not a debug build. Please unload debugger and try again!");
849         }
850         const uintptr_t h = _beginthreadex(NULL, 0, lamexp_debug_thread_proc, NULL, 0, NULL);
851         return (HANDLE)(h^0xdeadbeef);
852 }
853
854 /*
855  * Qt event filter
856  */
857 static bool lamexp_event_filter(void *message, long *result)
858
859         if((!(LAMEXP_DEBUG)) && lamexp_check_for_debugger())
860         {
861                 lamexp_fatal_exit(L"Not a debug build. Please unload debugger and try again!");
862         }
863         
864         switch(reinterpret_cast<MSG*>(message)->message)
865         {
866         case WM_QUERYENDSESSION:
867                 qWarning("WM_QUERYENDSESSION message received!");
868                 *result = lamexp_broadcast(lamexp_event_queryendsession, false) ? TRUE : FALSE;
869                 return true;
870         case WM_ENDSESSION:
871                 qWarning("WM_ENDSESSION message received!");
872                 if(reinterpret_cast<MSG*>(message)->wParam == TRUE)
873                 {
874                         lamexp_broadcast(lamexp_event_endsession, false);
875                         if(QApplication *app = reinterpret_cast<QApplication*>(QApplication::instance()))
876                         {
877                                 app->closeAllWindows();
878                                 app->quit();
879                         }
880                         lamexp_finalization();
881                         exit(1);
882                 }
883                 *result = 0;
884                 return true;
885         default:
886                 /*ignore this message and let Qt handle it*/
887                 return false;
888         }
889 }
890
891 /*
892  * Check for process elevation
893  */
894 static bool lamexp_process_is_elevated(bool *bIsUacEnabled = NULL)
895 {
896         bool bIsProcessElevated = false;
897         if(bIsUacEnabled) *bIsUacEnabled = false;
898         HANDLE hToken = NULL;
899         
900         if(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
901         {
902                 TOKEN_ELEVATION_TYPE tokenElevationType;
903                 DWORD returnLength;
904                 if(GetTokenInformation(hToken, TokenElevationType, &tokenElevationType, sizeof(TOKEN_ELEVATION_TYPE), &returnLength))
905                 {
906                         if(returnLength == sizeof(TOKEN_ELEVATION_TYPE))
907                         {
908                                 switch(tokenElevationType)
909                                 {
910                                 case TokenElevationTypeDefault:
911                                         qDebug("Process token elevation type: Default -> UAC is disabled.\n");
912                                         break;
913                                 case TokenElevationTypeFull:
914                                         qWarning("Process token elevation type: Full -> potential security risk!\n");
915                                         bIsProcessElevated = true;
916                                         if(bIsUacEnabled) *bIsUacEnabled = true;
917                                         break;
918                                 case TokenElevationTypeLimited:
919                                         qDebug("Process token elevation type: Limited -> not elevated.\n");
920                                         if(bIsUacEnabled) *bIsUacEnabled = true;
921                                         break;
922                                 default:
923                                         qWarning("Unknown tokenElevationType value: %d", tokenElevationType);
924                                         break;
925                                 }
926                         }
927                         else
928                         {
929                                 qWarning("GetTokenInformation() return an unexpected size!");
930                         }
931                 }
932                 CloseHandle(hToken);
933         }
934         else
935         {
936                 qWarning("Failed to open process token!");
937         }
938
939         return bIsProcessElevated;
940 }
941
942 /*
943  * Convert QIcon to HICON -> caller is responsible for destroying the HICON!
944  */
945 static HICON lamexp_qicon2hicon(const QIcon &icon, const int w, const int h)
946 {
947         if(!icon.isNull())
948         {
949                 QPixmap pixmap = icon.pixmap(w, h);
950                 if(!pixmap.isNull())
951                 {
952                         return pixmap.toWinHICON();
953                 }
954         }
955         return NULL;
956 }
957
958 /*
959  * Initialize Qt framework
960  */
961 bool lamexp_init_qt(int argc, char* argv[])
962 {
963         static bool qt_initialized = false;
964         typedef BOOL (WINAPI *SetDllDirectoryProc)(WCHAR *lpPathName);
965         const QStringList &arguments = lamexp_arguments();
966
967         //Don't initialized again, if done already
968         if(qt_initialized)
969         {
970                 return true;
971         }
972
973         //Secure DLL loading
974         QLibrary kernel32("kernel32.dll");
975         if(kernel32.load())
976         {
977                 SetDllDirectoryProc pSetDllDirectory = (SetDllDirectoryProc) kernel32.resolve("SetDllDirectoryW");
978                 if(pSetDllDirectory != NULL) pSetDllDirectory(L"");
979         }
980
981         //Extract executable name from argv[] array
982         QString executableName = QLatin1String("LameXP.exe");
983         if(arguments.count() > 0)
984         {
985                 static const char *delimiters = "\\/:?";
986                 executableName = arguments[0].trimmed();
987                 for(int i = 0; delimiters[i]; i++)
988                 {
989                         int temp = executableName.lastIndexOf(QChar(delimiters[i]));
990                         if(temp >= 0) executableName = executableName.mid(temp + 1);
991                 }
992                 executableName = executableName.trimmed();
993                 if(executableName.isEmpty())
994                 {
995                         executableName = QLatin1String("LameXP.exe");
996                 }
997         }
998
999         //Check Qt version
1000 #ifdef QT_BUILD_KEY
1001         qDebug("Using Qt v%s [%s], %s, %s", qVersion(), QLibraryInfo::buildDate().toString(Qt::ISODate).toLatin1().constData(), (qSharedBuild() ? "DLL" : "Static"), QLibraryInfo::buildKey().toLatin1().constData());
1002         qDebug("Compiled with Qt v%s [%s], %s\n", QT_VERSION_STR, QT_PACKAGEDATE_STR, QT_BUILD_KEY);
1003         if(_stricmp(qVersion(), QT_VERSION_STR))
1004         {
1005                 qFatal("%s", QApplication::tr("Executable '%1' requires Qt v%2, but found Qt v%3.").arg(executableName, QString::fromLatin1(QT_VERSION_STR), QString::fromLatin1(qVersion())).toLatin1().constData());
1006                 return false;
1007         }
1008         if(QLibraryInfo::buildKey().compare(QString::fromLatin1(QT_BUILD_KEY), Qt::CaseInsensitive))
1009         {
1010                 qFatal("%s", QApplication::tr("Executable '%1' was built for Qt '%2', but found Qt '%3'.").arg(executableName, QString::fromLatin1(QT_BUILD_KEY), QLibraryInfo::buildKey()).toLatin1().constData());
1011                 return false;
1012         }
1013 #else
1014         qDebug("Using Qt v%s [%s], %s", qVersion(), QLibraryInfo::buildDate().toString(Qt::ISODate).toLatin1().constData(), (qSharedBuild() ? "DLL" : "Static"));
1015         qDebug("Compiled with Qt v%s [%s]\n", QT_VERSION_STR, QT_PACKAGEDATE_STR);
1016 #endif
1017
1018         //Check the Windows version
1019         const lamexp_os_version_t &osVersionNo = lamexp_get_os_version();
1020         if(osVersionNo < lamexp_winver_winxp)
1021         {
1022                 qFatal("%s", QApplication::tr("Executable '%1' requires Windows XP or later.").arg(executableName).toLatin1().constData());
1023         }
1024
1025         //Supported Windows version?
1026         if(osVersionNo == lamexp_winver_winxp)
1027         {
1028                 qDebug("Running on Windows XP or Windows XP Media Center Edition.\n");                                          //lamexp_check_compatibility_mode("GetLargePageMinimum", executableName);
1029         }
1030         else if(osVersionNo == lamexp_winver_xpx64)
1031         {
1032                 qDebug("Running on Windows Server 2003, Windows Server 2003 R2 or Windows XP x64.\n");          //lamexp_check_compatibility_mode("GetLocaleInfoEx", executableName);
1033         }
1034         else if(osVersionNo == lamexp_winver_vista)
1035         {
1036                 qDebug("Running on Windows Vista or Windows Server 2008.\n");                                                           //lamexp_check_compatibility_mode("CreateRemoteThreadEx", executableName*/);
1037         }
1038         else if(osVersionNo == lamexp_winver_win70)
1039         {
1040                 qDebug("Running on Windows 7 or Windows Server 2008 R2.\n");                                                            //lamexp_check_compatibility_mode("CreateFile2", executableName);
1041         }
1042         else if(osVersionNo == lamexp_winver_win80)
1043         {
1044                 qDebug("Running on Windows 8 or Windows Server 2012.\n");                                                                       //lamexp_check_compatibility_mode("FindPackagesByPackageFamily", executableName);
1045         }
1046         else if(osVersionNo == lamexp_winver_win81)
1047         {
1048                 qDebug("Running on Windows 8.1 or Windows Server 2012 R2.\n");                                                          //lamexp_check_compatibility_mode(NULL, executableName);
1049         }
1050         else
1051         {
1052                 const QString message = QString().sprintf("Running on an unknown WindowsNT-based system (v%u.%u).", osVersionNo.versionMajor, osVersionNo.versionMinor);
1053                 qWarning("%s\n", QUTF8(message));
1054                 MessageBoxW(NULL, QWCHAR(message), L"LameXP", MB_OK | MB_TOPMOST | MB_ICONWARNING);
1055         }
1056
1057         //Check for compat mode
1058         if(osVersionNo.overrideFlag && (osVersionNo <= lamexp_winver_win81))
1059         {
1060                 qWarning("Windows compatibility mode detected!");
1061                 if(!arguments.contains("--ignore-compat-mode", Qt::CaseInsensitive))
1062                 {
1063                         qFatal("%s", QApplication::tr("Executable '%1' doesn't support Windows compatibility mode.").arg(executableName).toLatin1().constData());
1064                         return false;
1065                 }
1066         }
1067
1068         //Check for Wine
1069         if(lamexp_detect_wine())
1070         {
1071                 qWarning("It appears we are running under Wine, unexpected things might happen!\n");
1072         }
1073
1074         //Set text Codec for locale
1075         QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF-8"));
1076
1077         //Create Qt application instance
1078         QApplication *application = new QApplication(argc, argv);
1079
1080         //Load plugins from application directory
1081         QCoreApplication::setLibraryPaths(QStringList() << QApplication::applicationDirPath());
1082         qDebug("Library Path:\n%s\n", QUTF8(QApplication::libraryPaths().first()));
1083
1084         //Set application properties
1085         application->setApplicationName("LameXP - Audio Encoder Front-End");
1086         application->setApplicationVersion(QString().sprintf("%d.%02d.%04d", lamexp_version_major(), lamexp_version_minor(), lamexp_version_build())); 
1087         application->setOrganizationName("LoRd_MuldeR");
1088         application->setOrganizationDomain("mulder.at.gg");
1089         application->setWindowIcon(lamexp_app_icon());
1090         application->setEventFilter(lamexp_event_filter);
1091
1092         //Check for supported image formats
1093         QList<QByteArray> supportedFormats = QImageReader::supportedImageFormats();
1094         for(int i = 0; g_lamexp_imageformats[i]; i++)
1095         {
1096                 if(!supportedFormats.contains(g_lamexp_imageformats[i]))
1097                 {
1098                         qFatal("Qt initialization error: QImageIOHandler for '%s' missing!", g_lamexp_imageformats[i]);
1099                         return false;
1100                 }
1101         }
1102         
1103         //Enable larger/smaller font size
1104         double fontScaleFactor = 1.0;
1105         if(arguments.contains("--huge-font",  Qt::CaseInsensitive)) fontScaleFactor = 1.500;
1106         if(arguments.contains("--big-font",   Qt::CaseInsensitive)) fontScaleFactor = 1.250;
1107         if(arguments.contains("--small-font", Qt::CaseInsensitive)) fontScaleFactor = 0.875;
1108         if(arguments.contains("--tiny-font",  Qt::CaseInsensitive)) fontScaleFactor = 0.750;
1109         if(!qFuzzyCompare(fontScaleFactor, 1.0))
1110         {
1111                 qWarning("Application font scale factor set to: %.3f\n", fontScaleFactor);
1112                 QFont appFont = application->font();
1113                 appFont.setPointSizeF(appFont.pointSizeF() * fontScaleFactor);
1114                 application->setFont(appFont);
1115         }
1116
1117         //Add the default translations
1118         lamexp_translation_init();
1119
1120         //Check for process elevation
1121         if(lamexp_process_is_elevated() && (!lamexp_detect_wine()))
1122         {
1123                 QMessageBox messageBox(QMessageBox::Warning, "LameXP", "<nobr>LameXP was started with 'elevated' rights, altough LameXP does not need these rights.<br>Running an applications with unnecessary rights is a potential security risk!</nobr>", QMessageBox::NoButton, NULL, Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::WindowStaysOnTopHint);
1124                 messageBox.addButton("Quit Program (Recommended)", QMessageBox::NoRole);
1125                 messageBox.addButton("Ignore", QMessageBox::NoRole);
1126                 if(messageBox.exec() == 0)
1127                 {
1128                         return false;
1129                 }
1130         }
1131
1132         //Update console icon, if a console is attached
1133 #if QT_VERSION < QT_VERSION_CHECK(5,0,0)
1134         if(g_lamexp_console_attached && (!lamexp_detect_wine()))
1135         {
1136                 QLibrary kernel32("kernel32.dll");
1137                 if(kernel32.load())
1138                 {
1139                         typedef DWORD (__stdcall *SetConsoleIconFun)(HICON);
1140                         if(SetConsoleIconFun SetConsoleIconPtr = (SetConsoleIconFun) kernel32.resolve("SetConsoleIcon"))
1141                         {
1142                                 if(HICON hIcon = lamexp_qicon2hicon(QIcon(":/icons/sound.png"), 16, 16))
1143                                 {
1144                                         SetConsoleIconPtr(hIcon);
1145                                         DestroyIcon(hIcon);
1146                                 }
1147                         }
1148                 }
1149         }
1150 #endif
1151
1152         //Done
1153         qt_initialized = true;
1154         return true;
1155 }
1156
1157 const QStringList &lamexp_arguments(void)
1158 {
1159         QReadLocker readLock(&g_lamexp_argv.lock);
1160
1161         if(!g_lamexp_argv.list)
1162         {
1163                 readLock.unlock();
1164                 QWriteLocker writeLock(&g_lamexp_argv.lock);
1165
1166                 g_lamexp_argv.list = new QStringList;
1167
1168                 int nArgs = 0;
1169                 LPWSTR *szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
1170
1171                 if(NULL != szArglist)
1172                 {
1173                         for(int i = 0; i < nArgs; i++)
1174                         {
1175                                 (*g_lamexp_argv.list) << WCHAR2QSTR(szArglist[i]);
1176                         }
1177                         LocalFree(szArglist);
1178                 }
1179                 else
1180                 {
1181                         qWarning("CommandLineToArgvW() has failed !!!");
1182                 }
1183         }
1184
1185         return (*g_lamexp_argv.list);
1186 }
1187
1188 /*
1189  * Locate known folder on local system
1190  */
1191 const QString &lamexp_known_folder(lamexp_known_folder_t folder_id)
1192 {
1193         static const int CSIDL_FLAG_CREATE = 0x8000;
1194         typedef enum { KF_FLAG_CREATE = 0x00008000 } kf_flags_t;
1195         
1196         struct
1197         {
1198                 const int csidl;
1199                 const GUID guid;
1200         }
1201         static s_folders[] =
1202         {
1203                 { 0x001c, {0xF1B32785,0x6FBA,0x4FCF,{0x9D,0x55,0x7B,0x8E,0x7F,0x15,0x70,0x91}} },  //CSIDL_LOCAL_APPDATA
1204                 { 0x0026, {0x905e63b6,0xc1bf,0x494e,{0xb2,0x9c,0x65,0xb7,0x32,0xd3,0xd2,0x1a}} },  //CSIDL_PROGRAM_FILES
1205                 { 0x0024, {0xF38BF404,0x1D43,0x42F2,{0x93,0x05,0x67,0xDE,0x0B,0x28,0xFC,0x23}} },  //CSIDL_WINDOWS_FOLDER
1206                 { 0x0025, {0x1AC14E77,0x02E7,0x4E5D,{0xB7,0x44,0x2E,0xB1,0xAE,0x51,0x98,0xB7}} },  //CSIDL_SYSTEM_FOLDER
1207         };
1208
1209         size_t folderId = size_t(-1);
1210
1211         switch(folder_id)
1212         {
1213                 case lamexp_folder_localappdata: folderId = 0; break;
1214                 case lamexp_folder_programfiles: folderId = 1; break;
1215                 case lamexp_folder_systroot_dir: folderId = 2; break;
1216                 case lamexp_folder_systemfolder: folderId = 3; break;
1217         }
1218
1219         if(folderId == size_t(-1))
1220         {
1221                 qWarning("Invalid 'known' folder was requested!");
1222                 return *reinterpret_cast<QString*>(NULL);
1223         }
1224
1225         QReadLocker readLock(&g_lamexp_known_folder.lock);
1226
1227         //Already in cache?
1228         if(g_lamexp_known_folder.knownFolders)
1229         {
1230                 if(g_lamexp_known_folder.knownFolders->contains(folderId))
1231                 {
1232                         return (*g_lamexp_known_folder.knownFolders)[folderId];
1233                 }
1234         }
1235
1236         //Obtain write lock to initialize
1237         readLock.unlock();
1238         QWriteLocker writeLock(&g_lamexp_known_folder.lock);
1239
1240         //Still not in cache?
1241         if(g_lamexp_known_folder.knownFolders)
1242         {
1243                 if(g_lamexp_known_folder.knownFolders->contains(folderId))
1244                 {
1245                         return (*g_lamexp_known_folder.knownFolders)[folderId];
1246                 }
1247         }
1248
1249         //Initialize on first call
1250         if(!g_lamexp_known_folder.knownFolders)
1251         {
1252                 QLibrary shell32("shell32.dll");
1253                 if(shell32.load())
1254                 {
1255                         g_lamexp_known_folder.getFolderPath =      (SHGetFolderPath_t)      shell32.resolve("SHGetFolderPathW");
1256                         g_lamexp_known_folder.getKnownFolderPath = (SHGetKnownFolderPath_t) shell32.resolve("SHGetKnownFolderPath");
1257                 }
1258                 g_lamexp_known_folder.knownFolders = new QMap<size_t, QString>();
1259         }
1260
1261         QString folderPath;
1262
1263         //Now try to get the folder path!
1264         if(g_lamexp_known_folder.getKnownFolderPath)
1265         {
1266                 WCHAR *path = NULL;
1267                 if(g_lamexp_known_folder.getKnownFolderPath(s_folders[folderId].guid, KF_FLAG_CREATE, NULL, &path) == S_OK)
1268                 {
1269                         //MessageBoxW(0, path, L"SHGetKnownFolderPath", MB_TOPMOST);
1270                         QDir folderTemp = QDir(QDir::fromNativeSeparators(QString::fromUtf16(reinterpret_cast<const unsigned short*>(path))));
1271                         if(folderTemp.exists())
1272                         {
1273                                 folderPath = folderTemp.canonicalPath();
1274                         }
1275                         CoTaskMemFree(path);
1276                 }
1277         }
1278         else if(g_lamexp_known_folder.getFolderPath)
1279         {
1280                 WCHAR *path = new WCHAR[4096];
1281                 if(g_lamexp_known_folder.getFolderPath(NULL, s_folders[folderId].csidl | CSIDL_FLAG_CREATE, NULL, NULL, path) == S_OK)
1282                 {
1283                         //MessageBoxW(0, path, L"SHGetFolderPathW", MB_TOPMOST);
1284                         QDir folderTemp = QDir(QDir::fromNativeSeparators(QString::fromUtf16(reinterpret_cast<const unsigned short*>(path))));
1285                         if(folderTemp.exists())
1286                         {
1287                                 folderPath = folderTemp.canonicalPath();
1288                         }
1289                 }
1290                 LAMEXP_DELETE_ARRAY(path);
1291         }
1292
1293         //Update cache
1294         g_lamexp_known_folder.knownFolders->insert(folderId, folderPath);
1295         return (*g_lamexp_known_folder.knownFolders)[folderId];
1296 }
1297
1298 /*
1299  * Safely remove a file
1300  */
1301 bool lamexp_remove_file(const QString &filename)
1302 {
1303         if(!QFileInfo(filename).exists() || !QFileInfo(filename).isFile())
1304         {
1305                 return true;
1306         }
1307         else
1308         {
1309                 if(!QFile::remove(filename))
1310                 {
1311                         static const DWORD attrMask = FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_HIDDEN | FILE_ATTRIBUTE_SYSTEM;
1312                         const DWORD attributes = GetFileAttributesW(QWCHAR(filename));
1313                         if(attributes & attrMask)
1314                         {
1315                                 SetFileAttributesW(QWCHAR(filename), FILE_ATTRIBUTE_NORMAL);
1316                         }
1317                         if(!QFile::remove(filename))
1318                         {
1319                                 qWarning("Could not delete \"%s\"", filename.toLatin1().constData());
1320                                 return false;
1321                         }
1322                         else
1323                         {
1324                                 return true;
1325                         }
1326                 }
1327                 else
1328                 {
1329                         return true;
1330                 }
1331         }
1332 }
1333
1334 /*
1335  * Check if visual themes are enabled (WinXP and later)
1336  */
1337 bool lamexp_themes_enabled(void)
1338 {
1339         typedef int (WINAPI *IsAppThemedFun)(void);
1340         
1341         QReadLocker readLock(&g_lamexp_themes_enabled.lock);
1342         if(g_lamexp_themes_enabled.bInitialized)
1343         {
1344                 return g_lamexp_themes_enabled.bThemesEnabled;
1345         }
1346
1347         readLock.unlock();
1348         QWriteLocker writeLock(&g_lamexp_themes_enabled.lock);
1349
1350         if(!g_lamexp_themes_enabled.bInitialized)
1351         {
1352                 g_lamexp_themes_enabled.bThemesEnabled = false;
1353                 const lamexp_os_version_t &osVersion = lamexp_get_os_version();
1354                 if(osVersion >= lamexp_winver_winxp)
1355                 {
1356                         IsAppThemedFun IsAppThemedPtr = NULL;
1357                         QLibrary uxTheme(QString("%1/UxTheme.dll").arg(lamexp_known_folder(lamexp_folder_systemfolder)));
1358                         if(uxTheme.load())
1359                         {
1360                                 IsAppThemedPtr = (IsAppThemedFun) uxTheme.resolve("IsAppThemed");
1361                         }
1362                         if(IsAppThemedPtr)
1363                         {
1364                                 g_lamexp_themes_enabled.bThemesEnabled = IsAppThemedPtr();
1365                                 if(!g_lamexp_themes_enabled.bThemesEnabled)
1366                                 {
1367                                         qWarning("Theme support is disabled for this process!");
1368                                 }
1369                         }
1370                 }
1371                 g_lamexp_themes_enabled.bInitialized = true;
1372         }
1373
1374         return g_lamexp_themes_enabled.bThemesEnabled;
1375 }
1376
1377 /*
1378  * Get number of free bytes on disk
1379  */
1380 unsigned __int64 lamexp_free_diskspace(const QString &path, bool *ok)
1381 {
1382         ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes;
1383         if(GetDiskFreeSpaceExW(reinterpret_cast<const wchar_t*>(QDir::toNativeSeparators(path).utf16()), &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes))
1384         {
1385                 if(ok) *ok = true;
1386                 return freeBytesAvailable.QuadPart;
1387         }
1388         else
1389         {
1390                 if(ok) *ok = false;
1391                 return 0;
1392         }
1393 }
1394
1395 /*
1396  * Check if computer does support hibernation
1397  */
1398 bool lamexp_is_hibernation_supported(void)
1399 {
1400         bool hibernationSupported = false;
1401
1402         SYSTEM_POWER_CAPABILITIES pwrCaps;
1403         SecureZeroMemory(&pwrCaps, sizeof(SYSTEM_POWER_CAPABILITIES));
1404         
1405         if(GetPwrCapabilities(&pwrCaps))
1406         {
1407                 hibernationSupported = pwrCaps.SystemS4 && pwrCaps.HiberFilePresent;
1408         }
1409
1410         return hibernationSupported;
1411 }
1412
1413 /*
1414  * Shutdown the computer
1415  */
1416 bool lamexp_shutdown_computer(const QString &message, const unsigned long timeout, const bool forceShutdown, const bool hibernate)
1417 {
1418         HANDLE hToken = NULL;
1419
1420         if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
1421         {
1422                 TOKEN_PRIVILEGES privileges;
1423                 memset(&privileges, 0, sizeof(TOKEN_PRIVILEGES));
1424                 privileges.PrivilegeCount = 1;
1425                 privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1426                 
1427                 if(LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &privileges.Privileges[0].Luid))
1428                 {
1429                         if(AdjustTokenPrivileges(hToken, FALSE, &privileges, NULL, NULL, NULL))
1430                         {
1431                                 if(hibernate)
1432                                 {
1433                                         if(SetSuspendState(TRUE, TRUE, TRUE))
1434                                         {
1435                                                 return true;
1436                                         }
1437                                 }
1438                                 const DWORD reason = SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_FLAG_PLANNED;
1439                                 return InitiateSystemShutdownEx(NULL, const_cast<wchar_t*>(QWCHAR(message)), timeout, forceShutdown ? TRUE : FALSE, FALSE, reason);
1440                         }
1441                 }
1442         }
1443         
1444         return false;
1445 }
1446
1447 /*
1448  * Determines the current date, resistant against certain manipulations
1449  */
1450 QDate lamexp_current_date_safe(void)
1451 {
1452         const DWORD MAX_PROC = 1024;
1453         DWORD *processes = new DWORD[MAX_PROC];
1454         DWORD bytesReturned = 0;
1455         
1456         if(!EnumProcesses(processes, sizeof(DWORD) * MAX_PROC, &bytesReturned))
1457         {
1458                 LAMEXP_DELETE_ARRAY(processes);
1459                 return QDate::currentDate();
1460         }
1461
1462         const DWORD procCount = bytesReturned / sizeof(DWORD);
1463         ULARGE_INTEGER lastStartTime;
1464         memset(&lastStartTime, 0, sizeof(ULARGE_INTEGER));
1465
1466         for(DWORD i = 0; i < procCount; i++)
1467         {
1468                 HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processes[i]);
1469                 if(hProc)
1470                 {
1471                         FILETIME processTime[4];
1472                         if(GetProcessTimes(hProc, &processTime[0], &processTime[1], &processTime[2], &processTime[3]))
1473                         {
1474                                 ULARGE_INTEGER timeCreation;
1475                                 timeCreation.LowPart = processTime[0].dwLowDateTime;
1476                                 timeCreation.HighPart = processTime[0].dwHighDateTime;
1477                                 if(timeCreation.QuadPart > lastStartTime.QuadPart)
1478                                 {
1479                                         lastStartTime.QuadPart = timeCreation.QuadPart;
1480                                 }
1481                         }
1482                         CloseHandle(hProc);
1483                 }
1484         }
1485
1486         LAMEXP_DELETE_ARRAY(processes);
1487         
1488         FILETIME lastStartTime_fileTime;
1489         lastStartTime_fileTime.dwHighDateTime = lastStartTime.HighPart;
1490         lastStartTime_fileTime.dwLowDateTime = lastStartTime.LowPart;
1491
1492         FILETIME lastStartTime_localTime;
1493         if(!FileTimeToLocalFileTime(&lastStartTime_fileTime, &lastStartTime_localTime))
1494         {
1495                 memcpy(&lastStartTime_localTime, &lastStartTime_fileTime, sizeof(FILETIME));
1496         }
1497         
1498         SYSTEMTIME lastStartTime_system;
1499         if(!FileTimeToSystemTime(&lastStartTime_localTime, &lastStartTime_system))
1500         {
1501                 memset(&lastStartTime_system, 0, sizeof(SYSTEMTIME));
1502                 lastStartTime_system.wYear = 1970; lastStartTime_system.wMonth = lastStartTime_system.wDay = 1;
1503         }
1504
1505         const QDate currentDate = QDate::currentDate();
1506         const QDate processDate = QDate(lastStartTime_system.wYear, lastStartTime_system.wMonth, lastStartTime_system.wDay);
1507         return (currentDate >= processDate) ? currentDate : processDate;
1508 }
1509
1510 /*
1511  * Show system message box
1512  */
1513 int lamexp_system_message(const wchar_t *text, int beepType)
1514 {
1515         UINT flags = MB_OK | MB_TOPMOST;
1516
1517         switch(beepType)
1518         {
1519                 case lamexp_beep_info:    flags = flags | MB_ICONASTERISK;
1520                 case lamexp_beep_warning: flags = flags | MB_ICONEXCLAMATION;
1521                 case lamexp_beep_error:   flags = flags | MB_ICONHAND;
1522         }
1523
1524         return MessageBoxW(NULL, text, L"LameXP", flags);
1525 }
1526
1527 /*
1528  * Block window "move" message
1529  */
1530 bool lamexp_block_window_move(void *message)
1531 {
1532         if(message)
1533         {
1534                 MSG *msg = reinterpret_cast<MSG*>(message);
1535                 if((msg->message == WM_SYSCOMMAND) && (msg->wParam == SC_MOVE))
1536                 {
1537                         return true;
1538                 }
1539                 if((msg->message == WM_NCLBUTTONDOWN) && (msg->wParam == HTCAPTION))
1540                 {
1541                         return true;
1542                 }
1543         }
1544         return false;
1545 }
1546
1547 /*
1548  * Suspend calling thread for N milliseconds
1549  */
1550 inline void lamexp_sleep(const unsigned int delay)
1551 {
1552         Sleep(delay);
1553 }
1554
1555 bool lamexp_beep(int beepType)
1556 {
1557         switch(beepType)
1558         {
1559                 case lamexp_beep_info:    return MessageBeep(MB_ICONASTERISK) == TRUE;    break;
1560                 case lamexp_beep_warning: return MessageBeep(MB_ICONEXCLAMATION) == TRUE; break;
1561                 case lamexp_beep_error:   return MessageBeep(MB_ICONHAND) == TRUE;        break;
1562                 default: return false;
1563         }
1564 }
1565
1566 /*
1567  * Play a sound (from resources)
1568  */
1569 bool lamexp_play_sound(const QString &name, const bool bAsync)
1570 {
1571         const unsigned char *data = NULL;
1572         
1573         //Try to look-up the sound in the cache first
1574         if(!name.isEmpty())
1575         {
1576                 QReadLocker readLock(&g_lamexp_sounds.lock);
1577                 if(g_lamexp_sounds.sound_db && g_lamexp_sounds.sound_db->contains(name))
1578                 {
1579                         data = g_lamexp_sounds.sound_db->value(name);
1580                 }
1581         }
1582         
1583         //If data not found in cache, try to load from resource!
1584         if((!data) && (!name.isEmpty()))
1585         {
1586                 QResource resource(QString(":/sounds/%1.wav").arg(name));
1587                 if(resource.isValid() && (data = resource.data()))
1588                 {
1589                         QWriteLocker writeLock(&g_lamexp_sounds.lock);
1590                         if(!g_lamexp_sounds.sound_db)
1591                         {
1592                                 g_lamexp_sounds.sound_db = new QHash<const QString, const unsigned char*>();
1593                         }
1594                         g_lamexp_sounds.sound_db->insert(name, data);
1595                 }
1596                 else
1597                 {
1598                         qWarning("Sound effect \"%s\" not found!", QUTF8(name));
1599                 }
1600         }
1601
1602         //Play the sound, if availbale
1603         if(data)
1604         {
1605                 return PlaySound(LPCWSTR(data), NULL, (SND_MEMORY | (bAsync ? SND_ASYNC : SND_SYNC))) != FALSE;
1606         }
1607
1608         return false;
1609 }
1610
1611 /*
1612  * Play a sound (system alias)
1613  */
1614 bool lamexp_play_sound_alias(const QString &alias, const bool bAsync)
1615 {
1616         return PlaySound(QWCHAR(alias), GetModuleHandle(NULL), (SND_ALIAS | (bAsync ? SND_ASYNC : SND_SYNC))) != FALSE;
1617 }
1618
1619 /*
1620  * Play a sound (from external DLL)
1621  */
1622 bool lamexp_play_sound_file(const QString &library, const unsigned short uiSoundIdx, const bool bAsync)
1623 {
1624         bool result = false;
1625
1626         QFileInfo libraryFile(library);
1627         if(!libraryFile.isAbsolute())
1628         {
1629                 const QString &systemDir = lamexp_known_folder(lamexp_folder_systemfolder);
1630                 if(!systemDir.isEmpty())
1631                 {
1632                         libraryFile.setFile(QDir(systemDir), libraryFile.fileName());
1633                 }
1634         }
1635
1636         if(libraryFile.exists() && libraryFile.isFile())
1637         {
1638                 if(HMODULE module = LoadLibraryW(QWCHAR(QDir::toNativeSeparators(libraryFile.canonicalFilePath()))))
1639                 {
1640                         result = (PlaySound(MAKEINTRESOURCE(uiSoundIdx), module, (SND_RESOURCE | (bAsync ? SND_ASYNC : SND_SYNC))) != FALSE);
1641                         FreeLibrary(module);
1642                 }
1643         }
1644         else
1645         {
1646                 qWarning("PlaySound: File \"%s\" could not be found!", QUTF8(libraryFile.absoluteFilePath()));
1647         }
1648
1649         return result;
1650 }
1651
1652 /*
1653  * Open file using the shell
1654  */
1655 bool lamexp_exec_shell(const QWidget *win, const QString &url, const bool explore)
1656 {
1657         return lamexp_exec_shell(win, url, QString(), QString(), explore);
1658 }
1659
1660 /*
1661  * Open file using the shell (with parameters)
1662  */
1663 bool lamexp_exec_shell(const QWidget *win, const QString &url, const QString &parameters, const QString &directory, const bool explore)
1664 {
1665         return ((int) ShellExecuteW(((win) ? win->winId() : NULL), (explore ? L"explore" : L"open"), QWCHAR(url), ((!parameters.isEmpty()) ? QWCHAR(parameters) : NULL), ((!directory.isEmpty()) ? QWCHAR(directory) : NULL), SW_SHOW)) > 32;
1666 }
1667
1668         /*
1669  * Query value of the performance counter
1670  */
1671 __int64 lamexp_perfcounter_value(void)
1672 {
1673         LARGE_INTEGER counter;
1674         if(QueryPerformanceCounter(&counter) == TRUE)
1675         {
1676                 return counter.QuadPart;
1677         }
1678         return -1;
1679 }
1680
1681 /*
1682  * Query frequency of the performance counter
1683  */
1684 __int64 lamexp_perfcounter_frequ(void)
1685 {
1686         LARGE_INTEGER frequency;
1687         if(QueryPerformanceFrequency(&frequency) == TRUE)
1688         {
1689                 return frequency.QuadPart;
1690         }
1691         return -1;
1692 }
1693
1694 /*
1695  * Insert entry to the window's system menu
1696  */
1697 bool lamexp_append_sysmenu(const QWidget *win, const unsigned int identifier, const QString &text)
1698 {
1699         bool ok = false;
1700         
1701         if(HMENU hMenu = GetSystemMenu(win->winId(), FALSE))
1702         {
1703                 ok = (AppendMenuW(hMenu, MF_SEPARATOR, 0, 0) == TRUE);
1704                 ok = (AppendMenuW(hMenu, MF_STRING, identifier, QWCHAR(text)) == TRUE);
1705         }
1706
1707         return ok;
1708 }
1709
1710 /*
1711  * Insert entry to the window's system menu
1712  */
1713 bool lamexp_check_sysmenu_msg(void *message, const unsigned int identifier)
1714 {
1715         return (((MSG*)message)->message == WM_SYSCOMMAND) && ((((MSG*)message)->wParam & 0xFFF0) == identifier);
1716 }
1717
1718 /*
1719  * Update system menu entry
1720  */
1721 bool lamexp_update_sysmenu(const QWidget *win, const unsigned int identifier, const QString &text)
1722 {
1723         bool ok = false;
1724         
1725         if(HMENU hMenu = ::GetSystemMenu(win->winId(), FALSE))
1726         {
1727                 ok = (ModifyMenu(hMenu, identifier, MF_STRING | MF_BYCOMMAND, identifier, QWCHAR(text)) == TRUE);
1728         }
1729         return ok;
1730 }
1731
1732 /*
1733  * Display the window's close button
1734  */
1735 bool lamexp_enable_close_button(const QWidget *win, const bool bEnable)
1736 {
1737         bool ok = false;
1738
1739         if(HMENU hMenu = GetSystemMenu(win->winId(), FALSE))
1740         {
1741                 ok = (EnableMenuItem(hMenu, SC_CLOSE, MF_BYCOMMAND | (bEnable ? MF_ENABLED : MF_GRAYED)) == TRUE);
1742         }
1743
1744         return ok;
1745 }
1746
1747 /*
1748  * Check whether ESC key has been pressed since the previous call to this function
1749  */
1750 bool lamexp_check_escape_state(void)
1751 {
1752         return (GetAsyncKeyState(VK_ESCAPE) & 0x0001) != 0;
1753 }
1754
1755 /*
1756  * Set the process priority class for current process
1757  */
1758 bool lamexp_change_process_priority(const int priority)
1759 {
1760         return lamexp_change_process_priority(GetCurrentProcess(), priority);
1761 }
1762
1763 /*
1764  * Set the process priority class for specified process
1765  */
1766 bool lamexp_change_process_priority(const QProcess *proc, const int priority)
1767 {
1768         if(Q_PID qPid = proc->pid())
1769         {
1770                 return lamexp_change_process_priority(qPid->hProcess, priority);
1771         }
1772         else
1773         {
1774                 return false;
1775         }
1776 }
1777
1778 /*
1779  * Set the process priority class for specified process
1780  */
1781 bool lamexp_change_process_priority(void *hProcess, const int priority)
1782 {
1783         bool ok = false;
1784
1785         switch(qBound(-2, priority, 2))
1786         {
1787         case 2:
1788                 ok = (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS) == TRUE);
1789                 break;
1790         case 1:
1791                 if(!(ok = (SetPriorityClass(hProcess, ABOVE_NORMAL_PRIORITY_CLASS) == TRUE)))
1792                 {
1793                         ok = (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS) == TRUE);
1794                 }
1795                 break;
1796         case 0:
1797                 ok = (SetPriorityClass(hProcess, NORMAL_PRIORITY_CLASS) == TRUE);
1798                 break;
1799         case -1:
1800                 if(!(ok = (SetPriorityClass(hProcess, BELOW_NORMAL_PRIORITY_CLASS) == TRUE)))
1801                 {
1802                         ok = (SetPriorityClass(hProcess, IDLE_PRIORITY_CLASS) == TRUE);
1803                 }
1804                 break;
1805         case -2:
1806                 ok = (SetPriorityClass(hProcess, IDLE_PRIORITY_CLASS) == TRUE);
1807                 break;
1808         }
1809
1810         return ok;
1811 }
1812
1813 /*
1814  * Returns the current file time
1815  */
1816 unsigned __int64 lamexp_current_file_time(void)
1817 {
1818         FILETIME fileTime;
1819         GetSystemTimeAsFileTime(&fileTime);
1820
1821         ULARGE_INTEGER temp;
1822         temp.HighPart = fileTime.dwHighDateTime;
1823         temp.LowPart = fileTime.dwLowDateTime;
1824
1825         return temp.QuadPart;
1826 }
1827
1828 /*
1829  * Bring the specifed window to the front
1830  */
1831 bool lamexp_bring_to_front(const QWidget *window)
1832 {
1833         bool ret = false;
1834         
1835         if(window)
1836         {
1837                 for(int i = 0; (i < 5) && (!ret); i++)
1838                 {
1839                         ret = (SetForegroundWindow(window->winId()) != FALSE);
1840                         SwitchToThisWindow(window->winId(), TRUE);
1841                 }
1842                 LockSetForegroundWindow(LSFW_LOCK);
1843         }
1844
1845         return ret;
1846 }
1847
1848 /*
1849  * Bring window of the specifed process to the front (callback)
1850  */
1851 static BOOL CALLBACK lamexp_bring_process_to_front_helper(HWND hwnd, LPARAM lParam)
1852 {
1853         DWORD processId = *reinterpret_cast<WORD*>(lParam);
1854         DWORD windowProcessId = NULL;
1855         GetWindowThreadProcessId(hwnd, &windowProcessId);
1856         if(windowProcessId == processId)
1857         {
1858                 SwitchToThisWindow(hwnd, TRUE);
1859                 SetForegroundWindow(hwnd);
1860                 return FALSE;
1861         }
1862
1863         return TRUE;
1864 }
1865
1866 /*
1867  * Bring window of the specifed process to the front
1868  */
1869 bool lamexp_bring_process_to_front(const unsigned long pid)
1870 {
1871         return EnumWindows(lamexp_bring_process_to_front_helper, reinterpret_cast<LPARAM>(&pid)) == TRUE;
1872 }
1873
1874 /*
1875  * Check the network connectivity status
1876  */
1877 int lamexp_network_status(void)
1878 {
1879         DWORD dwFlags;
1880         const BOOL ret = IsNetworkAlive(&dwFlags);
1881         if(GetLastError() == 0)
1882         {
1883                 return (ret != FALSE) ? lamexp_network_yes : lamexp_network_non;
1884         }
1885         return lamexp_network_err;
1886 }
1887
1888 /*
1889  * Retrun the process ID of the given QProcess
1890  */
1891 unsigned long lamexp_process_id(const QProcess *proc)
1892 {
1893         PROCESS_INFORMATION *procInf = proc->pid();
1894         return (procInf) ? procInf->dwProcessId : NULL;
1895 }
1896
1897 /*
1898  * Convert long path to short path
1899  */
1900 QString lamexp_path_to_short(const QString &longPath)
1901 {
1902         QString shortPath;
1903         DWORD buffSize = GetShortPathNameW(reinterpret_cast<const wchar_t*>(longPath.utf16()), NULL, NULL);
1904         
1905         if(buffSize > 0)
1906         {
1907                 wchar_t *buffer = new wchar_t[buffSize];
1908                 DWORD result = GetShortPathNameW(reinterpret_cast<const wchar_t*>(longPath.utf16()), buffer, buffSize);
1909
1910                 if(result > 0 && result < buffSize)
1911                 {
1912                         shortPath = QString::fromUtf16(reinterpret_cast<const unsigned short*>(buffer));
1913                 }
1914
1915                 delete[] buffer;
1916         }
1917
1918         return (shortPath.isEmpty() ? longPath : shortPath);
1919 }
1920
1921 /*
1922  * Open media file in external player
1923  */
1924 bool lamexp_open_media_file(const QString &mediaFilePath)
1925 {
1926         const static wchar_t *registryPrefix[2] = { L"SOFTWARE\\", L"SOFTWARE\\Wow6432Node\\" };
1927         const static wchar_t *registryKeys[3] = 
1928         {
1929                 L"Microsoft\\Windows\\CurrentVersion\\Uninstall\\{97D341C8-B0D1-4E4A-A49A-C30B52F168E9}",
1930                 L"Microsoft\\Windows\\CurrentVersion\\Uninstall\\{DB9E4EAB-2717-499F-8D56-4CC8A644AB60}",
1931                 L"foobar2000"
1932         };
1933         const static wchar_t *appNames[4] = { L"smplayer_portable.exe", L"smplayer.exe", L"MPUI.exe", L"foobar2000.exe" };
1934         const static wchar_t *valueNames[2] = { L"InstallLocation", L"InstallDir" };
1935
1936         for(size_t i = 0; i < 3; i++)
1937         {
1938                 for(size_t j = 0; j < 2; j++)
1939                 {
1940                         QString mplayerPath;
1941                         HKEY registryKeyHandle = NULL;
1942
1943                         const QString currentKey = WCHAR2QSTR(registryPrefix[j]).append(WCHAR2QSTR(registryKeys[i]));
1944                         if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, QWCHAR(currentKey), 0, KEY_READ, &registryKeyHandle) == ERROR_SUCCESS)
1945                         {
1946                                 for(size_t k = 0; k < 2; k++)
1947                                 {
1948                                         wchar_t Buffer[4096];
1949                                         DWORD BuffSize = sizeof(wchar_t*) * 4096;
1950                                         DWORD DataType = REG_NONE;
1951                                         if(RegQueryValueExW(registryKeyHandle, valueNames[k], 0, &DataType, reinterpret_cast<BYTE*>(Buffer), &BuffSize) == ERROR_SUCCESS)
1952                                         {
1953                                                 if((DataType == REG_SZ) || (DataType == REG_EXPAND_SZ) || (DataType == REG_LINK))
1954                                                 {
1955                                                         mplayerPath = WCHAR2QSTR(Buffer);
1956                                                         break;
1957                                                 }
1958                                         }
1959                                 }
1960                                 RegCloseKey(registryKeyHandle);
1961                         }
1962
1963                         if(!mplayerPath.isEmpty())
1964                         {
1965                                 QDir mplayerDir(mplayerPath);
1966                                 if(mplayerDir.exists())
1967                                 {
1968                                         for(size_t k = 0; k < 4; k++)
1969                                         {
1970                                                 if(mplayerDir.exists(WCHAR2QSTR(appNames[k])))
1971                                                 {
1972                                                         qDebug("Player found at:\n%s\n", QUTF8(mplayerDir.absoluteFilePath(WCHAR2QSTR(appNames[k]))));
1973                                                         QProcess::startDetached(mplayerDir.absoluteFilePath(WCHAR2QSTR(appNames[k])), QStringList() << QDir::toNativeSeparators(mediaFilePath));
1974                                                         return true;
1975                                                 }
1976                                         }
1977                                 }
1978                         }
1979                 }
1980         }
1981         return false;
1982 }
1983
1984 static void lamexp_init_dwmapi(void)
1985 {
1986         QReadLocker writeLock(&g_lamexp_dwmapi.lock);
1987
1988         //Not initialized yet?
1989         if(g_lamexp_dwmapi.bInitialized)
1990         {
1991                 return;
1992         }
1993         
1994         //Reset function pointers
1995         g_lamexp_dwmapi.dwmIsCompositionEnabled = NULL;
1996         g_lamexp_dwmapi.dwmExtendFrameIntoClientArea = NULL;
1997         g_lamexp_dwmapi.dwmEnableBlurBehindWindow = NULL;
1998                         
1999         //Does OS support DWM?
2000         if(lamexp_get_os_version() >= lamexp_winver_vista)
2001         {
2002                 //Load DWMAPI.DLL
2003                 g_lamexp_dwmapi.dwmapi_dll = new QLibrary("dwmapi.dll");
2004                 if(g_lamexp_dwmapi.dwmapi_dll->load())
2005                 {
2006                         //Initialize function pointers
2007                         g_lamexp_dwmapi.dwmIsCompositionEnabled      = (HRESULT (__stdcall*)(BOOL*))                       g_lamexp_dwmapi.dwmapi_dll->resolve("DwmIsCompositionEnabled");
2008                         g_lamexp_dwmapi.dwmExtendFrameIntoClientArea = (HRESULT (__stdcall*)(HWND, const MARGINS*))        g_lamexp_dwmapi.dwmapi_dll->resolve("DwmExtendFrameIntoClientArea");
2009                         g_lamexp_dwmapi.dwmEnableBlurBehindWindow    = (HRESULT (__stdcall*)(HWND, const DWM_BLURBEHIND*)) g_lamexp_dwmapi.dwmapi_dll->resolve("DwmEnableBlurBehindWindow");
2010                 }
2011                 else
2012                 {
2013                         LAMEXP_DELETE(g_lamexp_dwmapi.dwmapi_dll);
2014                         qWarning("Failed to load DWMAPI.DLL on a DWM-enabled system!");
2015                 }
2016         }
2017
2018         g_lamexp_dwmapi.bInitialized = true;
2019 }
2020
2021 /*
2022  * Enable "sheet of glass" effect on the given Window
2023  */
2024 bool lamexp_sheet_of_glass(QWidget *window)
2025 {
2026         QReadLocker readLock(&g_lamexp_dwmapi.lock);
2027
2028         //Initialize the DWM API
2029         while(!g_lamexp_dwmapi.bInitialized)
2030         {
2031                 readLock.unlock();
2032                 lamexp_init_dwmapi();
2033                 readLock.relock();
2034         }
2035
2036         BOOL bCompositionEnabled = FALSE;
2037
2038         //Required functions available?
2039         if((g_lamexp_dwmapi.dwmIsCompositionEnabled != NULL) && (g_lamexp_dwmapi.dwmExtendFrameIntoClientArea != NULL) && (g_lamexp_dwmapi.dwmEnableBlurBehindWindow != NULL))
2040         {
2041                 //Check if composition is currently enabled
2042                 if(HRESULT hr = g_lamexp_dwmapi.dwmIsCompositionEnabled(&bCompositionEnabled))
2043                 {
2044                         qWarning("DwmIsCompositionEnabled function has failed! (error %d)", hr);
2045                         return false;
2046                 }
2047         }
2048         
2049         //All functions available *and* composition enabled?
2050         if(!bCompositionEnabled)
2051         {
2052                 return false;
2053         }
2054
2055         //Enable the "sheet of glass" effect on this window
2056         MARGINS margins = {-1, -1, -1, -1};
2057         if(HRESULT hr = g_lamexp_dwmapi.dwmExtendFrameIntoClientArea(window->winId(), &margins))
2058         {
2059                 qWarning("DwmExtendFrameIntoClientArea function has failed! (error %d)", hr);
2060                 return false;
2061         }
2062
2063         //Create and populate the Blur Behind structure
2064         DWM_BLURBEHIND bb;
2065         memset(&bb, 0, sizeof(DWM_BLURBEHIND));
2066         bb.fEnable = TRUE;
2067         bb.dwFlags = DWM_BB_ENABLE;
2068         if(HRESULT hr = g_lamexp_dwmapi.dwmEnableBlurBehindWindow(window->winId(), &bb))
2069         {
2070                 qWarning("DwmEnableBlurBehindWindow function has failed! (error %d)", hr);
2071                 return false;
2072         }
2073
2074         //Required for Qt
2075         window->setAutoFillBackground(false);
2076         window->setAttribute(Qt::WA_TranslucentBackground);
2077         window->setAttribute(Qt::WA_NoSystemBackground);
2078
2079         return true;
2080 }
2081
2082 /*
2083  * Update "sheet of glass" effect on the given Window
2084  */
2085 bool lamexp_sheet_of_glass_update(QWidget *window)
2086 {
2087         QReadLocker readLock(&g_lamexp_dwmapi.lock);
2088
2089         //Initialize the DWM API
2090         while(!g_lamexp_dwmapi.bInitialized)
2091         {
2092                 readLock.unlock();
2093                 lamexp_init_dwmapi();
2094                 readLock.relock();
2095         }
2096
2097         BOOL bCompositionEnabled = FALSE;
2098
2099         //Required functions available?
2100         if((g_lamexp_dwmapi.dwmIsCompositionEnabled != NULL) && (g_lamexp_dwmapi.dwmEnableBlurBehindWindow != NULL))
2101         {
2102                 //Check if composition is currently enabled
2103                 if(HRESULT hr = g_lamexp_dwmapi.dwmIsCompositionEnabled(&bCompositionEnabled))
2104                 {
2105                         qWarning("DwmIsCompositionEnabled function has failed! (error %d)", hr);
2106                         return false;
2107                 }
2108         }
2109         
2110         //All functions available *and* composition enabled?
2111         if(!bCompositionEnabled)
2112         {
2113                 return false;
2114         }
2115
2116         //Create and populate the Blur Behind structure
2117         DWM_BLURBEHIND bb;
2118         memset(&bb, 0, sizeof(DWM_BLURBEHIND));
2119         bb.fEnable = TRUE;
2120         bb.dwFlags = DWM_BB_ENABLE;
2121         if(HRESULT hr = g_lamexp_dwmapi.dwmEnableBlurBehindWindow(window->winId(), &bb))
2122         {
2123                 qWarning("DwmEnableBlurBehindWindow function has failed! (error %d)", hr);
2124                 return false;
2125         }
2126
2127         return true;
2128 }
2129
2130 /*
2131  * Update the window icon
2132  */
2133 lamexp_icon_t *lamexp_set_window_icon(QWidget *window, const QIcon &icon, const bool bIsBigIcon)
2134 {
2135         if(!icon.isNull())
2136         {
2137                 const int extend = (bIsBigIcon ? 32 : 16);
2138                 if(HICON hIcon = lamexp_qicon2hicon(icon, extend, extend))
2139                 {
2140                         SendMessage(window->winId(), WM_SETICON, (bIsBigIcon ? ICON_BIG : ICON_SMALL), LPARAM(hIcon));
2141                         return reinterpret_cast<lamexp_icon_t*>(hIcon);
2142                 }
2143         }
2144         return NULL;
2145 }
2146
2147 /*
2148  * Free window icon
2149  */
2150 void lamexp_free_window_icon(lamexp_icon_t *icon)
2151 {
2152         if(HICON hIcon = reinterpret_cast<HICON>(icon))
2153         {
2154                 DestroyIcon(hIcon);
2155         }
2156 }
2157
2158 /*
2159  * Get system color info
2160  */
2161 QColor lamexp_system_color(const int color_id)
2162 {
2163         int nIndex = -1;
2164
2165         switch(color_id)
2166         {
2167         case lamexp_syscolor_text:
2168                 nIndex = COLOR_WINDOWTEXT;       /*Text in windows*/
2169                 break;
2170         case lamexp_syscolor_background:
2171                 nIndex = COLOR_WINDOW;           /*Window background*/
2172                 break;
2173         case lamexp_syscolor_caption:
2174                 nIndex = COLOR_CAPTIONTEXT;      /*Text in caption, size box, and scroll bar arrow box*/
2175                 break;
2176         default:
2177                 qWarning("Unknown system color id (%d) specified!", color_id);
2178                 nIndex = COLOR_WINDOWTEXT;
2179         }
2180         
2181         const DWORD rgb = GetSysColor(nIndex);
2182         QColor color(GetRValue(rgb), GetGValue(rgb), GetBValue(rgb));
2183         return color;
2184 }
2185
2186 /*
2187  * Check if the current user is an administartor (helper function)
2188  */
2189 static bool lamexp_user_is_admin_helper(void)
2190 {
2191         HANDLE hToken = NULL;
2192         if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
2193         {
2194                 return false;
2195         }
2196
2197         DWORD dwSize = 0;
2198         if(!GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwSize))
2199         {
2200                 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
2201                 {
2202                         CloseHandle(hToken);
2203                         return false;
2204                 }
2205         }
2206
2207         PTOKEN_GROUPS lpGroups = (PTOKEN_GROUPS) malloc(dwSize);
2208         if(!lpGroups)
2209         {
2210                 CloseHandle(hToken);
2211                 return false;
2212         }
2213
2214         if(!GetTokenInformation(hToken, TokenGroups, lpGroups, dwSize, &dwSize))
2215         {
2216                 free(lpGroups);
2217                 CloseHandle(hToken);
2218                 return false;
2219         }
2220
2221         PSID lpSid = NULL; SID_IDENTIFIER_AUTHORITY Authority = {SECURITY_NT_AUTHORITY};
2222         if(!AllocateAndInitializeSid(&Authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &lpSid))
2223         {
2224                 free(lpGroups);
2225                 CloseHandle(hToken);
2226                 return false;
2227         }
2228
2229         bool bResult = false;
2230         for(DWORD i = 0; i < lpGroups->GroupCount; i++)
2231         {
2232                 if(EqualSid(lpSid, lpGroups->Groups[i].Sid))
2233                 {
2234                         bResult = true;
2235                         break;
2236                 }
2237         }
2238
2239         FreeSid(lpSid);
2240         free(lpGroups);
2241         CloseHandle(hToken);
2242         return bResult;
2243 }
2244
2245 /*
2246  * Check if the current user is an administartor
2247  */
2248 bool lamexp_user_is_admin(void)
2249 {
2250         bool isAdmin = false;
2251
2252         //Check for process elevation and UAC support first!
2253         if(lamexp_process_is_elevated(&isAdmin))
2254         {
2255                 qWarning("Process is elevated -> user is admin!");
2256                 return true;
2257         }
2258         
2259         //If not elevated and UAC is not available -> user must be in admin group!
2260         if(!isAdmin)
2261         {
2262                 qDebug("UAC is disabled/unavailable -> checking for Administrators group");
2263                 isAdmin = lamexp_user_is_admin_helper();
2264         }
2265
2266         return isAdmin;
2267 }
2268
2269 /*
2270  * Check if file is a valid Win32/Win64 executable
2271  */
2272 bool lamexp_is_executable(const QString &path)
2273 {
2274         bool bIsExecutable = false;
2275         DWORD binaryType;
2276         if(GetBinaryType(QWCHAR(QDir::toNativeSeparators(path)), &binaryType))
2277         {
2278                 bIsExecutable = (binaryType == SCS_32BIT_BINARY || binaryType == SCS_64BIT_BINARY);
2279         }
2280         return bIsExecutable;
2281 }
2282
2283 /*
2284  * Fatal application exit
2285  */
2286 #pragma intrinsic(_InterlockedExchange)
2287 void lamexp_fatal_exit(const wchar_t* exitMessage, const wchar_t* errorBoxMessage)
2288 {
2289         static volatile long bFatalFlag = 0L;
2290
2291         if(_InterlockedExchange(&bFatalFlag, 1L) == 0L)
2292         {
2293                 if(GetCurrentThreadId() != g_main_thread_id)
2294                 {
2295                         HANDLE mainThread = OpenThread(THREAD_TERMINATE, FALSE, g_main_thread_id);
2296                         if(mainThread) TerminateThread(mainThread, ULONG_MAX);
2297                 }
2298         
2299                 if(errorBoxMessage)
2300                 {
2301                         MessageBoxW(NULL, errorBoxMessage, L"LameXP - GURU MEDITATION", MB_ICONERROR | MB_TOPMOST | MB_TASKMODAL);
2302                 }
2303
2304                 for(;;)
2305                 {
2306                         FatalAppExit(0, exitMessage);
2307                         TerminateProcess(GetCurrentProcess(), -1);
2308                 }
2309         }
2310
2311         TerminateThread(GetCurrentThread(), -1);
2312         Sleep(INFINITE);
2313 }
2314
2315 /*
2316  * Initialize debug thread
2317  */
2318 static const HANDLE g_debug_thread1 = LAMEXP_DEBUG ? NULL : lamexp_debug_thread_init();
2319
2320 /*
2321  * Get number private bytes [debug only]
2322  */
2323 unsigned long lamexp_dbg_private_bytes(void)
2324 {
2325 #if LAMEXP_DEBUG
2326         for(int i = 0; i < 8; i++) _heapmin();
2327         PROCESS_MEMORY_COUNTERS_EX memoryCounters;
2328         memoryCounters.cb = sizeof(PROCESS_MEMORY_COUNTERS_EX);
2329         GetProcessMemoryInfo(GetCurrentProcess(), (PPROCESS_MEMORY_COUNTERS) &memoryCounters, sizeof(PROCESS_MEMORY_COUNTERS_EX));
2330         return memoryCounters.PrivateUsage;
2331 #else
2332         THROW("Cannot call this function in a non-debug build!");
2333 #endif //LAMEXP_DEBUG
2334 }
2335
2336 /*
2337  * Output string to debugger [debug only]
2338  */
2339 void lamexp_dbg_dbg_output_string(const char* format, ...)
2340 {
2341 #if LAMEXP_DEBUG
2342         char buffer[256];
2343         va_list args;
2344         va_start (args, format);
2345         vsnprintf_s(buffer, 256, _TRUNCATE, format, args);
2346         OutputDebugStringA(buffer);
2347         va_end(args);
2348 #else
2349         THROW("Cannot call this function in a non-debug build!");
2350 #endif //LAMEXP_DEBUG
2351 }
2352
2353 ///////////////////////////////////////////////////////////////////////////////
2354 // INITIALIZATION
2355 ///////////////////////////////////////////////////////////////////////////////
2356
2357 extern "C" void _lamexp_global_init_win32(void)
2358 {
2359         if((!LAMEXP_DEBUG) && lamexp_check_for_debugger())
2360         {
2361                 lamexp_fatal_exit(L"Not a debug build. Please unload debugger and try again!");
2362         }
2363
2364         //Zero *before* constructors are called
2365         LAMEXP_ZERO_MEMORY(g_lamexp_argv);
2366         LAMEXP_ZERO_MEMORY(g_lamexp_known_folder);
2367         LAMEXP_ZERO_MEMORY(g_lamexp_os_version);
2368         LAMEXP_ZERO_MEMORY(g_lamexp_wine);
2369         LAMEXP_ZERO_MEMORY(g_lamexp_themes_enabled);
2370         LAMEXP_ZERO_MEMORY(g_lamexp_dwmapi);
2371         LAMEXP_ZERO_MEMORY(g_lamexp_sounds);
2372 }
2373
2374 ///////////////////////////////////////////////////////////////////////////////
2375 // FINALIZATION
2376 ///////////////////////////////////////////////////////////////////////////////
2377
2378 extern "C" void _lamexp_global_free_win32(void)
2379 {
2380         //Clear folder cache
2381         LAMEXP_DELETE(g_lamexp_known_folder.knownFolders);
2382
2383         //Destroy Qt application object
2384         QApplication *application = dynamic_cast<QApplication*>(QApplication::instance());
2385         LAMEXP_DELETE(application);
2386
2387         //Release DWM API
2388         g_lamexp_dwmapi.dwmIsCompositionEnabled = NULL;
2389         g_lamexp_dwmapi.dwmExtendFrameIntoClientArea = NULL;
2390         g_lamexp_dwmapi.dwmEnableBlurBehindWindow = NULL;
2391         LAMEXP_DELETE(g_lamexp_dwmapi.dwmapi_dll);
2392
2393         //Free STDOUT and STDERR buffers
2394         if(g_lamexp_console_attached)
2395         {
2396                 if(std::filebuf *tmp = dynamic_cast<std::filebuf*>(std::cout.rdbuf()))
2397                 {
2398                         std::cout.rdbuf(NULL);
2399                         LAMEXP_DELETE(tmp);
2400                 }
2401                 if(std::filebuf *tmp = dynamic_cast<std::filebuf*>(std::cerr.rdbuf()))
2402                 {
2403                         std::cerr.rdbuf(NULL);
2404                         LAMEXP_DELETE(tmp);
2405                 }
2406         }
2407
2408         //Close log file
2409         if(g_lamexp_log_file)
2410         {
2411                 fclose(g_lamexp_log_file);
2412                 g_lamexp_log_file = NULL;
2413         }
2414
2415         //Clear sound cache
2416         LAMEXP_DELETE(g_lamexp_sounds.sound_db);
2417
2418         //Free CLI Arguments
2419         LAMEXP_DELETE(g_lamexp_argv.list);
2420 }