OSDN Git Service

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