OSDN Git Service

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