OSDN Git Service

Moved translation support into MUtilities library + make clean-up of temporary files...
[mutilities/MUtilities.git] / src / OSSupport_Win32.cpp
1 ///////////////////////////////////////////////////////////////////////////////
2 // MuldeR's Utilities for Qt
3 // Copyright (C) 2004-2014 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 <Objbase.h>
26 #include <Psapi.h>
27 #include <Sensapi.h>
28 #include <Shellapi.h>
29 #include <PowrProf.h>
30 #include <Mmsystem.h>
31
32 //Internal
33 #include <MUtils/Global.h>
34 #include <MUtils/OSSupport.h>
35 #include <MUtils/GUI.h>
36 #include "CriticalSection_Win32.h"
37
38 //Qt
39 #include <QMap>
40 #include <QReadWriteLock>
41 #include <QLibrary>
42 #include <QDir>
43 #include <QWidget>
44 #include <QProcess>
45
46 //Main thread ID
47 static const DWORD g_main_thread_id = GetCurrentThreadId();
48
49 ///////////////////////////////////////////////////////////////////////////////
50 // SYSTEM MESSAGE
51 ///////////////////////////////////////////////////////////////////////////////
52
53 static const UINT g_msgBoxFlags = MB_TOPMOST | MB_TASKMODAL | MB_SETFOREGROUND;
54
55 void MUtils::OS::system_message_nfo(const wchar_t *const title, const wchar_t *const text)
56 {
57         MessageBoxW(NULL, text, title, g_msgBoxFlags | MB_ICONINFORMATION);
58 }
59
60 void MUtils::OS::system_message_wrn(const wchar_t *const title, const wchar_t *const text)
61 {
62         MessageBoxW(NULL, text, title, g_msgBoxFlags | MB_ICONWARNING);
63 }
64
65 void MUtils::OS::system_message_err(const wchar_t *const title, const wchar_t *const text)
66 {
67         MessageBoxW(NULL, text, title, g_msgBoxFlags | MB_ICONERROR);
68 }
69
70 ///////////////////////////////////////////////////////////////////////////////
71 // FETCH CLI ARGUMENTS
72 ///////////////////////////////////////////////////////////////////////////////
73
74 static QReadWriteLock g_arguments_lock;
75 static QScopedPointer<QStringList> g_arguments_list;
76
77 const QStringList &MUtils::OS::arguments(void)
78 {
79         QReadLocker readLock(&g_arguments_lock);
80
81         //Already initialized?
82         if(!g_arguments_list.isNull())
83         {
84                 return (*(g_arguments_list.data()));
85         }
86
87         readLock.unlock();
88         QWriteLocker writeLock(&g_arguments_lock);
89
90         //Still not initialized?
91         if(!g_arguments_list.isNull())
92         {
93                 return (*(g_arguments_list.data()));
94         }
95
96         g_arguments_list.reset(new QStringList);
97         int nArgs = 0;
98         LPWSTR *szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
99
100         if(NULL != szArglist)
101         {
102                 for(int i = 0; i < nArgs; i++)
103                 {
104                         *(g_arguments_list.data()) << MUTILS_QSTR(szArglist[i]);
105                 }
106                 LocalFree(szArglist);
107         }
108         else
109         {
110                 qWarning("CommandLineToArgvW() has failed !!!");
111         }
112
113         return (*(g_arguments_list.data()));
114 }
115
116 ///////////////////////////////////////////////////////////////////////////////
117 // OS VERSION DETECTION
118 ///////////////////////////////////////////////////////////////////////////////
119
120 static bool g_os_version_initialized = false;
121 static MUtils::OS::Version::os_version_t g_os_version_info = MUtils::OS::Version::UNKNOWN_OPSYS;
122 static QReadWriteLock g_os_version_lock;
123
124 //Maps marketing names to the actual Windows NT versions
125 static const struct
126 {
127         MUtils::OS::Version::os_version_t version;
128         const char friendlyName[64];
129 }
130 g_os_version_lut[] =
131 {
132         { MUtils::OS::Version::WINDOWS_WIN2K, "Windows 2000"                                  },        //2000
133         { MUtils::OS::Version::WINDOWS_WINXP, "Windows XP or Windows XP Media Center Edition" },        //XP
134         { MUtils::OS::Version::WINDOWS_XPX64, "Windows Server 2003 or Windows XP x64"         },        //XP_x64
135         { MUtils::OS::Version::WINDOWS_VISTA, "Windows Vista or Windows Server 2008"          },        //Vista
136         { MUtils::OS::Version::WINDOWS_WIN70, "Windows 7 or Windows Server 2008 R2"           },        //7
137         { MUtils::OS::Version::WINDOWS_WIN80, "Windows 8 or Windows Server 2012"              },        //8
138         { MUtils::OS::Version::WINDOWS_WIN81, "Windows 8.1 or Windows Server 2012 R2"         },        //8.1
139         { MUtils::OS::Version::WINDOWS_WN100, "Windows 10 or Windows Server 2014 (Preview)"   },        //10
140         { MUtils::OS::Version::UNKNOWN_OPSYS, "N/A" }
141 };
142
143 static bool verify_os_version(const DWORD major, const DWORD minor)
144 {
145         OSVERSIONINFOEXW osvi;
146         DWORDLONG dwlConditionMask = 0;
147
148         //Initialize the OSVERSIONINFOEX structure
149         memset(&osvi, 0, sizeof(OSVERSIONINFOEXW));
150         osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
151         osvi.dwMajorVersion = major;
152         osvi.dwMinorVersion = minor;
153         osvi.dwPlatformId = VER_PLATFORM_WIN32_NT;
154
155         //Initialize the condition mask
156         VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
157         VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
158         VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);
159
160         // Perform the test
161         const BOOL ret = VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID, dwlConditionMask);
162
163         //Error checking
164         if(!ret)
165         {
166                 if(GetLastError() != ERROR_OLD_WIN_VERSION)
167                 {
168                         qWarning("VerifyVersionInfo() system call has failed!");
169                 }
170         }
171
172         return (ret != FALSE);
173 }
174
175 static bool get_real_os_version(unsigned int *major, unsigned int *minor, bool *pbOverride)
176 {
177         *major = *minor = 0;
178         *pbOverride = false;
179         
180         //Initialize local variables
181         OSVERSIONINFOEXW osvi;
182         memset(&osvi, 0, sizeof(OSVERSIONINFOEXW));
183         osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
184
185         //Try GetVersionEx() first
186         if(GetVersionExW((LPOSVERSIONINFOW)&osvi) == FALSE)
187         {
188                 qWarning("GetVersionEx() has failed, cannot detect Windows version!");
189                 return false;
190         }
191
192         //Make sure we are running on NT
193         if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
194         {
195                 *major = osvi.dwMajorVersion;
196                 *minor = osvi.dwMinorVersion;
197         }
198         else
199         {
200                 qWarning("Not running on Windows NT, unsupported operating system!");
201                 return false;
202         }
203
204         //Determine the real *major* version first
205         forever
206         {
207                 const DWORD nextMajor = (*major) + 1;
208                 if(verify_os_version(nextMajor, 0))
209                 {
210                         *pbOverride = true;
211                         *major = nextMajor;
212                         *minor = 0;
213                         continue;
214                 }
215                 break;
216         }
217
218         //Now also determine the real *minor* version
219         forever
220         {
221                 const DWORD nextMinor = (*minor) + 1;
222                 if(verify_os_version((*major), nextMinor))
223                 {
224                         *pbOverride = true;
225                         *minor = nextMinor;
226                         continue;
227                 }
228                 break;
229         }
230
231         return true;
232 }
233
234 const MUtils::OS::Version::os_version_t &MUtils::OS::os_version(void)
235 {
236         QReadLocker readLock(&g_os_version_lock);
237
238         //Already initialized?
239         if(g_os_version_initialized)
240         {
241                 return g_os_version_info;
242         }
243         
244         readLock.unlock();
245         QWriteLocker writeLock(&g_os_version_lock);
246
247         //Initialized now?
248         if(g_os_version_initialized)
249         {
250                 return g_os_version_info;
251         }
252
253         //Detect OS version
254         unsigned int major, minor; bool overrideFlg;
255         if(get_real_os_version(&major, &minor, &overrideFlg))
256         {
257                 g_os_version_info.type = Version::OS_WINDOWS;
258                 g_os_version_info.versionMajor = major;
259                 g_os_version_info.versionMinor = minor;
260                 g_os_version_info.overrideFlag = overrideFlg;
261         }
262         else
263         {
264                 qWarning("Failed to determin the operating system version!");
265         }
266
267         //Completed
268         g_os_version_initialized = true;
269         return g_os_version_info;
270 }
271
272 const char *MUtils::OS::os_friendly_name(const MUtils::OS::Version::os_version_t &os_version)
273 {
274         for(size_t i = 0; g_os_version_lut[i].version != MUtils::OS::Version::UNKNOWN_OPSYS; i++)
275         {
276                 if(os_version == g_os_version_lut[i].version)
277                 {
278                         return g_os_version_lut[i].friendlyName;
279                 }
280         }
281
282         return NULL;
283 }
284
285 ///////////////////////////////////////////////////////////////////////////////
286 // WINE DETECTION
287 ///////////////////////////////////////////////////////////////////////////////
288
289 static bool g_wine_deteced = false;
290 static bool g_wine_initialized = false;
291 static QReadWriteLock g_wine_lock;
292
293 static const bool detect_wine(void)
294 {
295         bool is_wine = false;
296         
297         QLibrary ntdll("ntdll.dll");
298         if(ntdll.load())
299         {
300                 if(ntdll.resolve("wine_nt_to_unix_file_name") != NULL) is_wine = true;
301                 if(ntdll.resolve("wine_get_version")          != NULL) is_wine = true;
302                 ntdll.unload();
303         }
304
305         return is_wine;
306 }
307
308 const bool &MUtils::OS::running_on_wine(void)
309 {
310         QReadLocker readLock(&g_wine_lock);
311
312         //Already initialized?
313         if(g_wine_initialized)
314         {
315                 return g_wine_deteced;
316         }
317
318         readLock.unlock();
319         QWriteLocker writeLock(&g_wine_lock);
320
321         //Initialized now?
322         if(g_wine_initialized)
323         {
324                 return g_wine_deteced;
325         }
326
327         //Try to detect Wine
328         g_wine_deteced = detect_wine();
329         g_wine_initialized = true;
330
331         return g_wine_deteced;
332 }
333
334 ///////////////////////////////////////////////////////////////////////////////
335 // KNWON FOLDERS
336 ///////////////////////////////////////////////////////////////////////////////
337
338 typedef QMap<size_t, QString> KFMap;
339 typedef HRESULT (WINAPI *SHGetKnownFolderPath_t)(const GUID &rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath);
340 typedef HRESULT (WINAPI *SHGetFolderPath_t)(HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath);
341
342 static QScopedPointer<KFMap>  g_known_folders_map;
343 static SHGetKnownFolderPath_t g_known_folders_fpGetKnownFolderPath;
344 static SHGetFolderPath_t      g_known_folders_fpGetFolderPath;
345 static QReadWriteLock         g_known_folders_lock;
346
347 const QString &MUtils::OS::known_folder(known_folder_t folder_id)
348 {
349         static const int CSIDL_FLAG_CREATE = 0x8000;
350         typedef enum { KF_FLAG_CREATE = 0x00008000 } kf_flags_t;
351         
352         struct
353         {
354                 const int csidl;
355                 const GUID guid;
356         }
357         static s_folders[] =
358         {
359                 { 0x001c, {0xF1B32785,0x6FBA,0x4FCF,{0x9D,0x55,0x7B,0x8E,0x7F,0x15,0x70,0x91}} },  //CSIDL_LOCAL_APPDATA
360                 { 0x0026, {0x905e63b6,0xc1bf,0x494e,{0xb2,0x9c,0x65,0xb7,0x32,0xd3,0xd2,0x1a}} },  //CSIDL_PROGRAM_FILES
361                 { 0x0024, {0xF38BF404,0x1D43,0x42F2,{0x93,0x05,0x67,0xDE,0x0B,0x28,0xFC,0x23}} },  //CSIDL_WINDOWS_FOLDER
362                 { 0x0025, {0x1AC14E77,0x02E7,0x4E5D,{0xB7,0x44,0x2E,0xB1,0xAE,0x51,0x98,0xB7}} },  //CSIDL_SYSTEM_FOLDER
363         };
364
365         size_t folderId = size_t(-1);
366
367         switch(folder_id)
368         {
369                 case FOLDER_LOCALAPPDATA: folderId = 0; break;
370                 case FOLDER_PROGRAMFILES: folderId = 1; break;
371                 case FOLDER_SYSTROOT_DIR: folderId = 2; break;
372                 case FOLDER_SYSTEMFOLDER: folderId = 3; break;
373         }
374
375         if(folderId == size_t(-1))
376         {
377                 qWarning("Invalid 'known' folder was requested!");
378                 return *reinterpret_cast<QString*>(NULL);
379         }
380
381         QReadLocker readLock(&g_known_folders_lock);
382
383         //Already in cache?
384         if(!g_known_folders_map.isNull())
385         {
386                 if(g_known_folders_map->contains(folderId))
387                 {
388                         return g_known_folders_map->operator[](folderId);
389                 }
390         }
391
392         //Obtain write lock to initialize
393         readLock.unlock();
394         QWriteLocker writeLock(&g_known_folders_lock);
395
396         //Still not in cache?
397         if(!g_known_folders_map.isNull())
398         {
399                 if(g_known_folders_map->contains(folderId))
400                 {
401                         return g_known_folders_map->operator[](folderId);
402                 }
403         }
404
405         //Initialize on first call
406         if(g_known_folders_map.isNull())
407         {
408                 QLibrary shell32("shell32.dll");
409                 if(shell32.load())
410                 {
411                         g_known_folders_fpGetFolderPath =      (SHGetFolderPath_t)      shell32.resolve("SHGetFolderPathW");
412                         g_known_folders_fpGetKnownFolderPath = (SHGetKnownFolderPath_t) shell32.resolve("SHGetKnownFolderPath");
413                 }
414                 g_known_folders_map.reset(new QMap<size_t, QString>());
415         }
416
417         QString folderPath;
418
419         //Now try to get the folder path!
420         if(g_known_folders_fpGetKnownFolderPath)
421         {
422                 WCHAR *path = NULL;
423                 if(g_known_folders_fpGetKnownFolderPath(s_folders[folderId].guid, KF_FLAG_CREATE, NULL, &path) == S_OK)
424                 {
425                         //MessageBoxW(0, path, L"SHGetKnownFolderPath", MB_TOPMOST);
426                         QDir folderTemp = QDir(QDir::fromNativeSeparators(MUTILS_QSTR(path)));
427                         if(folderTemp.exists())
428                         {
429                                 folderPath = folderTemp.canonicalPath();
430                         }
431                         CoTaskMemFree(path);
432                 }
433         }
434         else if(g_known_folders_fpGetFolderPath)
435         {
436                 QScopedArrayPointer<WCHAR> path(new WCHAR[4096]);
437                 if(g_known_folders_fpGetFolderPath(NULL, s_folders[folderId].csidl | CSIDL_FLAG_CREATE, NULL, NULL, path.data()) == S_OK)
438                 {
439                         //MessageBoxW(0, path, L"SHGetFolderPathW", MB_TOPMOST);
440                         QDir folderTemp = QDir(QDir::fromNativeSeparators(MUTILS_QSTR(path.data())));
441                         if(folderTemp.exists())
442                         {
443                                 folderPath = folderTemp.canonicalPath();
444                         }
445                 }
446         }
447
448         //Update cache
449         g_known_folders_map->insert(folderId, folderPath);
450         return g_known_folders_map->operator[](folderId);
451 }
452
453 ///////////////////////////////////////////////////////////////////////////////
454 // CURRENT DATA & TIME
455 ///////////////////////////////////////////////////////////////////////////////
456
457 QDate MUtils::OS::current_date(void)
458 {
459         const DWORD MAX_PROC = 1024;
460         QScopedArrayPointer<DWORD> processes(new DWORD[MAX_PROC]);
461         DWORD bytesReturned = 0;
462         
463         if(!EnumProcesses(processes.data(), sizeof(DWORD) * MAX_PROC, &bytesReturned))
464         {
465                 return QDate::currentDate();
466         }
467
468         const DWORD procCount = bytesReturned / sizeof(DWORD);
469         ULARGE_INTEGER lastStartTime;
470         memset(&lastStartTime, 0, sizeof(ULARGE_INTEGER));
471
472         for(DWORD i = 0; i < procCount; i++)
473         {
474                 if(HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processes[i]))
475                 {
476                         FILETIME processTime[4];
477                         if(GetProcessTimes(hProc, &processTime[0], &processTime[1], &processTime[2], &processTime[3]))
478                         {
479                                 ULARGE_INTEGER timeCreation;
480                                 timeCreation.LowPart  = processTime[0].dwLowDateTime;
481                                 timeCreation.HighPart = processTime[0].dwHighDateTime;
482                                 if(timeCreation.QuadPart > lastStartTime.QuadPart)
483                                 {
484                                         lastStartTime.QuadPart = timeCreation.QuadPart;
485                                 }
486                         }
487                         CloseHandle(hProc);
488                 }
489         }
490         
491         FILETIME lastStartTime_fileTime;
492         lastStartTime_fileTime.dwHighDateTime = lastStartTime.HighPart;
493         lastStartTime_fileTime.dwLowDateTime  = lastStartTime.LowPart;
494
495         FILETIME lastStartTime_localTime;
496         if(!FileTimeToLocalFileTime(&lastStartTime_fileTime, &lastStartTime_localTime))
497         {
498                 memcpy(&lastStartTime_localTime, &lastStartTime_fileTime, sizeof(FILETIME));
499         }
500         
501         SYSTEMTIME lastStartTime_system;
502         if(!FileTimeToSystemTime(&lastStartTime_localTime, &lastStartTime_system))
503         {
504                 memset(&lastStartTime_system, 0, sizeof(SYSTEMTIME));
505                 lastStartTime_system.wYear = 1970; lastStartTime_system.wMonth = lastStartTime_system.wDay = 1;
506         }
507
508         const QDate currentDate = QDate::currentDate();
509         const QDate processDate = QDate(lastStartTime_system.wYear, lastStartTime_system.wMonth, lastStartTime_system.wDay);
510         return (currentDate >= processDate) ? currentDate : processDate;
511 }
512
513 quint64 MUtils::OS::current_file_time(void)
514 {
515         FILETIME fileTime;
516         GetSystemTimeAsFileTime(&fileTime);
517
518         ULARGE_INTEGER temp;
519         temp.HighPart = fileTime.dwHighDateTime;
520         temp.LowPart = fileTime.dwLowDateTime;
521
522         return temp.QuadPart;
523 }
524
525 ///////////////////////////////////////////////////////////////////////////////
526 // PROCESS ELEVATION
527 ///////////////////////////////////////////////////////////////////////////////
528
529 static bool user_is_admin_helper(void)
530 {
531         HANDLE hToken = NULL;
532         if(!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
533         {
534                 return false;
535         }
536
537         DWORD dwSize = 0;
538         if(!GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwSize))
539         {
540                 if(GetLastError() != ERROR_INSUFFICIENT_BUFFER)
541                 {
542                         CloseHandle(hToken);
543                         return false;
544                 }
545         }
546
547         PTOKEN_GROUPS lpGroups = (PTOKEN_GROUPS) malloc(dwSize);
548         if(!lpGroups)
549         {
550                 CloseHandle(hToken);
551                 return false;
552         }
553
554         if(!GetTokenInformation(hToken, TokenGroups, lpGroups, dwSize, &dwSize))
555         {
556                 free(lpGroups);
557                 CloseHandle(hToken);
558                 return false;
559         }
560
561         PSID lpSid = NULL; SID_IDENTIFIER_AUTHORITY Authority = {SECURITY_NT_AUTHORITY};
562         if(!AllocateAndInitializeSid(&Authority, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &lpSid))
563         {
564                 free(lpGroups);
565                 CloseHandle(hToken);
566                 return false;
567         }
568
569         bool bResult = false;
570         for(DWORD i = 0; i < lpGroups->GroupCount; i++)
571         {
572                 if(EqualSid(lpSid, lpGroups->Groups[i].Sid))
573                 {
574                         bResult = true;
575                         break;
576                 }
577         }
578
579         FreeSid(lpSid);
580         free(lpGroups);
581         CloseHandle(hToken);
582         return bResult;
583 }
584
585 bool MUtils::OS::is_elevated(bool *bIsUacEnabled)
586 {
587         if(bIsUacEnabled)
588         {
589                 *bIsUacEnabled = false;
590         }
591
592         bool bIsProcessElevated = false;
593         HANDLE hToken = NULL;
594         
595         if(OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hToken))
596         {
597                 TOKEN_ELEVATION_TYPE tokenElevationType;
598                 DWORD returnLength;
599                 if(GetTokenInformation(hToken, TokenElevationType, &tokenElevationType, sizeof(TOKEN_ELEVATION_TYPE), &returnLength))
600                 {
601                         if(returnLength == sizeof(TOKEN_ELEVATION_TYPE))
602                         {
603                                 switch(tokenElevationType)
604                                 {
605                                 case TokenElevationTypeDefault:
606                                         qDebug("Process token elevation type: Default -> UAC is disabled.\n");
607                                         break;
608                                 case TokenElevationTypeFull:
609                                         qWarning("Process token elevation type: Full -> potential security risk!\n");
610                                         bIsProcessElevated = true;
611                                         if(bIsUacEnabled) *bIsUacEnabled = true;
612                                         break;
613                                 case TokenElevationTypeLimited:
614                                         qDebug("Process token elevation type: Limited -> not elevated.\n");
615                                         if(bIsUacEnabled) *bIsUacEnabled = true;
616                                         break;
617                                 default:
618                                         qWarning("Unknown tokenElevationType value: %d", tokenElevationType);
619                                         break;
620                                 }
621                         }
622                         else
623                         {
624                                 qWarning("GetTokenInformation() return an unexpected size!");
625                         }
626                 }
627                 CloseHandle(hToken);
628         }
629         else
630         {
631                 qWarning("Failed to open process token!");
632         }
633
634         return bIsProcessElevated;
635 }
636
637 bool MUtils::OS::user_is_admin(void)
638 {
639         bool isAdmin = false;
640
641         //Check for process elevation and UAC support first!
642         if(MUtils::OS::is_elevated(&isAdmin))
643         {
644                 qWarning("Process is elevated -> user is admin!");
645                 return true;
646         }
647         
648         //If not elevated and UAC is not available -> user must be in admin group!
649         if(!isAdmin)
650         {
651                 qDebug("UAC is disabled/unavailable -> checking for Administrators group");
652                 isAdmin = user_is_admin_helper();
653         }
654
655         return isAdmin;
656 }
657
658 ///////////////////////////////////////////////////////////////////////////////
659 // NETWORK STATE
660 ///////////////////////////////////////////////////////////////////////////////
661
662 int MUtils::OS::network_status(void)
663 {
664         DWORD dwFlags;
665         const BOOL ret = IsNetworkAlive(&dwFlags);
666         if(GetLastError() == 0)
667         {
668                 return (ret != FALSE) ? NETWORK_TYPE_YES : NETWORK_TYPE_NON;
669         }
670         return NETWORK_TYPE_ERR;
671 }
672
673 ///////////////////////////////////////////////////////////////////////////////
674 // MESSAGE HANDLER
675 ///////////////////////////////////////////////////////////////////////////////
676
677 bool MUtils::OS::handle_os_message(const void *const message, long *result)
678 {
679         const MSG *const msg = reinterpret_cast<const MSG*>(message);
680
681         switch(msg->message)
682         {
683         case WM_QUERYENDSESSION:
684                 qWarning("WM_QUERYENDSESSION message received!");
685                 *result = MUtils::GUI::broadcast(MUtils::GUI::USER_EVENT_QUERYENDSESSION, false) ? TRUE : FALSE;
686                 return true;
687         case WM_ENDSESSION:
688                 qWarning("WM_ENDSESSION message received!");
689                 if(msg->wParam == TRUE)
690                 {
691                         MUtils::GUI::broadcast(MUtils::GUI::USER_EVENT_ENDSESSION, false);
692                         MUtils::GUI::force_quit();
693                         exit(1);
694                 }
695                 *result = 0;
696                 return true;
697         default:
698                 /*ignore this message and let Qt handle it*/
699                 return false;
700         }
701 }
702
703 ///////////////////////////////////////////////////////////////////////////////
704 // SLEEP
705 ///////////////////////////////////////////////////////////////////////////////
706
707 void MUtils::OS::sleep_ms(const size_t &duration)
708 {
709         Sleep((DWORD) duration);
710 }
711
712 ///////////////////////////////////////////////////////////////////////////////
713 // EXECUTABLE CHECK
714 ///////////////////////////////////////////////////////////////////////////////
715
716 bool MUtils::OS::is_executable_file(const QString &path)
717 {
718         bool bIsExecutable = false;
719         DWORD binaryType;
720         if(GetBinaryType(MUTILS_WCHR(QDir::toNativeSeparators(path)), &binaryType))
721         {
722                 bIsExecutable = (binaryType == SCS_32BIT_BINARY || binaryType == SCS_64BIT_BINARY);
723         }
724         return bIsExecutable;
725 }
726
727 ///////////////////////////////////////////////////////////////////////////////
728 // HIBERNATION / SHUTDOWN
729 ///////////////////////////////////////////////////////////////////////////////
730
731 bool MUtils::OS::is_hibernation_supported(void)
732 {
733         bool hibernationSupported = false;
734
735         SYSTEM_POWER_CAPABILITIES pwrCaps;
736         SecureZeroMemory(&pwrCaps, sizeof(SYSTEM_POWER_CAPABILITIES));
737         
738         if(GetPwrCapabilities(&pwrCaps))
739         {
740                 hibernationSupported = pwrCaps.SystemS4 && pwrCaps.HiberFilePresent;
741         }
742
743         return hibernationSupported;
744 }
745
746 bool MUtils::OS::shutdown_computer(const QString &message, const unsigned long timeout, const bool forceShutdown, const bool hibernate)
747 {
748         HANDLE hToken = NULL;
749
750         if(OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
751         {
752                 TOKEN_PRIVILEGES privileges;
753                 memset(&privileges, 0, sizeof(TOKEN_PRIVILEGES));
754                 privileges.PrivilegeCount = 1;
755                 privileges.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
756                 
757                 if(LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &privileges.Privileges[0].Luid))
758                 {
759                         if(AdjustTokenPrivileges(hToken, FALSE, &privileges, NULL, NULL, NULL))
760                         {
761                                 if(hibernate)
762                                 {
763                                         if(SetSuspendState(TRUE, TRUE, TRUE))
764                                         {
765                                                 return true;
766                                         }
767                                 }
768                                 const DWORD reason = SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_FLAG_PLANNED;
769                                 return InitiateSystemShutdownEx(NULL, const_cast<wchar_t*>(MUTILS_WCHR(message)), timeout, forceShutdown ? TRUE : FALSE, FALSE, reason);
770                         }
771                 }
772         }
773         
774         return false;
775 }
776
777 ///////////////////////////////////////////////////////////////////////////////
778 // FREE DISKSPACE
779 ///////////////////////////////////////////////////////////////////////////////
780
781 bool MUtils::OS::free_diskspace(const QString &path, quint64 &freeSpace)
782 {
783         ULARGE_INTEGER freeBytesAvailable, totalNumberOfBytes, totalNumberOfFreeBytes;
784         if(GetDiskFreeSpaceExW(reinterpret_cast<const wchar_t*>(QDir::toNativeSeparators(path).utf16()), &freeBytesAvailable, &totalNumberOfBytes, &totalNumberOfFreeBytes))
785         {
786                 freeSpace = freeBytesAvailable.QuadPart;
787                 return true;;
788         }
789
790         freeSpace = -1;
791         return false;
792 }
793
794 ///////////////////////////////////////////////////////////////////////////////
795 // SHELL OPEN
796 ///////////////////////////////////////////////////////////////////////////////
797
798 bool MUtils::OS::shell_open(const QWidget *parent, const QString &url, const QString &parameters, const QString &directory, const bool explore)
799 {
800         return ((int) ShellExecuteW((parent ? 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;
801 }
802
803 bool MUtils::OS::shell_open(const QWidget *parent, const QString &url, const bool explore)
804 {
805         return shell_open(parent, url, QString(), QString(), explore);
806 }
807
808 ///////////////////////////////////////////////////////////////////////////////
809 // OPEN MEDIA FILE
810 ///////////////////////////////////////////////////////////////////////////////
811
812 bool MUtils::OS::open_media_file(const QString &mediaFilePath)
813 {
814         const static wchar_t *registryPrefix[2] = { L"SOFTWARE\\", L"SOFTWARE\\Wow6432Node\\" };
815         const static wchar_t *registryKeys[3] = 
816         {
817                 L"Microsoft\\Windows\\CurrentVersion\\Uninstall\\{97D341C8-B0D1-4E4A-A49A-C30B52F168E9}",
818                 L"Microsoft\\Windows\\CurrentVersion\\Uninstall\\{DB9E4EAB-2717-499F-8D56-4CC8A644AB60}",
819                 L"foobar2000"
820         };
821         const static wchar_t *appNames[4] = { L"smplayer_portable.exe", L"smplayer.exe", L"MPUI.exe", L"foobar2000.exe" };
822         const static wchar_t *valueNames[2] = { L"InstallLocation", L"InstallDir" };
823
824         for(size_t i = 0; i < 3; i++)
825         {
826                 for(size_t j = 0; j < 2; j++)
827                 {
828                         QString mplayerPath;
829                         HKEY registryKeyHandle = NULL;
830
831                         const QString currentKey = MUTILS_QSTR(registryPrefix[j]).append(MUTILS_QSTR(registryKeys[i]));
832                         if(RegOpenKeyExW(HKEY_LOCAL_MACHINE, MUTILS_WCHR(currentKey), 0, KEY_READ, &registryKeyHandle) == ERROR_SUCCESS)
833                         {
834                                 for(size_t k = 0; k < 2; k++)
835                                 {
836                                         wchar_t Buffer[4096];
837                                         DWORD BuffSize = sizeof(wchar_t*) * 4096;
838                                         DWORD DataType = REG_NONE;
839                                         if(RegQueryValueExW(registryKeyHandle, valueNames[k], 0, &DataType, reinterpret_cast<BYTE*>(Buffer), &BuffSize) == ERROR_SUCCESS)
840                                         {
841                                                 if((DataType == REG_SZ) || (DataType == REG_EXPAND_SZ) || (DataType == REG_LINK))
842                                                 {
843                                                         mplayerPath = MUTILS_QSTR(Buffer);
844                                                         break;
845                                                 }
846                                         }
847                                 }
848                                 RegCloseKey(registryKeyHandle);
849                         }
850
851                         if(!mplayerPath.isEmpty())
852                         {
853                                 QDir mplayerDir(mplayerPath);
854                                 if(mplayerDir.exists())
855                                 {
856                                         for(size_t k = 0; k < 4; k++)
857                                         {
858                                                 if(mplayerDir.exists(MUTILS_QSTR(appNames[k])))
859                                                 {
860                                                         qDebug("Player found at:\n%s\n", MUTILS_UTF8(mplayerDir.absoluteFilePath(MUTILS_QSTR(appNames[k]))));
861                                                         QProcess::startDetached(mplayerDir.absoluteFilePath(MUTILS_QSTR(appNames[k])), QStringList() << QDir::toNativeSeparators(mediaFilePath));
862                                                         return true;
863                                                 }
864                                         }
865                                 }
866                         }
867                 }
868         }
869         return false;
870 }
871
872 ///////////////////////////////////////////////////////////////////////////////
873 // DEBUGGER CHECK
874 ///////////////////////////////////////////////////////////////////////////////
875
876 static bool change_process_priority_helper(const HANDLE hProcess, const int priority)
877 {
878         bool ok = false;
879
880         switch(qBound(-2, priority, 2))
881         {
882         case 2:
883                 ok = (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS) == TRUE);
884                 break;
885         case 1:
886                 if(!(ok = (SetPriorityClass(hProcess, ABOVE_NORMAL_PRIORITY_CLASS) == TRUE)))
887                 {
888                         ok = (SetPriorityClass(hProcess, HIGH_PRIORITY_CLASS) == TRUE);
889                 }
890                 break;
891         case 0:
892                 ok = (SetPriorityClass(hProcess, NORMAL_PRIORITY_CLASS) == TRUE);
893                 break;
894         case -1:
895                 if(!(ok = (SetPriorityClass(hProcess, BELOW_NORMAL_PRIORITY_CLASS) == TRUE)))
896                 {
897                         ok = (SetPriorityClass(hProcess, IDLE_PRIORITY_CLASS) == TRUE);
898                 }
899                 break;
900         case -2:
901                 ok = (SetPriorityClass(hProcess, IDLE_PRIORITY_CLASS) == TRUE);
902                 break;
903         }
904
905         return ok;
906 }
907
908 bool MUtils::OS::change_process_priority(const int priority)
909 {
910         return change_process_priority_helper(GetCurrentProcess(), priority);
911 }
912
913 bool MUtils::OS::change_process_priority(const QProcess *proc, const int priority)
914 {
915         if(Q_PID qPid = proc->pid())
916         {
917                 return change_process_priority_helper(qPid->hProcess, priority);
918         }
919         else
920         {
921                 return false;
922         }
923 }
924
925 ///////////////////////////////////////////////////////////////////////////////
926 // PROCESS ID
927 ///////////////////////////////////////////////////////////////////////////////
928
929 quint32 MUtils::OS::process_id(const QProcess *proc)
930 {
931         PROCESS_INFORMATION *procInf = proc->pid();
932         return (procInf) ? procInf->dwProcessId : NULL;
933 }
934
935 ///////////////////////////////////////////////////////////////////////////////
936 // SYSTEM TIMER
937 ///////////////////////////////////////////////////////////////////////////////
938
939 bool MUtils::OS::setup_timer_resolution(const quint32 &interval)
940 {
941         return timeBeginPeriod(interval) == TIMERR_NOERROR;
942 }
943
944 bool MUtils::OS::reset_timer_resolution(const quint32 &interval)
945 {
946         return timeEndPeriod(interval) == TIMERR_NOERROR;
947 }
948
949 ///////////////////////////////////////////////////////////////////////////////
950 // CHECK KEY STATE
951 ///////////////////////////////////////////////////////////////////////////////
952
953 bool MUtils::OS::check_key_state_esc(void)
954 {
955         return (GetAsyncKeyState(VK_ESCAPE) & 0x0001) != 0;
956 }
957
958 ///////////////////////////////////////////////////////////////////////////////
959 // DEBUGGER CHECK
960 ///////////////////////////////////////////////////////////////////////////////
961
962 #if (!(MUTILS_DEBUG))
963 static __forceinline bool is_debugger_present(void)
964 {
965         __try
966         {
967                 CloseHandle((HANDLE)((DWORD_PTR)-3));
968         }
969         __except(1)
970         {
971                 return true;
972         }
973
974         BOOL bHaveDebugger = FALSE;
975         if(CheckRemoteDebuggerPresent(GetCurrentProcess(), &bHaveDebugger))
976         {
977                 if(bHaveDebugger) return true;
978         }
979
980         return IsDebuggerPresent();
981 }
982 static __forceinline bool check_debugger_helper(void)
983 {
984         if(is_debugger_present())
985         {
986                 MUtils::OS::fatal_exit(L"Not a debug build. Please unload debugger and try again!");
987                 return true;
988         }
989         return false;
990 }
991 #else
992 #define check_debugger_helper() (false)
993 #endif
994
995 void MUtils::OS::check_debugger(void)
996 {
997         check_debugger_helper();
998 }
999
1000 static volatile bool g_debug_check = check_debugger_helper();
1001
1002 ///////////////////////////////////////////////////////////////////////////////
1003 // FATAL EXIT
1004 ///////////////////////////////////////////////////////////////////////////////
1005
1006 static MUtils::Internal::CriticalSection g_fatal_exit_lock;
1007 static volatile bool g_fatal_exit_flag = true;
1008
1009 static DWORD WINAPI fatal_exit_helper(LPVOID lpParameter)
1010 {
1011         MUtils::OS::system_message_err(L"GURU MEDITATION", (LPWSTR) lpParameter);
1012         return 0;
1013 }
1014
1015 void MUtils::OS::fatal_exit(const wchar_t* const errorMessage)
1016 {
1017         g_fatal_exit_lock.enter();
1018         
1019         if(!g_fatal_exit_flag)
1020         {
1021                 return; /*prevent recursive invocation*/
1022         }
1023
1024         g_fatal_exit_flag = false;
1025
1026         if(g_main_thread_id != GetCurrentThreadId())
1027         {
1028                 if(HANDLE hThreadMain = OpenThread(THREAD_SUSPEND_RESUME, FALSE, g_main_thread_id))
1029                 {
1030                         SuspendThread(hThreadMain); /*stop main thread*/
1031                 }
1032         }
1033
1034         if(HANDLE hThread = CreateThread(NULL, 0, fatal_exit_helper, (LPVOID) errorMessage, 0, NULL))
1035         {
1036                 WaitForSingleObject(hThread, INFINITE);
1037         }
1038
1039         for(;;)
1040         {
1041                 TerminateProcess(GetCurrentProcess(), 666);
1042         }
1043 }
1044
1045 ///////////////////////////////////////////////////////////////////////////////