OSDN Git Service

78bdcb358fe7b19065f6f3256876b875db771a03
[mutilities/MUtilities.git] / src / OSSupport_Win32.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2021 LoRd_MuldeR <MuldeR2@GMX.de>
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
18 //
19 // http://www.gnu.org/licenses/lgpl-2.1.txt
20 //////////////////////////////////////////////////////////////////////////////////
21
22 //Win32 API
23 #define PSAPI_VERSION 1
24 #define WIN32_LEAN_AND_MEAN 1
25 #include <Windows.h>
26 #include <Psapi.h>
27 #include <Sensapi.h>
28 #include <Shellapi.h>
29 #include <PowrProf.h>
30 #include <Mmsystem.h>
31 #include <WinIoCtl.h>
32 #pragma warning(push)
33 #pragma warning(disable:4091) //for MSVC2015
34 #include <ShlObj.h>
35 #pragma warning(pop)
36
37 //CRT
38 #include <io.h>
39
40 //Internal
41 #include <MUtils/Global.h>
42 #include <MUtils/OSSupport.h>
43 #include <MUtils/GUI.h>
44 #include "Internal.h"
45 #include "CriticalSection_Win32.h"
46 #include "Utils_Win32.h"
47
48 //Qt
49 #include <QMap>
50 #include <QReadWriteLock>
51 #include <QDir>
52 #include <QWidget>
53 #include <QProcess>
54 #include <QSet>
55
56 //Main thread ID
57 static const DWORD g_main_thread_id = GetCurrentThreadId();
58
59 ///////////////////////////////////////////////////////////////////////////////
60 // SYSTEM MESSAGE
61 ///////////////////////////////////////////////////////////////////////////////
62
63 static const UINT g_msgBoxFlags = MB_TOPMOST | MB_TASKMODAL | MB_SETFOREGROUND;
64
65 void MUtils::OS::system_message_nfo(const wchar_t *const title, const wchar_t *const text)
66 {
67         MessageBoxW(NULL, text, title, g_msgBoxFlags | MB_ICONINFORMATION);
68 }
69
70 void MUtils::OS::system_message_wrn(const wchar_t *const title, const wchar_t *const text)
71 {
72         MessageBoxW(NULL, text, title, g_msgBoxFlags | MB_ICONWARNING);
73 }
74
75 void MUtils::OS::system_message_err(const wchar_t *const title, const wchar_t *const text)
76 {
77         MessageBoxW(NULL, text, title, g_msgBoxFlags | MB_ICONERROR);
78 }
79
80 ///////////////////////////////////////////////////////////////////////////////
81 // FETCH CLI ARGUMENTS
82 ///////////////////////////////////////////////////////////////////////////////
83
84 static QReadWriteLock                          g_arguments_lock;
85 static QScopedPointer<MUtils::OS::ArgumentMap> g_arguments_list;
86
87 const QStringList MUtils::OS::crack_command_line(const QString &command_line)
88 {
89         int nArgs = 0;
90         LPWSTR *szArglist = CommandLineToArgvW(command_line.isNull() ? GetCommandLineW() : MUTILS_WCHR(command_line), &nArgs);
91
92         QStringList command_line_tokens;
93         if(NULL != szArglist)
94         {
95                 for(int i = 0; i < nArgs; i++)
96                 {
97                         const QString argStr = MUTILS_QSTR(szArglist[i]).trimmed();
98                         if(!argStr.isEmpty())
99                         {
100                                 command_line_tokens << argStr;
101                         }
102                 }
103                 LocalFree(szArglist);
104         }
105
106         return command_line_tokens;
107 }
108
109 const MUtils::OS::ArgumentMap &MUtils::OS::arguments(void)
110 {
111         QReadLocker readLock(&g_arguments_lock);
112
113         //Already initialized?
114         if(!g_arguments_list.isNull())
115         {
116                 return (*(g_arguments_list.data()));
117         }
118
119         readLock.unlock();
120         QWriteLocker writeLock(&g_arguments_lock);
121
122         //Still not initialized?
123         if(!g_arguments_list.isNull())
124         {
125                 return (*(g_arguments_list.data()));
126         }
127
128         g_arguments_list.reset(new ArgumentMap());
129         const QStringList argList = crack_command_line();
130
131         if(!argList.isEmpty())
132         {
133                 const QString argPrefix = QLatin1String("--");
134                 const QChar   separator = QLatin1Char('=');
135
136                 bool firstToken = true;
137                 for(QStringList::ConstIterator iter = argList.constBegin(); iter != argList.constEnd(); iter++)
138                 {
139                         if(firstToken)
140                         {
141                                 firstToken = false;
142                                 continue; /*skip executable file name*/
143                         }
144                         if(iter->startsWith(argPrefix))
145                         {
146                                 const QString argData = iter->mid(2).trimmed();
147                                 if(argData.length() > 0)
148                                 {
149                                         const int separatorIndex = argData.indexOf(separator);
150                                         if(separatorIndex > 0)
151                                         {
152                                                 const QString argKey = argData.left(separatorIndex).trimmed();
153                                                 const QString argVal = argData.mid(separatorIndex + 1).trimmed();
154                                                 g_arguments_list->insertMulti(argKey.toLower(), argVal);
155                                         }
156                                         else
157                                         {
158                                                 g_arguments_list->insertMulti(argData.toLower(), QString());
159                                         }
160                                 }
161                         }
162                 }
163         }
164         else if(argList.empty())
165         {
166                 qWarning("CommandLineToArgvW() has failed !!!");
167         }
168
169         return (*(g_arguments_list.data()));
170 }
171
172 ///////////////////////////////////////////////////////////////////////////////
173 // COPY FILE
174 ///////////////////////////////////////////////////////////////////////////////
175
176 typedef struct _progress_callback_data_t
177 {
178         MUtils::OS::progress_callback_t callback_function;
179         void *user_data;
180 }
181 progress_callback_data_t;
182
183 static DWORD __stdcall copy_file_progress(LARGE_INTEGER TotalFileSize, LARGE_INTEGER TotalBytesTransferred, LARGE_INTEGER /*StreamSize*/, LARGE_INTEGER /*StreamBytesTransferred*/, DWORD /*dwStreamNumber*/, DWORD /*dwCallbackReason*/, HANDLE /*hSourceFile*/, HANDLE /*hDestinationFile*/, LPVOID lpData)
184 {
185         if(const progress_callback_data_t *data = (progress_callback_data_t*) lpData)
186         {
187                 const double progress = qBound(0.0, double(TotalBytesTransferred.QuadPart) / double(TotalFileSize.QuadPart), 1.0);
188                 return data->callback_function(progress, data->user_data) ? PROGRESS_CONTINUE : PROGRESS_CANCEL;
189         }
190         return PROGRESS_CONTINUE;
191 }
192
193 MUTILS_API bool MUtils::OS::copy_file(const QString &sourcePath, const QString &outputPath, const bool &overwrite, const progress_callback_t callback, void *const userData)
194 {
195         progress_callback_data_t callback_data = { callback, userData };
196         BOOL cancel = FALSE;
197         const BOOL result = CopyFileExW(MUTILS_WCHR(QDir::toNativeSeparators(sourcePath)), MUTILS_WCHR(QDir::toNativeSeparators(outputPath)), ((callback_data.callback_function) ? copy_file_progress : NULL), ((callback_data.callback_function) ? &callback_data : NULL), &cancel, (overwrite ? 0 : COPY_FILE_FAIL_IF_EXISTS));
198
199         if(result == FALSE)
200         {
201                 const DWORD errorCode = GetLastError();
202                 if(errorCode != ERROR_REQUEST_ABORTED)
203                 {
204                         qWarning("CopyFile() failed with error code 0x%08X!", errorCode);
205                 }
206                 else
207                 {
208                         qWarning("CopyFile() operation was abroted by user!");
209                 }
210         }
211
212         return (result != FALSE);
213 }
214
215 ///////////////////////////////////////////////////////////////////////////////
216 // GET FILE VERSION
217 ///////////////////////////////////////////////////////////////////////////////
218
219 static bool get_file_version_helper(const QString fileName, PVOID buffer, const size_t &size, quint16 *const major, quint16 *const minor, quint16 *const patch, quint16 *const build)
220 {
221         if(!GetFileVersionInfo(MUTILS_WCHR(fileName), 0, size, buffer))
222         {
223                 qWarning("GetFileVersionInfo() has failed, file version cannot be determined!");
224                 return false;
225         }
226
227         VS_FIXEDFILEINFO *verInfo;
228         UINT verInfoLen;
229         if(!VerQueryValue(buffer, L"\\", (LPVOID*)(&verInfo), &verInfoLen))
230         {
231                 qWarning("VerQueryValue() has failed, file version cannot be determined!");
232                 return false;
233         }
234
235         if(major) *major = quint16((verInfo->dwFileVersionMS >> 16) & 0x0000FFFF);
236         if(minor) *minor = quint16((verInfo->dwFileVersionMS)       & 0x0000FFFF);
237         if(patch) *patch = quint16((verInfo->dwFileVersionLS >> 16) & 0x0000FFFF);
238         if(build) *build = quint16((verInfo->dwFileVersionLS)       & 0x0000FFFF);
239
240         return true;
241 }
242
243 bool MUtils::OS::get_file_version(const QString fileName, quint16 *const major, quint16 *const minor, quint16 *const patch, quint16 *const build)
244 {
245         if(major) *major = 0U; if(minor) *minor = 0U;
246         if(patch) *patch = 0U; if(build) *build = 0U;
247
248         const DWORD size = GetFileVersionInfoSize(MUTILS_WCHR(fileName), NULL);
249         if(size < 1)
250         {
251                 qWarning("GetFileVersionInfoSize() has failed, file version cannot be determined!");
252                 return false;
253         }
254         
255         PVOID buffer = _malloca(size);
256         if(!buffer)
257         {
258                 qWarning("Memory allocation has failed!");
259                 return false;
260         }
261
262         const bool success = get_file_version_helper(fileName, buffer, size, major, minor, patch, build);
263
264         _freea(buffer);
265         return success;
266 }
267
268 ///////////////////////////////////////////////////////////////////////////////
269 // OS VERSION DETECTION
270 ///////////////////////////////////////////////////////////////////////////////
271
272 static bool g_os_version_initialized = false;
273 static MUtils::OS::Version::os_version_t g_os_version_info = MUtils::OS::Version::UNKNOWN_OPSYS;
274 static QReadWriteLock g_os_version_lock;
275
276 //Maps marketing names to the actual Windows NT versions
277 static const struct
278 {
279         MUtils::OS::Version::os_version_t version;
280         const char friendlyName[64];
281 }
282 g_os_version_lut[] =
283 {
284         { MUtils::OS::Version::WINDOWS_WIN2K, "Windows 2000"                                  },        //2000
285         { MUtils::OS::Version::WINDOWS_WINXP, "Windows XP or Windows XP Media Center Edition" },        //XP
286         { MUtils::OS::Version::WINDOWS_XPX64, "Windows Server 2003 or Windows XP x64"         },        //XP_x64
287         { MUtils::OS::Version::WINDOWS_VISTA, "Windows Vista or Windows Server 2008"          },        //Vista
288         { MUtils::OS::Version::WINDOWS_WIN70, "Windows 7 or Windows Server 2008 R2"           },        //7
289         { MUtils::OS::Version::WINDOWS_WIN80, "Windows 8 or Windows Server 2012"              },        //8
290         { MUtils::OS::Version::WINDOWS_WIN81, "Windows 8.1 or Windows Server 2012 R2"         },        //8.1
291         { MUtils::OS::Version::WINDOWS_WIN10, "Windows 10 or Windows Server 2016/2019"        },        //10
292         { MUtils::OS::Version::WINDOWS_WIN11, "Windows 11 or Windows Server 2022"             },        //11
293         { MUtils::OS::Version::UNKNOWN_OPSYS, "N/A" }
294 };
295
296 //OS version data dtructures
297 namespace MUtils
298 {
299         namespace OS
300         {
301                 namespace Version
302                 {
303                         //Comparision operators for os_version_t
304                         bool os_version_t::operator>  (const os_version_t &rhs) const { return (versionMajor > rhs.versionMajor) || ((versionMajor == rhs.versionMajor) && (versionMinor > rhs.versionMinor)) || ((versionMajor == rhs.versionMajor) && (versionMinor == rhs.versionMinor) && (versionBuild >  rhs.versionBuild)); }
305                         bool os_version_t::operator>= (const os_version_t &rhs) const { return (versionMajor > rhs.versionMajor) || ((versionMajor == rhs.versionMajor) && (versionMinor > rhs.versionMinor)) || ((versionMajor == rhs.versionMajor) && (versionMinor == rhs.versionMinor) && (versionBuild >= rhs.versionBuild)); }
306                         bool os_version_t::operator<  (const os_version_t &rhs) const { return (versionMajor < rhs.versionMajor) || ((versionMajor == rhs.versionMajor) && (versionMinor < rhs.versionMinor)) || ((versionMajor == rhs.versionMajor) && (versionMinor == rhs.versionMinor) && (versionBuild <  rhs.versionBuild)); }
307                         bool os_version_t::operator<= (const os_version_t &rhs) const { return (versionMajor < rhs.versionMajor) || ((versionMajor == rhs.versionMajor) && (versionMinor < rhs.versionMinor)) || ((versionMajor == rhs.versionMajor) && (versionMinor == rhs.versionMinor) && (versionBuild <= rhs.versionBuild)); }
308
309                         //Known Windows NT versions
310                         const os_version_t WINDOWS_WIN2K = { OS_WINDOWS,  5, 0,  2195, 0 };     // 2000
311                         const os_version_t WINDOWS_WINXP = { OS_WINDOWS,  5, 1,  2600, 0 };     // XP
312                         const os_version_t WINDOWS_XPX64 = { OS_WINDOWS,  5, 2,  3790, 0 };     // XP_x64
313                         const os_version_t WINDOWS_VISTA = { OS_WINDOWS,  6, 0,  6000, 0 };     // Vista
314                         const os_version_t WINDOWS_WIN70 = { OS_WINDOWS,  6, 1,  7600, 0 };     // 7
315                         const os_version_t WINDOWS_WIN80 = { OS_WINDOWS,  6, 2,  9200, 0 };     // 8
316                         const os_version_t WINDOWS_WIN81 = { OS_WINDOWS,  6, 3,  9600, 0 };     // 8.1
317                         const os_version_t WINDOWS_WIN10 = { OS_WINDOWS, 10, 0, 10240, 0 };     // 10
318                         const os_version_t WINDOWS_WIN11 = { OS_WINDOWS, 10, 0, 20348, 0 };     // 11
319
320                         //Unknown OS
321                         const os_version_t UNKNOWN_OPSYS = { OS_UNKNOWN, 0,  0,     0, 0 };     // N/A
322                 }
323         }
324 }
325
326 static inline DWORD SAFE_ADD(const DWORD &a, const DWORD &b, const DWORD &limit = MAXDWORD)
327 {
328         return ((a >= limit) || (b >= limit) || ((limit - a) <= b)) ? limit : (a + b);
329 }
330
331 static void initialize_os_version(OSVERSIONINFOEXW *const osInfo)
332 {
333         memset(osInfo, 0, sizeof(OSVERSIONINFOEXW));
334         osInfo->dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
335 }
336
337 static inline DWORD initialize_step_size(const DWORD &limit)
338 {
339         DWORD result = 1;
340         while (result < limit)
341         {
342                 result = SAFE_ADD(result, result);
343         }
344         return result;
345 }
346
347 #pragma warning(push)
348 #pragma warning(disable: 4996)
349
350 static bool rtl_get_version(OSVERSIONINFOEXW *const osInfo)
351 {
352         typedef LONG(__stdcall *RtlGetVersion)(LPOSVERSIONINFOEXW);
353         if (const HMODULE ntdll = GetModuleHandleW(L"ntdll"))
354         {
355                 if (const RtlGetVersion pRtlGetVersion = (RtlGetVersion)GetProcAddress(ntdll, "RtlGetVersion"))
356                 {
357                         initialize_os_version(osInfo);
358                         if (pRtlGetVersion(osInfo) == 0)
359                         {
360                                 return true;
361                         }
362                 }
363         }
364
365         //Fallback
366         initialize_os_version(osInfo);
367         return (GetVersionExW((LPOSVERSIONINFOW)osInfo) != FALSE);
368 }
369
370 #pragma warning(pop) 
371
372 static bool get_real_os_version(unsigned int *const major, unsigned int *const minor, unsigned int *const build, unsigned int *const spack, bool *const pbOverride)
373 {
374         static const DWORD MAX_VERSION = MAXWORD;
375         static const DWORD MAX_BUILDNO = MAXINT;
376         static const DWORD MAX_SRVCPCK = MAXWORD;
377
378         *major = *minor = *build = *spack = 0U;
379         *pbOverride = false;
380         
381         //Initialize local variables
382         OSVERSIONINFOEXW osvi;
383         memset(&osvi, 0, sizeof(OSVERSIONINFOEXW));
384         osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
385
386         //Try GetVersionEx() first
387         if (!rtl_get_version(&osvi))
388         {
389                 qWarning("GetVersionEx() has failed, cannot detect Windows version!");
390                 return false;
391         }
392
393         //Make sure we are running on NT
394         if (osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
395         {
396                 *major = osvi.dwMajorVersion;
397                 *minor = osvi.dwMinorVersion;
398                 *build = osvi.dwBuildNumber;
399                 *spack = osvi.wServicePackMajor;
400         }
401         else
402         {
403                 qWarning("Not running on Windows NT, unsupported operating system!");
404                 return false;
405         }
406
407         return true;
408 }
409
410 const MUtils::OS::Version::os_version_t &MUtils::OS::os_version(void)
411 {
412         QReadLocker readLock(&g_os_version_lock);
413
414         //Already initialized?
415         if(g_os_version_initialized)
416         {
417                 return g_os_version_info;
418         }
419         
420         readLock.unlock();
421         QWriteLocker writeLock(&g_os_version_lock);
422
423         //Initialized now?
424         if(g_os_version_initialized)
425         {
426                 return g_os_version_info;
427         }
428
429         //Detect OS version
430         unsigned int major, minor, build, spack; bool overrideFlg;
431         if(get_real_os_version(&major, &minor, &build, &spack, &overrideFlg))
432         {
433                 g_os_version_info.type = Version::OS_WINDOWS;
434                 g_os_version_info.versionMajor = major;
435                 g_os_version_info.versionMinor = minor;
436                 g_os_version_info.versionBuild = build;
437                 g_os_version_info.versionSPack = spack;
438                 g_os_version_info.overrideFlag = overrideFlg;
439         }
440         else
441         {
442                 qWarning("Failed to determine the operating system version!");
443         }
444
445         //Completed
446         g_os_version_initialized = true;
447         return g_os_version_info;
448 }
449
450 const char *MUtils::OS::os_friendly_name(const MUtils::OS::Version::os_version_t &os_version)
451 {
452         const char *friendly_name = NULL;
453         for(size_t i = 0; g_os_version_lut[i].version.type != MUtils::OS::Version::OS_UNKNOWN; i++)
454         {
455                 if(os_version >= g_os_version_lut[i].version)
456                 {
457                         friendly_name = g_os_version_lut[i].friendlyName;
458                         continue;
459                 }
460                 break;
461         }
462
463         return friendly_name;
464 }
465
466 ///////////////////////////////////////////////////////////////////////////////
467 // OS ARCHITECTURE DETECTION
468 ///////////////////////////////////////////////////////////////////////////////
469
470 static bool g_os_arch_initialized = false;
471 static MUtils::OS::os_arch_t g_os_arch = MUtils::OS::os_arch_t(0);
472 static QReadWriteLock g_os_arch_lock;
473
474 static MUtils::OS::os_arch_t detect_os_arch(void)
475 {
476 #if (!(defined(_M_X64) || defined(_M_IA64)))
477         typedef BOOL(WINAPI * IsWow64ProcessFun)(__in HANDLE hProcess, __out PBOOL Wow64Process);
478         const IsWow64ProcessFun isWow64ProcessPtr = MUtils::Win32Utils::resolve<IsWow64ProcessFun>(QLatin1String("kernel32"), QLatin1String("IsWow64Process"));
479         if (isWow64ProcessPtr)
480         {
481                 BOOL x64flag = FALSE;
482                 if (isWow64ProcessPtr(GetCurrentProcess(), &x64flag))
483                 {
484                         if (x64flag)
485                         {
486                                 return MUtils::OS::ARCH_X64;
487                         }
488                 }
489         }
490         return MUtils::OS::ARCH_X86;
491 #else
492         return MUtils::OS::ARCH_X64;
493 #endif
494 }
495
496 const MUtils::OS::os_arch_t &MUtils::OS::os_architecture(void)
497 {
498         QReadLocker readLock(&g_os_arch_lock);
499
500         //Already initialized?
501         if (g_os_arch_initialized)
502         {
503                 return g_os_arch;
504         }
505
506         readLock.unlock();
507         QWriteLocker writeLock(&g_os_arch_lock);
508
509         //Initialized now?
510         if (g_os_arch_initialized)
511         {
512                 return g_os_arch;
513         }
514
515         g_os_arch = detect_os_arch();
516         g_os_arch_initialized = MUTILS_BOOLIFY(g_os_arch);
517         return g_os_arch;
518 }
519
520 ///////////////////////////////////////////////////////////////////////////////
521 // WINE DETECTION
522 ///////////////////////////////////////////////////////////////////////////////
523
524 static bool g_wine_deteced = false;
525 static bool g_wine_initialized = false;
526 static QReadWriteLock g_wine_lock;
527
528 static const bool detect_wine(void)
529 {
530         void *const ptr = MUtils::Win32Utils::resolve<void*>(QLatin1String("ntdll"), QLatin1String("wine_get_version"));
531         return (ptr != NULL);
532 }
533
534 const bool &MUtils::OS::running_on_wine(void)
535 {
536         QReadLocker readLock(&g_wine_lock);
537
538         //Already initialized?
539         if(g_wine_initialized)
540         {
541                 return g_wine_deteced;
542         }
543
544         readLock.unlock();
545         QWriteLocker writeLock(&g_wine_lock);
546
547         //Initialized now?
548         if(g_wine_initialized)
549         {
550                 return g_wine_deteced;
551         }
552
553         //Try to detect Wine
554         g_wine_deteced = detect_wine();
555         g_wine_initialized = true;
556
557         return g_wine_deteced;
558 }
559
560 ///////////////////////////////////////////////////////////////////////////////
561 // KNWON FOLDERS
562 ///////////////////////////////////////////////////////////////////////////////
563
564 static QReadWriteLock                         g_known_folders_lock;
565 static QScopedPointer<QHash<size_t, QString>> g_known_folders_data;
566
567 typedef HRESULT (WINAPI *SHGetKnownFolderPathProc)(const GUID &rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath);
568 typedef HRESULT (WINAPI *SHGetFolderPathProc)(HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath);
569
570 static const struct
571 {
572         MUtils::OS::known_folder_t id;
573         INT32 csidl;
574         GUID known_folder_id;
575 }
576 s_known_folders_lut[] =
577 {
578         { MUtils::OS::FOLDER_PROFILE_USER,  0x0028, { 0x5E6C858F, 0x0E22, 0x4760, { 0x9A, 0xFE, 0xEA, 0x33, 0x17, 0xB6, 0x71, 0x73 } } },  //CSIDL_PROFILE
579         { MUtils::OS::FOLDER_PROFILE_PUBL,  0x0000, { 0xDFDF76A2, 0xC82A, 0x4D63, { 0x90, 0x6A, 0x56, 0x44, 0xAC, 0x45, 0x73, 0x85 } } },  //FOLDERID_Public
580         { MUtils::OS::FOLDER_APPDATA_ROAM,  0x001A, { 0x3EB685DB, 0x65F9, 0x4CF6, { 0xA0, 0x3A, 0xE3, 0xEF, 0x65, 0x72, 0x9F, 0x3D } } },  //CSIDL_APPDATA
581         { MUtils::OS::FOLDER_APPDATA_LOCA,  0x001C, { 0xF1B32785, 0x6FBA, 0x4FCF, { 0x9D, 0x55, 0x7B, 0x8E, 0x7F, 0x15, 0x70, 0x91 } } },  //CSIDL_LOCAL_APPDATA
582         { MUtils::OS::FOLDER_DOCS_USER,     0x0005, { 0xFDD39AD0, 0x238F, 0x46AF, { 0xAD, 0xB4, 0x6C, 0x85, 0x48, 0x03, 0x69, 0xC7 } } },  //CSIDL_MYDOCUMENTS
583         { MUtils::OS::FOLDER_DOCS_PUBL,     0x002E, { 0xED4824AF, 0xDCE4, 0x45A8, { 0x81, 0xE2, 0xFC, 0x79, 0x65, 0x08, 0x36, 0x34 } } },  //CSIDL_COMMON_DOCUMENTS
584         { MUtils::OS::FOLDER_DESKTOP_USER,  0x0010, { 0xB4BFCC3A, 0xDB2C, 0x424C, { 0xB0, 0x29, 0x7F, 0xE9, 0x9A, 0x87, 0xC6, 0x41 } } },  //CSIDL_DESKTOPDIRECTORY
585         { MUtils::OS::FOLDER_DESKTOP_PUBL,  0x0019, { 0xC4AA340D, 0xF20F, 0x4863, { 0xAF, 0xEF, 0xF8, 0x7E, 0xF2, 0xE6, 0xBA, 0x25 } } },  //CSIDL_COMMON_DESKTOPDIRECTORY
586         { MUtils::OS::FOLDER_PICTURES_USER, 0x0027, { 0x33E28130, 0x4E1E, 0x4676, { 0x83, 0x5A, 0x98, 0x39, 0x5C, 0x3B, 0xC3, 0xBB } } },  //CSIDL_MYPICTURES
587         { MUtils::OS::FOLDER_PICTURES_PUBL, 0x0036, { 0xB6EBFB86, 0x6907, 0x413C, { 0x9A, 0xF7, 0x4F, 0xC2, 0xAB, 0xF0, 0x7C, 0xC5 } } },  //CSIDL_COMMON_PICTURES
588         { MUtils::OS::FOLDER_MUSIC_USER,    0x000D, { 0x4BD8D571, 0x6D19, 0x48D3, { 0xBE, 0x97, 0x42, 0x22, 0x20, 0x08, 0x0E, 0x43 } } },  //CSIDL_MYMUSIC
589         { MUtils::OS::FOLDER_MUSIC_PUBL,    0x0035, { 0x3214FAB5, 0x9757, 0x4298, { 0xBB, 0x61, 0x92, 0xA9, 0xDE, 0xAA, 0x44, 0xFF } } },  //CSIDL_COMMON_MUSIC
590         { MUtils::OS::FOLDER_VIDEO_USER,    0x000E, { 0x18989B1D, 0x99B5, 0x455B, { 0x84, 0x1C, 0xAB, 0x7C, 0x74, 0xE4, 0xDD, 0xFC } } },  //CSIDL_MYVIDEO
591         { MUtils::OS::FOLDER_VIDEO_PUBL,    0x0037, { 0x2400183A, 0x6185, 0x49FB, { 0xA2, 0xD8, 0x4A, 0x39, 0x2A, 0x60, 0x2B, 0xA3 } } },  //CSIDL_COMMON_VIDEO
592         { MUtils::OS::FOLDER_PROGRAMS_DEF,  0x0026, { 0x905E63B6, 0xC1BF, 0x494E, { 0xB2, 0x9C, 0x65, 0xB7, 0x32, 0xD3, 0xD2, 0x1A } } },  //CSIDL_PROGRAM_FILES
593         { MUtils::OS::FOLDER_PROGRAMS_X86,  0x002A, { 0x7C5A40EF, 0xA0FB, 0x4BFC, { 0x87, 0x4A, 0xC0, 0xF2, 0xE0, 0xB9, 0xFA, 0x8E } } },  //CSIDL_PROGRAM_FILESX86
594         { MUtils::OS::FOLDER_PROGRAMS_X64,  0x0000, { 0x6D809377, 0x6AF0, 0x444B, { 0x89, 0x57, 0xA3, 0x77, 0x3F, 0x02, 0x20, 0x0E } } },  //FOLDERID_ProgramFilesX64 (not supported for 32-bit applications)
595         { MUtils::OS::FOLDER_SYSROOT,       0x0024, { 0xF38BF404, 0x1D43, 0x42F2, { 0x93, 0x05, 0x67, 0xDE, 0x0B, 0x28, 0xFC, 0x23 } } },  //CSIDL_WINDOWS
596         { MUtils::OS::FOLDER_SYSTEM_DEF,    0x0025, { 0x1AC14E77, 0x02E7, 0x4E5D, { 0xB7, 0x44, 0x2E, 0xB1, 0xAE, 0x51, 0x98, 0xB7 } } },  //CSIDL_SYSTEM
597         { MUtils::OS::FOLDER_SYSTEM_X86,    0x0029, { 0xD65231B0, 0xB2F1, 0x4857, { 0xA4, 0xCE, 0xA8, 0xE7, 0xC6, 0xEA, 0x7D, 0x27 } } },  //CSIDL_SYSTEMX86
598         { static_cast<MUtils::OS::known_folder_t>(0) }
599 };
600
601 static QString known_folder_verify(const wchar_t *const path)
602 {
603         const QDir folderPath = QDir(QDir::fromNativeSeparators(MUTILS_QSTR(path)));
604         if (folderPath.exists())
605         {
606                 const QString absolutePath = folderPath.absolutePath();
607                 const HANDLE handle = CreateFileW(MUTILS_WCHR(QDir::toNativeSeparators(absolutePath)), 0U, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL);
608                 if (handle != INVALID_HANDLE_VALUE)
609                 {
610                         SetHandleInformation(handle, HANDLE_FLAG_PROTECT_FROM_CLOSE, HANDLE_FLAG_PROTECT_FROM_CLOSE);
611                         return absolutePath;
612                 }
613         }
614
615         return QString(); /*failed*/
616 }
617
618 static QString known_folder_fallback(const size_t folderId)
619 {
620         static const DWORD s_shgfpTypes[3] =
621         {
622                 0, /*SHGFP_TYPE_CURRENT*/
623                 1, /*SHGFP_TYPE_DEFAULT*/
624                 MAXDWORD
625         };
626
627         static const INT32 s_shgfpFlags[3] = 
628         {
629                 0x0000, /*No extra flags*/
630                 0x8000, /*SHGFP_FLAG_CREATE*/
631                 MAXINT32
632         };
633
634         if (s_known_folders_lut[folderId].csidl)
635         {
636                 const SHGetFolderPathProc getFolderPath = MUtils::Win32Utils::resolve<SHGetFolderPathProc>(QLatin1String("shell32"), QLatin1String("SHGetFolderPathW"));
637                 if (getFolderPath)
638                 {
639                         QVector<WCHAR> pathBuffer(MAX_PATH);
640                         for (size_t i = 0; s_shgfpTypes[i] != MAXDWORD; ++i)
641                         {
642                                 for (size_t j = 0; s_shgfpFlags[j] != MAXINT32; ++j)
643                                 {
644                                         if (getFolderPath(NULL, s_known_folders_lut[folderId].csidl | s_shgfpFlags[j], NULL, s_shgfpTypes[i], pathBuffer.data()) == S_OK)
645                                         {
646                                                 //MessageBoxW(0, pathBuffer.data(), L"SHGetFolderPathW", MB_TOPMOST);
647                                                 const QString folderPath = known_folder_verify(pathBuffer.data());
648                                                 if (!folderPath.isEmpty())
649                                                 {
650                                                         return folderPath;
651                                                 }
652                                         }
653                                 }
654                         }
655                 }
656         }
657
658         return QString(); /*failed!*/
659 }
660
661 static QString known_folder_detect(const size_t folderId)
662 {
663         typedef enum
664         {
665                 KF_FLAG_DEFAULT      = 0x00000000,
666                 KF_FLAG_DEFAULT_PATH = 0x00000400,
667                 KF_FLAG_CREATE       = 0x00008000
668         }
669         kf_flag_t;
670
671         static const DWORD s_kfFlags[5] =
672         { 
673                 KF_FLAG_DEFAULT,
674                 KF_FLAG_CREATE,
675                 KF_FLAG_DEFAULT_PATH,
676                 KF_FLAG_DEFAULT_PATH | KF_FLAG_CREATE,
677                 MAXDWORD
678         };
679
680         const SHGetKnownFolderPathProc getKnownFolderPath = MUtils::Win32Utils::resolve<SHGetKnownFolderPathProc>(QLatin1String("shell32"), QLatin1String("SHGetKnownFolderPath"));
681         if (getKnownFolderPath)
682         {
683                 for (size_t i = 0; s_kfFlags[i] != MAXDWORD; ++i)
684                 {
685                         WCHAR* path = NULL;
686                         if (getKnownFolderPath(s_known_folders_lut[folderId].known_folder_id, s_kfFlags[i], NULL, &path) == S_OK)
687                         {
688                                 //MessageBoxW(0, path, L"SHGetKnownFolderPath", MB_TOPMOST);
689                                 const QString folderPath = known_folder_verify(path);
690                                 CoTaskMemFree(path);
691                                 if (!folderPath.isEmpty())
692                                 {
693                                         return folderPath;
694                                 }
695                         }
696                 }
697         }
698
699         return known_folder_fallback(folderId); /*fallback!*/
700 }
701
702 static size_t known_folder_decode(const MUtils::OS::known_folder_t folder_id)
703 {
704         for (size_t i = 0; s_known_folders_lut[i].id; ++i)
705         {
706                 if (s_known_folders_lut[i].id == folder_id)
707                 {
708                         return i;
709                 }
710         }
711         qWarning("Invalid 'known' folder was requested!");
712         return SIZE_MAX;
713 }
714
715 const QString &MUtils::OS::known_folder(const known_folder_t folder_id)
716 {
717         //Map to numeric id
718         const size_t folderId = known_folder_decode(folder_id);
719         if (folderId == SIZE_MAX)
720         {
721                 return Internal::g_empty;
722         }
723         
724         //Obtain read lock 
725         QReadLocker readLock(&g_known_folders_lock);
726
727         //Already in cache?
728         if(!g_known_folders_data.isNull())
729         {
730                 if(g_known_folders_data->contains(folderId))
731                 {
732                         return (*g_known_folders_data)[folderId];
733                 }
734         }
735
736         //Obtain write lock to initialize
737         readLock.unlock();
738         QWriteLocker writeLock(&g_known_folders_lock);
739
740         //Still not in cache?
741         if(!g_known_folders_data.isNull())
742         {
743                 if(g_known_folders_data->contains(folderId))
744                 {
745                         return (*g_known_folders_data)[folderId];
746                 }
747         }
748
749         //Initialize on first call
750         if(g_known_folders_data.isNull())
751         {
752                 g_known_folders_data.reset(new QHash<size_t, QString>());
753         }
754
755         //Detect path now!
756         const QString folderPath = known_folder_detect(folderId);
757
758         //Update cache
759         if (!folderPath.isEmpty())
760         {
761                 g_known_folders_data->insert(folderId, folderPath);
762                 return (*g_known_folders_data)[folderId];
763         }
764
765         return Internal::g_empty;
766 }
767
768 ///////////////////////////////////////////////////////////////////////////////
769 // CURRENT DATA & TIME
770 ///////////////////////////////////////////////////////////////////////////////
771
772 QDate MUtils::OS::current_date(void)
773 {
774         const DWORD MAX_PROC = 1024;
775         QScopedArrayPointer<DWORD> processes(new DWORD[MAX_PROC]);
776         DWORD bytesReturned = 0;
777         
778         if(!EnumProcesses(processes.data(), sizeof(DWORD) * MAX_PROC, &bytesReturned))
779         {
780                 return QDate::currentDate();
781         }
782
783         const DWORD procCount = bytesReturned / sizeof(DWORD);
784         ULARGE_INTEGER lastStartTime;
785         memset(&lastStartTime, 0, sizeof(ULARGE_INTEGER));
786
787         for(DWORD i = 0; i < procCount; i++)
788         {
789                 if(HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processes[i]))
790                 {
791                         FILETIME processTime[4];
792                         if(GetProcessTimes(hProc, &processTime[0], &processTime[1], &processTime[2], &processTime[3]))
793                         {
794                                 ULARGE_INTEGER timeCreation;
795                                 timeCreation.LowPart  = processTime[0].dwLowDateTime;
796                                 timeCreation.HighPart = processTime[0].dwHighDateTime;
797                                 if(timeCreation.QuadPart > lastStartTime.QuadPart)
798                                 {
799                                         lastStartTime.QuadPart = timeCreation.QuadPart;
800                                 }
801                         }
802                         CloseHandle(hProc);
803                 }
804         }
805         
806         FILETIME lastStartTime_fileTime;
807         lastStartTime_fileTime.dwHighDateTime = lastStartTime.HighPart;
808         lastStartTime_fileTime.dwLowDateTime  = lastStartTime.LowPart;
809
810         FILETIME lastStartTime_localTime;
811         if(!FileTimeToLocalFileTime(&lastStartTime_fileTime, &lastStartTime_localTime))
812         {
813                 memcpy(&lastStartTime_localTime, &lastStartTime_fileTime, sizeof(FILETIME));
814         }
815         
816         SYSTEMTIME lastStartTime_system;
817         if(!FileTimeToSystemTime(&lastStartTime_localTime, &lastStartTime_system))
818         {
819                 memset(&lastStartTime_system, 0, sizeof(SYSTEMTIME));
820                 lastStartTime_system.wYear = 1970; lastStartTime_system.wMonth = lastStartTime_system.wDay = 1;
821         }
822
823         const QDate currentDate = QDate::currentDate();
824         const QDate processDate = QDate(lastStartTime_system.wYear, lastStartTime_system.wMonth, lastStartTime_system.wDay);
825         return (currentDate >= processDate) ? currentDate : processDate;
826 }
827
828 quint64 MUtils::OS::current_file_time(void)
829 {
830         FILETIME fileTime;
831         GetSystemTimeAsFileTime(&fileTime);
832
833         ULARGE_INTEGER temp;
834         temp.HighPart = fileTime.dwHighDateTime;
835         temp.LowPart = fileTime.dwLowDateTime;
836
837         return temp.QuadPart;
838 }
839
840 ///////////////////////////////////////////////////////////////////////////////
841 // FILE PATH FROM FD
842 ///////////////////////////////////////////////////////////////////////////////
843
844 typedef DWORD(_stdcall *GetPathNameByHandleFun)(HANDLE hFile, LPWSTR lpszFilePath, DWORD cchFilePath, DWORD dwFlags);
845
846 static QString get_file_path_drive_list(void)
847 {
848         QString list;
849         const DWORD len = GetLogicalDriveStringsW(0, NULL);
850         if (len > 0)
851         {
852                 if (wchar_t *const buffer = (wchar_t*) _malloca(sizeof(wchar_t) * len))
853                 {
854                         const DWORD ret = GetLogicalDriveStringsW(len, buffer);
855                         if ((ret > 0) && (ret < len))
856                         {
857                                 const wchar_t *ptr = buffer;
858                                 while (const size_t current_len = wcslen(ptr))
859                                 {
860                                         list.append(QChar(*reinterpret_cast<const ushort*>(ptr)));
861                                         ptr += (current_len + 1);
862                                 }
863                         }
864                         _freea(buffer);
865                 }
866         }
867         return list;
868 }
869
870 static void get_file_path_translate(QString &path)
871 {
872         static const DWORD BUFSIZE = 2048;
873         wchar_t buffer[BUFSIZE], drive[3];
874
875         const QString driveList = get_file_path_drive_list();
876         wcscpy_s(drive, 3, L"?:");
877         for (const wchar_t *current = MUTILS_WCHR(driveList); *current; current++)
878         {
879                 drive[0] = (*current);
880                 if (QueryDosDeviceW(drive, buffer, MAX_PATH))
881                 {
882                         const QString prefix = MUTILS_QSTR(buffer);
883                         if (path.startsWith(prefix, Qt::CaseInsensitive))
884                         {
885                                 path.remove(0, prefix.length()).prepend(QLatin1Char(':')).prepend(QChar(*reinterpret_cast<const ushort*>(current)));
886                                 break;
887                         }
888                 }
889         }
890 }
891
892 static QString get_file_path_fallback(const HANDLE &hFile)
893 {
894         QString filePath;
895
896         const HANDLE hFileMap = CreateFileMappingW(hFile, NULL, PAGE_READONLY, 0, 1, NULL);
897         if (hFileMap)
898         {
899                 void *const pMem = MapViewOfFile(hFileMap, FILE_MAP_READ, 0, 0, 1);
900                 if (pMem)
901                 {
902                         static const size_t BUFFSIZE = 2048;
903                         wchar_t buffer[BUFFSIZE];
904                         if (GetMappedFileNameW(GetCurrentProcess(), pMem, buffer, BUFFSIZE) > 0)
905                         {
906                                 filePath = MUTILS_QSTR(buffer);
907                         }
908                         UnmapViewOfFile(pMem);
909                 }
910                 CloseHandle(hFileMap);
911         }
912
913         if (!filePath.isEmpty())
914         {
915                 get_file_path_translate(filePath);
916         }
917
918         return filePath;
919 }
920
921 QString MUtils::OS::get_file_path(const int &fd)
922 {
923         if (fd >= 0)
924         {
925                 const GetPathNameByHandleFun getPathNameByHandleFun = MUtils::Win32Utils::resolve<GetPathNameByHandleFun>(QLatin1String("kernel32"), QLatin1String("GetFinalPathNameByHandleW"));
926                 if (!getPathNameByHandleFun)
927                 {
928                         return get_file_path_fallback((HANDLE)_get_osfhandle(fd));
929                 }
930
931                 const HANDLE handle = (HANDLE) _get_osfhandle(fd);
932                 const DWORD len = getPathNameByHandleFun(handle, NULL, 0, FILE_NAME_OPENED);
933                 if (len > 0)
934                 {
935                         if (wchar_t *const buffer = (wchar_t*)_malloca(sizeof(wchar_t) * len))
936                         {
937                                 const DWORD ret = getPathNameByHandleFun(handle, buffer, len, FILE_NAME_OPENED);
938                                 if ((ret > 0) && (ret < len))
939                                 {
940                                         const QString path(MUTILS_QSTR(buffer));
941                                         return path.startsWith(QLatin1String("\\\\?\\")) ? path.mid(4) : path;
942                                 }
943                                 _freea(buffer);
944                         }
945                 }
946         }
947
948         return QString();
949 }
950
951 ///////////////////////////////////////////////////////////////////////////////
952 // PROCESS ELEVATION
953 ///////////////////////////////////////////////////////////////////////////////
954
955 static bool user_is_admin_helper(void)
956 {
957         HANDLE hToken = NULL;
958         if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
959         {
960                 return false;
961         }
962
963         DWORD dwSize = 0;
964         if(!GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwSize))
965         {
966                 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
967                 {
968                         CloseHandle(hToken);
969                         return false;
970                 }
971         }
972
973         PTOKEN_GROUPS lpGroups = (PTOKEN_GROUPS) malloc(dwSize);
974         if(!lpGroups)
975         {
976                 CloseHandle(hToken);
977                 return false;
978         }
979
980         if(!GetTokenInformation(hToken, TokenGroups, lpGroups, dwSize, &dwSize))
981         {
982                 free(lpGroups);
983                 CloseHandle(hToken);
984                 return false;
985         }
986
987         PSID lpSid = NULL; SID_IDENTIFIER_AUTHORITY Authority = {SECURITY_NT_AUTHORITY};
988         if(!AllocateAndInitializeSid(&Authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &lpSid))
989         {
990                 free(lpGroups);
991                 CloseHandle(hToken);
992                 return false;
993         }
994
995         bool bResult = false;
996         for(DWORD i = 0; i < lpGroups->GroupCount; i++)
997         {
998                 if(EqualSid(lpSid, lpGroups->Groups[i].Sid))
999                 {
1000                         bResult = true;
1001                         break;
1002                 }
1003         }
1004
1005         FreeSid(lpSid);
1006         free(lpGroups);
1007         CloseHandle(hToken);
1008         return bResult;
1009 }
1010
1011 bool MUtils::OS::is_elevated(bool *bIsUacEnabled)
1012 {
1013         if(bIsUacEnabled)
1014         {
1015                 *bIsUacEnabled = false;
1016         }
1017
1018         bool bIsProcessElevated = false;
1019         HANDLE hToken = NULL;
1020         
1021         if(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
1022         {
1023                 TOKEN_ELEVATION_TYPE tokenElevationType;
1024                 DWORD returnLength;
1025                 if(GetTokenInformation(hToken, TokenElevationType, &tokenElevationType, sizeof(TOKEN_ELEVATION_TYPE), &returnLength))
1026                 {
1027                         if(returnLength == sizeof(TOKEN_ELEVATION_TYPE))
1028                         {
1029                                 switch(tokenElevationType)
1030                                 {
1031                                 case TokenElevationTypeDefault:
1032                                         qDebug("Process token elevation type: Default -> UAC is disabled.\n");
1033                                         break;
1034                                 case TokenElevationTypeFull:
1035                                         qWarning("Process token elevation type: Full -> potential security risk!\n");
1036                                         bIsProcessElevated = true;
1037                                         if(bIsUacEnabled) *bIsUacEnabled = true;
1038                                         break;
1039                                 case TokenElevationTypeLimited:
1040                                         qDebug("Process token elevation type: Limited -> not elevated.\n");
1041                                         if(bIsUacEnabled) *bIsUacEnabled = true;
1042                                         break;
1043                                 default:
1044                                         qWarning("Unknown tokenElevationType value: %d", tokenElevationType);
1045                                         break;
1046                                 }
1047                         }
1048                         else
1049                         {
1050                                 qWarning("GetTokenInformation() return an unexpected size!");
1051                         }
1052                 }
1053                 CloseHandle(hToken);
1054         }
1055         else
1056         {
1057                 qWarning("Failed to open process token!");
1058         }
1059
1060         return bIsProcessElevated;
1061 }
1062
1063 bool MUtils::OS::user_is_admin(void)
1064 {
1065         bool isAdmin = false;
1066
1067         //Check for process elevation and UAC support first!
1068         if(MUtils::OS::is_elevated(&isAdmin))
1069         {
1070                 qWarning("Process is elevated -> user is admin!");
1071                 return true;
1072         }
1073         
1074         //If not elevated and UAC is not available -> user must be in admin group!
1075         if(!isAdmin)
1076         {
1077                 qDebug("UAC is disabled/unavailable -> checking for Administrators group");
1078                 isAdmin = user_is_admin_helper();
1079         }
1080
1081         return isAdmin;
1082 }
1083
1084 ///////////////////////////////////////////////////////////////////////////////
1085 // NETWORK STATE
1086 ///////////////////////////////////////////////////////////////////////////////
1087
1088 int MUtils::OS::network_status(void)
1089 {
1090         DWORD dwFlags;
1091         const BOOL ret = IsNetworkAlive(&dwFlags);
1092         if(GetLastError() == 0)
1093         {
1094                 return (ret != FALSE) ? NETWORK_TYPE_YES : NETWORK_TYPE_NON;
1095         }
1096         return NETWORK_TYPE_ERR;
1097 }
1098
1099 ///////////////////////////////////////////////////////////////////////////////
1100 // MESSAGE HANDLER
1101 ///////////////////////////////////////////////////////////////////////////////
1102
1103 bool MUtils::OS::handle_os_message(const void *const message, long *result)
1104 {
1105         const MSG *const msg = reinterpret_cast<const MSG*>(message);
1106
1107         switch(msg->message)
1108         {
1109         case WM_QUERYENDSESSION:
1110                 qWarning("WM_QUERYENDSESSION message received!");
1111                 *result = MUtils::GUI::broadcast(MUtils::GUI::USER_EVENT_QUERYENDSESSION, false) ? TRUE : FALSE;
1112                 return true;
1113         case WM_ENDSESSION:
1114                 qWarning("WM_ENDSESSION message received!");
1115                 if(msg->wParam == TRUE)
1116                 {
1117                         MUtils::GUI::broadcast(MUtils::GUI::USER_EVENT_ENDSESSION, false);
1118                         MUtils::GUI::force_quit();
1119                         exit(1);
1120                 }
1121                 *result = 0;
1122                 return true;
1123         default:
1124                 /*ignore this message and let Qt handle it*/
1125                 return false;
1126         }
1127 }
1128
1129 ///////////////////////////////////////////////////////////////////////////////
1130 // SLEEP
1131 ///////////////////////////////////////////////////////////////////////////////
1132
1133 void MUtils::OS::sleep_ms(const size_t &duration)
1134 {
1135         Sleep((DWORD) duration);
1136 }
1137
1138 ///////////////////////////////////////////////////////////////////////////////
1139 // EXECUTABLE CHECK
1140 ///////////////////////////////////////////////////////////////////////////////
1141
1142 static int g_library_as_image_resource_supported = -1;
1143 static QReadWriteLock g_library_as_image_resource_supported_lock;
1144
1145 static bool library_as_image_resource_supported()
1146 {
1147         QReadLocker readLocker(&g_library_as_image_resource_supported_lock);
1148         if (g_library_as_image_resource_supported >= 0)
1149         {
1150                 return (g_library_as_image_resource_supported > 0);
1151         }
1152
1153         readLocker.unlock();
1154         QWriteLocker writeLocker(&g_library_as_image_resource_supported_lock);
1155
1156         if (g_library_as_image_resource_supported < 0)
1157         {
1158                 g_library_as_image_resource_supported = 0;
1159                 OSVERSIONINFOEXW osvi;
1160                 if (rtl_get_version(&osvi))
1161                 {
1162                         if ((osvi.dwPlatformId == VER_PLATFORM_WIN32_NT) && (osvi.dwMajorVersion >= 6U))
1163                         {
1164                                 g_library_as_image_resource_supported = 1;
1165                         }
1166                 }
1167         }
1168
1169         return (g_library_as_image_resource_supported > 0);
1170 }
1171
1172 bool MUtils::OS::is_executable_file(const QString &path)
1173 {
1174         DWORD binaryType;
1175         if(GetBinaryType(MUTILS_WCHR(QDir::toNativeSeparators(path)), &binaryType))
1176         {
1177                 return ((binaryType == SCS_32BIT_BINARY) || (binaryType == SCS_64BIT_BINARY));
1178         }
1179
1180         const DWORD errorCode = GetLastError();
1181         qWarning("GetBinaryType() failed with error: 0x%08X", errorCode);
1182         return false;
1183 }
1184
1185 bool MUtils::OS::is_library_file(const QString &path)
1186 {
1187         const DWORD flags = library_as_image_resource_supported() ? (LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE) : (LOAD_LIBRARY_AS_DATAFILE | DONT_RESOLVE_DLL_REFERENCES);
1188         if (const HMODULE hMod = LoadLibraryEx(MUTILS_WCHR(QDir::toNativeSeparators(path)), NULL, flags))
1189         {
1190                 FreeLibrary(hMod);
1191                 return true;
1192         }
1193
1194         const DWORD errorCode = GetLastError();
1195         qWarning("LoadLibraryEx() failed with error: 0x%08X", errorCode);
1196         return false;
1197 }
1198
1199 ///////////////////////////////////////////////////////////////////////////////
1200 // HIBERNATION / SHUTDOWN
1201 ///////////////////////////////////////////////////////////////////////////////
1202
1203 bool MUtils::OS::is_hibernation_supported(void)
1204 {
1205         bool hibernationSupported = false;
1206
1207         SYSTEM_POWER_CAPABILITIES pwrCaps;
1208         SecureZeroMemory(&pwrCaps, sizeof(SYSTEM_POWER_CAPABILITIES));
1209         
1210         if(GetPwrCapabilities(&pwrCaps))
1211         {
1212                 hibernationSupported = pwrCaps.SystemS4 && pwrCaps.HiberFilePresent;
1213         }
1214
1215         return hibernationSupported;
1216 }
1217
1218 bool MUtils::OS::shutdown_computer(const QString &message, const unsigned long timeout, const bool forceShutdown, const bool hibernate)
1219 {
1220         HANDLE hToken = NULL;
1221
1222         if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
1223         {
1224                 TOKEN_PRIVILEGES privileges;
1225                 memset(&privileges, 0, sizeof(TOKEN_PRIVILEGES));
1226                 privileges.PrivilegeCount = 1;
1227                 privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
1228                 
1229                 if(LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &privileges.Privileges[0].Luid))
1230                 {
1231                         if(AdjustTokenPrivileges(hToken, FALSE, &privileges, NULL, NULL, NULL))
1232                         {
1233                                 if(hibernate)
1234                                 {
1235                                         if(SetSuspendState(TRUE, TRUE, TRUE))
1236                                         {
1237                                                 return true;
1238                                         }
1239                                 }
1240                                 const DWORD reason = SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_FLAG_PLANNED;
1241                                 return InitiateSystemShutdownEx(NULL, const_cast<wchar_t*>(MUTILS_WCHR(message)), timeout, forceShutdown ? TRUE : FALSE, FALSE, reason);
1242                         }
1243                 }
1244         }
1245         
1246         return false;
1247 }
1248
1249 ///////////////////////////////////////////////////////////////////////////////
1250 // FREE DISKSPACE
1251 ///////////////////////////////////////////////////////////////////////////////
1252
1253 bool MUtils::OS::free_diskspace(const QString &path, quint64 &freeSpace)
1254 {
1255         ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes;
1256         if(GetDiskFreeSpaceExW(reinterpret_cast<const wchar_t*>(QDir::toNativeSeparators(path).utf16()), &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes))
1257         {
1258                 freeSpace = freeBytesAvailable.QuadPart;
1259                 return true;;
1260         }
1261
1262         freeSpace = static_cast<quint64>(-1);
1263         return false;
1264 }
1265
1266 ///////////////////////////////////////////////////////////////////////////////
1267 // DRIVE TYPE
1268 ///////////////////////////////////////////////////////////////////////////////
1269
1270 static wchar_t get_drive_letter(const QString &path)
1271 {
1272         QString nativePath = QDir::toNativeSeparators(path);
1273         while (nativePath.startsWith("\\\\?\\") || nativePath.startsWith("\\\\.\\"))
1274         {
1275                 nativePath = QDir::toNativeSeparators(nativePath.mid(4));
1276         }
1277         if ((path.length() > 1) && (path[1] == QLatin1Char(':')))
1278         {
1279                 const wchar_t letter = static_cast<wchar_t>(path[0].unicode());
1280                 if (((letter >= 'A') && (letter <= 'Z')) || ((letter >= 'a') && (letter <= 'z')))
1281                 {
1282                         return towupper(letter);
1283                 }
1284         }
1285         return L'\0'; /*invalid path spec*/
1286 }
1287
1288 static QSet<DWORD> get_physical_drive_ids(const wchar_t drive_letter)
1289 {
1290         QSet<DWORD> physical_drives;
1291         wchar_t driveName[8];
1292         _snwprintf_s(driveName, 8, _TRUNCATE, L"\\\\.\\%c:", drive_letter);
1293         const HANDLE hDrive = CreateFileW(driveName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1294         if (hDrive && (hDrive != INVALID_HANDLE_VALUE))
1295         {
1296                 const size_t BUFF_SIZE = sizeof(VOLUME_DISK_EXTENTS) + (32U * sizeof(DISK_EXTENT));
1297                 VOLUME_DISK_EXTENTS *const diskExtents = (VOLUME_DISK_EXTENTS*)_malloca(BUFF_SIZE);
1298                 DWORD dwSize;
1299                 if (DeviceIoControl(hDrive, IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS, NULL, 0, (LPVOID)diskExtents, (DWORD)BUFF_SIZE, (LPDWORD)&dwSize, NULL))
1300                 {
1301                         for (DWORD index = 0U; index < diskExtents->NumberOfDiskExtents; ++index)
1302                         {
1303                                 physical_drives.insert(diskExtents->Extents[index].DiskNumber);
1304                         }
1305                 }
1306                 _freea(diskExtents);
1307                 CloseHandle(hDrive);
1308         }
1309         return physical_drives;
1310 }
1311
1312 static bool incurs_seek_penalty(const DWORD device_id)
1313 {
1314         wchar_t driveName[24];
1315         _snwprintf_s(driveName, 24, _TRUNCATE, L"\\\\?\\PhysicalDrive%u", device_id);
1316         const HANDLE hDevice = CreateFileW(driveName, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL);
1317         bool seeking_penalty = true;
1318         if (hDevice && (hDevice != INVALID_HANDLE_VALUE))
1319         {
1320                 STORAGE_PROPERTY_QUERY spq;
1321                 DEVICE_SEEK_PENALTY_DESCRIPTOR dspd;
1322                 memset(&spq, 0, sizeof(STORAGE_PROPERTY_QUERY));
1323                 spq.PropertyId = (STORAGE_PROPERTY_ID)StorageDeviceSeekPenaltyProperty;
1324                 spq.QueryType = PropertyStandardQuery;
1325                 DWORD dwSize;
1326                 if (DeviceIoControl(hDevice, IOCTL_STORAGE_QUERY_PROPERTY, (LPVOID)&spq, (DWORD)sizeof(spq), (LPVOID)&dspd, (DWORD)sizeof(dspd), (LPDWORD)&dwSize, NULL))
1327                 {
1328                         seeking_penalty = dspd.IncursSeekPenalty;
1329                 }
1330                 CloseHandle(hDevice);
1331         }
1332         return seeking_penalty;
1333 }
1334
1335 static bool is_fast_seeking_drive(const wchar_t drive_letter)
1336 {
1337         bool fast_seeking = false;
1338         const QSet<DWORD> physical_drive_ids = get_physical_drive_ids(drive_letter);
1339         if (!physical_drive_ids.empty())
1340         {
1341                 fast_seeking = true;
1342                 for (QSet<DWORD>::const_iterator iter = physical_drive_ids.constBegin(); iter != physical_drive_ids.constEnd(); ++iter)
1343                 {
1344                         fast_seeking = fast_seeking && (!incurs_seek_penalty(*iter));
1345                 }
1346         }
1347         return fast_seeking;
1348 }
1349
1350 MUtils::OS::drive_type_t MUtils::OS::get_drive_type(const QString &path, bool *fast_seeking)
1351 {
1352         drive_type_t driveType = DRIVE_TYPE_ERR;
1353         const wchar_t driveLetter = get_drive_letter(path);
1354         if (driveLetter)
1355         {
1356                 wchar_t driveName[8];
1357                 _snwprintf_s(driveName, 8, _TRUNCATE, L"\\\\.\\%c:\\", driveLetter);
1358                 switch (GetDriveTypeW(driveName))
1359                 {
1360                         case DRIVE_REMOVABLE: driveType = DRIVE_TYPE_FDD; break;
1361                         case DRIVE_FIXED:     driveType = DRIVE_TYPE_HDD; break;
1362                         case DRIVE_REMOTE:    driveType = DRIVE_TYPE_NET; break;
1363                         case DRIVE_CDROM:     driveType = DRIVE_TYPE_OPT; break;
1364                         case DRIVE_RAMDISK:   driveType = DRIVE_TYPE_RAM; break;
1365                 }
1366         }
1367         if (fast_seeking)
1368         {
1369                 if (driveType == DRIVE_TYPE_HDD)
1370                 {
1371                         *fast_seeking = is_fast_seeking_drive(driveLetter);
1372                 }
1373                 else
1374                 {
1375                         *fast_seeking = (driveType == DRIVE_TYPE_RAM);
1376                 }
1377         }
1378         return driveType;
1379 }
1380
1381 ///////////////////////////////////////////////////////////////////////////////
1382 // SHELL OPEN
1383 ///////////////////////////////////////////////////////////////////////////////
1384
1385 bool MUtils::OS::shell_open(const QWidget *parent, const QString &url, const QString &parameters, const QString &directory, const bool explore)
1386 {
1387         return ((int) ShellExecuteW((parent ? reinterpret_cast<HWND>(parent->winId()) : NULL), (explore ? L"explore" : L"open"), MUTILS_WCHR(url), ((!parameters.isEmpty()) ? MUTILS_WCHR(parameters) : NULL), ((!directory.isEmpty()) ? MUTILS_WCHR(directory) : NULL), SW_SHOW)) > 32;
1388 }
1389
1390 bool MUtils::OS::shell_open(const QWidget *parent, const QString &url, const bool explore)
1391 {
1392         return shell_open(parent, url, QString(), QString(), explore);
1393 }
1394
1395 ///////////////////////////////////////////////////////////////////////////////
1396 // OPEN MEDIA FILE
1397 ///////////////////////////////////////////////////////////////////////////////
1398
1399 bool MUtils::OS::open_media_file(const QString &mediaFilePath)
1400 {
1401         const static wchar_t *registryPrefix[2] = { L"SOFTWARE\\", L"SOFTWARE\\Wow6432Node\\" };
1402         const static wchar_t *registryKeys[3] = 
1403         {
1404                 L"Microsoft\\Windows\\CurrentVersion\\Uninstall\\{97D341C8-B0D1-4E4A-A49A-C30B52F168E9}",
1405                 L"Microsoft\\Windows\\CurrentVersion\\Uninstall\\{DB9E4EAB-2717-499F-8D56-4CC8A644AB60}",
1406                 L"foobar2000"
1407         };
1408         const static wchar_t *appNames[4] = { L"smplayer_portable.exe", L"smplayer.exe", L"MPUI.exe", L"foobar2000.exe" };
1409         const static wchar_t *valueNames[2] = { L"InstallLocation", L"InstallDir" };
1410
1411         for(size_t i = 0; i < 3; i++)
1412         {
1413                 for(size_t j = 0; j < 2; j++)
1414                 {
1415                         QString mplayerPath;
1416                         HKEY registryKeyHandle = NULL;
1417
1418                         const QString currentKey = MUTILS_QSTR(registryPrefix[j]).append(MUTILS_QSTR(registryKeys[i]));
1419                         if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, MUTILS_WCHR(currentKey), 0, KEY_READ, &registryKeyHandle) == ERROR_SUCCESS)
1420                         {
1421                                 for(size_t k = 0; k < 2; k++)
1422                                 {
1423                                         wchar_t Buffer[4096];
1424                                         DWORD BuffSize = sizeof(wchar_t*) * 4096;
1425                                         DWORD DataType = REG_NONE;
1426                                         if(RegQueryValueExW(registryKeyHandle, valueNames[k], 0, &DataType, reinterpret_cast<BYTE*>(Buffer), &BuffSize) == ERROR_SUCCESS)
1427                                         {
1428                                                 if((DataType == REG_SZ) || (DataType == REG_EXPAND_SZ) || (DataType == REG_LINK))
1429                                                 {
1430                                                         mplayerPath = MUTILS_QSTR(Buffer);
1431                                                         break;
1432                                                 }
1433                                         }
1434                                 }
1435                                 RegCloseKey(registryKeyHandle);
1436                         }
1437
1438                         if(!mplayerPath.isEmpty())
1439                         {
1440                                 QDir mplayerDir(mplayerPath);
1441                                 if(mplayerDir.exists())
1442                                 {
1443                                         for(size_t k = 0; k < 4; k++)
1444                                         {
1445                                                 if(mplayerDir.exists(MUTILS_QSTR(appNames[k])))
1446                                                 {
1447                                                         qDebug("Player found at:\n%s\n", MUTILS_UTF8(mplayerDir.absoluteFilePath(MUTILS_QSTR(appNames[k]))));
1448                                                         QProcess::startDetached(mplayerDir.absoluteFilePath(MUTILS_QSTR(appNames[k])), QStringList() << QDir::toNativeSeparators(mediaFilePath));
1449                                                         return true;
1450                                                 }
1451                                         }
1452                                 }
1453                         }
1454                 }
1455         }
1456         return false;
1457 }
1458
1459 ///////////////////////////////////////////////////////////////////////////////
1460 // DEBUGGER CHECK
1461 ///////////////////////////////////////////////////////////////////////////////
1462
1463 static bool change_process_priority_helper(const HANDLE hProcess, const int priority)
1464 {
1465         bool ok = false;
1466
1467         switch(qBound(-2, priority, 2))
1468         {
1469         case 2:
1470                 ok = (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS) == TRUE);
1471                 break;
1472         case 1:
1473                 if(!(ok = (SetPriorityClass(hProcess, ABOVE_NORMAL_PRIORITY_CLASS) == TRUE)))
1474                 {
1475                         ok = (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS) == TRUE);
1476                 }
1477                 break;
1478         case 0:
1479                 ok = (SetPriorityClass(hProcess, NORMAL_PRIORITY_CLASS) == TRUE);
1480                 break;
1481         case -1:
1482                 if(!(ok = (SetPriorityClass(hProcess, BELOW_NORMAL_PRIORITY_CLASS) == TRUE)))
1483                 {
1484                         ok = (SetPriorityClass(hProcess, IDLE_PRIORITY_CLASS) == TRUE);
1485                 }
1486                 break;
1487         case -2:
1488                 ok = (SetPriorityClass(hProcess, IDLE_PRIORITY_CLASS) == TRUE);
1489                 break;
1490         }
1491
1492         return ok;
1493 }
1494
1495 bool MUtils::OS::change_process_priority(const int priority)
1496 {
1497         return change_process_priority_helper(GetCurrentProcess(), priority);
1498 }
1499
1500 bool MUtils::OS::change_process_priority(const QProcess *proc, const int priority)
1501 {
1502         if(Q_PID qPid = proc->pid())
1503         {
1504                 return change_process_priority_helper(qPid->hProcess, priority);
1505         }
1506         else
1507         {
1508                 return false;
1509         }
1510 }
1511
1512 ///////////////////////////////////////////////////////////////////////////////
1513 // PROCESS ID
1514 ///////////////////////////////////////////////////////////////////////////////
1515
1516 quint32 MUtils::OS::process_id(void)
1517 {
1518         return GetCurrentProcessId();
1519 }
1520
1521 quint32 MUtils::OS::process_id(const QProcess *const proc)
1522 {
1523         PROCESS_INFORMATION *procInf = proc->pid();
1524         return (procInf) ? procInf->dwProcessId : 0;
1525 }
1526
1527 quint32 MUtils::OS::thread_id(void)
1528 {
1529         return GetCurrentThreadId();
1530 }
1531
1532 quint32 MUtils::OS::thread_id(const QProcess *const proc)
1533 {
1534         PROCESS_INFORMATION *procInf = proc->pid();
1535         return (procInf) ? procInf->dwThreadId : 0;
1536 }
1537
1538 ///////////////////////////////////////////////////////////////////////////////
1539 // PROCESS SUSPEND/RESUME
1540 ///////////////////////////////////////////////////////////////////////////////
1541
1542 bool MUtils::OS::suspend_process(const QProcess *proc, const bool suspend)
1543 {
1544         if(Q_PID pid = proc->pid())
1545         {
1546                 if(suspend)
1547                 {
1548                         return (SuspendThread(pid->hThread) != ((DWORD) -1));
1549                 }
1550                 else
1551                 {
1552                         return (ResumeThread(pid->hThread)  != ((DWORD) -1));
1553                 }
1554         }
1555         else
1556         {
1557                 return false;
1558         }
1559 }
1560
1561 ///////////////////////////////////////////////////////////////////////////////
1562 // SYSTEM TIMER
1563 ///////////////////////////////////////////////////////////////////////////////
1564
1565 bool MUtils::OS::setup_timer_resolution(const quint32 &interval)
1566 {
1567         return timeBeginPeriod(interval) == TIMERR_NOERROR;
1568 }
1569
1570 bool MUtils::OS::reset_timer_resolution(const quint32 &interval)
1571 {
1572         return timeEndPeriod(interval) == TIMERR_NOERROR;
1573 }
1574
1575 ///////////////////////////////////////////////////////////////////////////////
1576 // SET FILE TIME
1577 ///////////////////////////////////////////////////////////////////////////////
1578
1579 static QScopedPointer<QDateTime> s_epoch;
1580 static QReadWriteLock            s_epochLock;
1581
1582 static const QDateTime *get_epoch(void)
1583 {
1584         QReadLocker rdLock(&s_epochLock);
1585
1586         if (s_epoch.isNull())
1587         {
1588                 rdLock.unlock();
1589                 QWriteLocker wrLock(&s_epochLock);
1590                 if (s_epoch.isNull())
1591                 {
1592                         s_epoch.reset(new QDateTime(QDate(1601, 1, 1), QTime(0, 0, 0, 0), Qt::UTC));
1593                 }
1594                 wrLock.unlock();
1595                 rdLock.relock();
1596         }
1597
1598         return s_epoch.data();
1599 }
1600
1601 static FILETIME *qt_time_to_file_time(FILETIME *const fileTime, const QDateTime &dateTime)
1602 {
1603         memset(fileTime, 0, sizeof(FILETIME));
1604
1605         if (const QDateTime *const epoch = get_epoch())
1606         {
1607                 const qint64 msecs = epoch->msecsTo(dateTime);
1608                 if (msecs > 0)
1609                 {
1610                         const quint64 ticks = 10000U * quint64(msecs);
1611                         fileTime->dwHighDateTime = ((ticks >> 32) & 0xFFFFFFFF);
1612                         fileTime->dwLowDateTime = (ticks & 0xFFFFFFFF);
1613                         return fileTime;
1614                 }
1615         }
1616
1617         return NULL;
1618 }
1619
1620 static bool set_file_time(const HANDLE hFile, const QDateTime &created, const QDateTime &lastMod, const QDateTime &lastAcc)
1621 {
1622         FILETIME ftCreated, ftLastMod, ftLastAcc;
1623
1624         FILETIME *const pCreated = created.isValid() ? qt_time_to_file_time(&ftCreated, created) : NULL;
1625         FILETIME *const pLastMod = lastMod.isValid() ? qt_time_to_file_time(&ftLastMod, lastMod) : NULL;
1626         FILETIME *const pLastAcc = lastAcc.isValid() ? qt_time_to_file_time(&ftLastAcc, lastAcc) : NULL;
1627
1628         if (pCreated || pLastMod || pLastAcc)
1629         {
1630                 return (SetFileTime(hFile, pCreated, pLastAcc, pLastMod) != FALSE);
1631         }
1632
1633         return false;
1634 }
1635
1636 bool MUtils::OS::set_file_time(const QFile &file, const QDateTime &created, const QDateTime &lastMod, const QDateTime &lastAcc)
1637 {
1638         const int fd = file.handle();
1639         if (fd >= 0)
1640         {
1641                 return set_file_time((HANDLE)_get_osfhandle(fd), created, lastMod, lastAcc);
1642         }
1643         return false;
1644 }
1645
1646 bool MUtils::OS::set_file_time(const QString &path, const QDateTime &created, const QDateTime &lastMod, const QDateTime &lastAcc)
1647 {
1648         const HANDLE hFile = CreateFileW(MUTILS_WCHR(path), FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0);
1649         bool okay = false;
1650         if ((hFile != NULL) && (hFile != INVALID_HANDLE_VALUE))
1651         {
1652                 okay = set_file_time(hFile, created, lastMod, lastAcc);
1653                 CloseHandle(hFile);
1654         }
1655         return okay;
1656 }
1657
1658 ///////////////////////////////////////////////////////////////////////////////
1659 // CHECK KEY STATE
1660 ///////////////////////////////////////////////////////////////////////////////
1661
1662 bool MUtils::OS::check_key_state_esc(void)
1663 {
1664         return (GetAsyncKeyState(VK_ESCAPE) & 0x0001) != 0;
1665 }
1666
1667 ///////////////////////////////////////////////////////////////////////////////
1668 // SHELL CHANGE NOTIFICATION
1669 ///////////////////////////////////////////////////////////////////////////////
1670
1671 void MUtils::OS::shell_change_notification(void)
1672 {
1673         SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
1674 }
1675
1676 ///////////////////////////////////////////////////////////////////////////////
1677 // WOW64 REDIRECTION
1678 ///////////////////////////////////////////////////////////////////////////////
1679
1680 typedef BOOL (_stdcall *Wow64DisableWow64FsRedirectionFun)(PVOID *OldValue);
1681 typedef BOOL (_stdcall *Wow64RevertWow64FsRedirectionFun) (PVOID  OldValue);
1682
1683 bool MUtils::OS::wow64fsredir_disable(uintptr_t &oldValue)
1684 {
1685         oldValue = reinterpret_cast<uintptr_t>(nullptr);
1686         const Wow64DisableWow64FsRedirectionFun wow64redir_disable = MUtils::Win32Utils::resolve<Wow64DisableWow64FsRedirectionFun>(QLatin1String("kernel32"), QLatin1String("Wow64DisableWow64FsRedirection"));
1687         if(wow64redir_disable)
1688         {
1689                 PVOID temp = NULL;
1690                 if (wow64redir_disable(&temp))
1691                 {
1692                         oldValue = reinterpret_cast<uintptr_t>(temp);
1693                         return true;
1694                 }
1695         }
1696         return false;
1697 }
1698
1699 bool MUtils::OS::wow64fsredir_revert(const uintptr_t oldValue)
1700 {
1701         const Wow64RevertWow64FsRedirectionFun wow64redir_disable = MUtils::Win32Utils::resolve<Wow64RevertWow64FsRedirectionFun>(QLatin1String("kernel32"), QLatin1String("Wow64RevertWow64FsRedirection"));
1702         if (wow64redir_disable)
1703         {
1704                 if (wow64redir_disable(reinterpret_cast<PVOID>(oldValue)))
1705                 {
1706                         return true;
1707                 }
1708         }
1709         return false;
1710 }
1711
1712 ///////////////////////////////////////////////////////////////////////////////
1713 // DEBUGGER CHECK
1714 ///////////////////////////////////////////////////////////////////////////////
1715
1716 QString MUtils::OS::get_envvar(const QString &name)
1717 {
1718         wchar_t *buffer = NULL;
1719         size_t requiredSize = 0, buffSize = 0;
1720         QString result;
1721
1722         forever
1723         {
1724                 //Adjust the buffer size as required first!
1725                 if (buffSize < requiredSize)
1726                 {
1727                         if (buffer)
1728                         {
1729                                 _freea(buffer);
1730                         }
1731                         if (!(buffer = (wchar_t*)_malloca(sizeof(wchar_t) * requiredSize)))
1732                         {
1733                                 break; /*out of memory error!*/
1734                         }
1735                         buffSize = requiredSize;
1736                 }
1737
1738                 //Try to fetch the environment variable now
1739                 const errno_t error = _wgetenv_s(&requiredSize, buffer, buffSize, MUTILS_WCHR(name));
1740                 if(!error)
1741                 {
1742                         if (requiredSize > 0)
1743                         {
1744                                 result = MUTILS_QSTR(buffer);
1745                         }
1746                         break; /*done*/
1747                 }
1748                 else if (error != ERANGE)
1749                 {
1750                         break; /*somethging else went wrong!*/
1751                 }
1752         }
1753         
1754         if (buffer)
1755         {
1756                 _freea(buffer);
1757                 buffSize = 0;
1758                 buffer = NULL;
1759         }
1760
1761         return result;
1762 }
1763
1764 bool MUtils::OS::set_envvar(const QString &name, const QString &value)
1765 {
1766         if (!_wputenv_s(MUTILS_WCHR(name), MUTILS_WCHR(value)))
1767         {
1768                 return true;
1769         }
1770         return false;
1771 }
1772
1773 ///////////////////////////////////////////////////////////////////////////////
1774 // NULL DEVICE
1775 ///////////////////////////////////////////////////////////////////////////////
1776
1777 static const QLatin1String NULL_DEVICE("NUL");
1778
1779 const QLatin1String &MUtils::OS::null_device(void)
1780 {
1781         return NULL_DEVICE;
1782 }
1783
1784 ///////////////////////////////////////////////////////////////////////////////
1785 // DEBUGGER CHECK
1786 ///////////////////////////////////////////////////////////////////////////////
1787
1788 #if (!(MUTILS_DEBUG))
1789 static __forceinline bool is_debugger_present(void)
1790 {
1791         __try
1792         {
1793                 CloseHandle((HANDLE)((DWORD_PTR)-3));
1794         }
1795         __except(1)
1796         {
1797                 return true;
1798         }
1799
1800         BOOL bHaveDebugger = FALSE;
1801         if(CheckRemoteDebuggerPresent(GetCurrentProcess(), &bHaveDebugger))
1802         {
1803                 if(bHaveDebugger) return true;
1804         }
1805
1806         return IsDebuggerPresent();
1807 }
1808 static __forceinline bool check_debugger_helper(void)
1809 {
1810         if(is_debugger_present())
1811         {
1812                 MUtils::OS::fatal_exit(L"Not a debug build. Please unload debugger and try again!");
1813                 return true;
1814         }
1815         return false;
1816 }
1817 #else
1818 #define check_debugger_helper() (false)
1819 #endif
1820
1821 void MUtils::OS::check_debugger(void)
1822 {
1823         check_debugger_helper();
1824 }
1825
1826 static volatile bool g_debug_check = check_debugger_helper();
1827
1828 ///////////////////////////////////////////////////////////////////////////////
1829 // FATAL EXIT
1830 ///////////////////////////////////////////////////////////////////////////////
1831
1832 static MUtils::Internal::CriticalSection g_fatal_exit_lock;
1833 static QAtomicInt g_fatal_exit_flag;
1834
1835 static BOOL CALLBACK fatal_exit_enum_helper(const HWND hwnd, const LPARAM /*lParam*/)
1836 {
1837         SetWindowPos(hwnd, HWND_NOTOPMOST, NULL, NULL, NULL, NULL, SWP_ASYNCWINDOWPOS | SWP_NOMOVE | SWP_NOSIZE);
1838         return TRUE;
1839 }
1840
1841 void MUtils::OS::fatal_exit(const wchar_t* const errorMessage)
1842 {
1843         g_fatal_exit_lock.enter();
1844         
1845         if(!g_fatal_exit_flag.testAndSetOrdered(0, 1))
1846         {
1847                 return; /*prevent recursive invocation*/
1848         }
1849
1850         EnumThreadWindows(g_main_thread_id, fatal_exit_enum_helper, 0L);
1851
1852         if(g_main_thread_id != GetCurrentThreadId())
1853         {
1854                 if(HANDLE hThreadMain = OpenThread(THREAD_SUSPEND_RESUME, FALSE, g_main_thread_id))
1855                 {
1856                         SuspendThread(hThreadMain); /*stop main thread*/
1857                 }
1858         }
1859
1860         MUtils::OS::system_message_err(L"GURU MEDITATION", errorMessage);
1861
1862         for(;;)
1863         {
1864                 TerminateProcess(GetCurrentProcess(), 666);
1865         }
1866 }
1867
1868 ///////////////////////////////////////////////////////////////////////////////