OSDN Git Service

Implemented startup and error handling functions.
[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 #pragma once
23
24 //Internal
25 #include <MUtils/Global.h>
26 #include <MUtils/OSSupport.h>
27 #include "CriticalSection_Win32.h"
28
29 //Win32 API
30 #define WIN32_LEAN_AND_MEAN 1
31 #include <Windows.h>
32 #include <Objbase.h>
33 #include <Psapi.h>
34 #include <Sensapi.h>
35 #include <Shellapi.h>
36
37 //Qt
38 #include <QMap>
39 #include <QReadWriteLock>
40 #include <QLibrary>
41 #include <QDir>
42
43 //Main thread ID
44 static const DWORD g_main_thread_id = GetCurrentThreadId();
45
46 ///////////////////////////////////////////////////////////////////////////////
47 // SYSTEM MESSAGE
48 ///////////////////////////////////////////////////////////////////////////////
49
50 static const UINT g_msgBoxFlags = MB_TOPMOST | MB_TASKMODAL | MB_SETFOREGROUND;
51
52 void MUtils::OS::system_message_nfo(const wchar_t *const title, const wchar_t *const text)
53 {
54         MessageBoxW(NULL, text, title, g_msgBoxFlags | MB_ICONINFORMATION);
55 }
56
57 void MUtils::OS::system_message_wrn(const wchar_t *const title, const wchar_t *const text)
58 {
59         MessageBoxW(NULL, text, title, g_msgBoxFlags | MB_ICONWARNING);
60 }
61
62 void MUtils::OS::system_message_err(const wchar_t *const title, const wchar_t *const text)
63 {
64         MessageBoxW(NULL, text, title, g_msgBoxFlags | MB_ICONERROR);
65 }
66
67 ///////////////////////////////////////////////////////////////////////////////
68 // FETCH CLI ARGUMENTS
69 ///////////////////////////////////////////////////////////////////////////////
70
71 static QReadWriteLock g_arguments_lock;
72 static QScopedPointer<QStringList> g_arguments_list;
73
74 const QStringList &MUtils::OS::arguments(void)
75 {
76         QReadLocker readLock(&g_arguments_lock);
77
78         //Already initialized?
79         if(!g_arguments_list.isNull())
80         {
81                 return (*(g_arguments_list.data()));
82         }
83
84         readLock.unlock();
85         QWriteLocker writeLock(&g_arguments_lock);
86
87         //Still not initialized?
88         if(!g_arguments_list.isNull())
89         {
90                 return (*(g_arguments_list.data()));
91         }
92
93         g_arguments_list.reset(new QStringList);
94         int nArgs = 0;
95         LPWSTR *szArglist = CommandLineToArgvW(GetCommandLineW(), &nArgs);
96
97         if(NULL != szArglist)
98         {
99                 for(int i = 0; i < nArgs; i++)
100                 {
101                         *(g_arguments_list.data()) << MUTILS_QSTR(szArglist[i]);
102                 }
103                 LocalFree(szArglist);
104         }
105         else
106         {
107                 qWarning("CommandLineToArgvW() has failed !!!");
108         }
109
110         return (*(g_arguments_list.data()));
111 }
112
113 ///////////////////////////////////////////////////////////////////////////////
114 // OS VERSION DETECTION
115 ///////////////////////////////////////////////////////////////////////////////
116
117 static bool g_os_version_initialized = false;
118 static MUtils::OS::Version::os_version_t g_os_version_info = MUtils::OS::Version::UNKNOWN_OPSYS;
119 static QReadWriteLock g_os_version_lock;
120
121 //Maps marketing names to the actual Windows NT versions
122 static const struct
123 {
124         MUtils::OS::Version::os_version_t version;
125         const char friendlyName[64];
126 }
127 g_os_version_lut[] =
128 {
129         { MUtils::OS::Version::WINDOWS_WIN2K, "Windows 2000"                                  },        //2000
130         { MUtils::OS::Version::WINDOWS_WINXP, "Windows XP or Windows XP Media Center Edition" },        //XP
131         { MUtils::OS::Version::WINDOWS_XPX64, "Windows Server 2003 or Windows XP x64"         },        //XP_x64
132         { MUtils::OS::Version::WINDOWS_VISTA, "Windows Vista or Windows Server 2008"          },        //Vista
133         { MUtils::OS::Version::WINDOWS_WIN70, "Windows 7 or Windows Server 2008 R2"           },        //7
134         { MUtils::OS::Version::WINDOWS_WIN80, "Windows 8 or Windows Server 2012"              },        //8
135         { MUtils::OS::Version::WINDOWS_WIN81, "Windows 8.1 or Windows Server 2012 R2"         },        //8.1
136         { MUtils::OS::Version::WINDOWS_WN100, "Windows 10 or Windows Server 2014 (Preview)"   },        //10
137         { MUtils::OS::Version::UNKNOWN_OPSYS, "N/A" }
138 };
139
140 static bool verify_os_version(const DWORD major, const DWORD minor)
141 {
142         OSVERSIONINFOEXW osvi;
143         DWORDLONG dwlConditionMask = 0;
144
145         //Initialize the OSVERSIONINFOEX structure
146         memset(&osvi, 0, sizeof(OSVERSIONINFOEXW));
147         osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
148         osvi.dwMajorVersion = major;
149         osvi.dwMinorVersion = minor;
150         osvi.dwPlatformId = VER_PLATFORM_WIN32_NT;
151
152         //Initialize the condition mask
153         VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
154         VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
155         VER_SET_CONDITION(dwlConditionMask, VER_PLATFORMID, VER_EQUAL);
156
157         // Perform the test
158         const BOOL ret = VerifyVersionInfoW(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_PLATFORMID, dwlConditionMask);
159
160         //Error checking
161         if(!ret)
162         {
163                 if(GetLastError() != ERROR_OLD_WIN_VERSION)
164                 {
165                         qWarning("VerifyVersionInfo() system call has failed!");
166                 }
167         }
168
169         return (ret != FALSE);
170 }
171
172 static bool get_real_os_version(unsigned int *major, unsigned int *minor, bool *pbOverride)
173 {
174         *major = *minor = 0;
175         *pbOverride = false;
176         
177         //Initialize local variables
178         OSVERSIONINFOEXW osvi;
179         memset(&osvi, 0, sizeof(OSVERSIONINFOEXW));
180         osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEXW);
181
182         //Try GetVersionEx() first
183         if(GetVersionExW((LPOSVERSIONINFOW)&osvi) == FALSE)
184         {
185                 qWarning("GetVersionEx() has failed, cannot detect Windows version!");
186                 return false;
187         }
188
189         //Make sure we are running on NT
190         if(osvi.dwPlatformId == VER_PLATFORM_WIN32_NT)
191         {
192                 *major = osvi.dwMajorVersion;
193                 *minor = osvi.dwMinorVersion;
194         }
195         else
196         {
197                 qWarning("Not running on Windows NT, unsupported operating system!");
198                 return false;
199         }
200
201         //Determine the real *major* version first
202         forever
203         {
204                 const DWORD nextMajor = (*major) + 1;
205                 if(verify_os_version(nextMajor, 0))
206                 {
207                         *pbOverride = true;
208                         *major = nextMajor;
209                         *minor = 0;
210                         continue;
211                 }
212                 break;
213         }
214
215         //Now also determine the real *minor* version
216         forever
217         {
218                 const DWORD nextMinor = (*minor) + 1;
219                 if(verify_os_version((*major), nextMinor))
220                 {
221                         *pbOverride = true;
222                         *minor = nextMinor;
223                         continue;
224                 }
225                 break;
226         }
227
228         return true;
229 }
230
231 const MUtils::OS::Version::os_version_t &MUtils::OS::os_version(void)
232 {
233         QReadLocker readLock(&g_os_version_lock);
234
235         //Already initialized?
236         if(g_os_version_initialized)
237         {
238                 return g_os_version_info;
239         }
240         
241         readLock.unlock();
242         QWriteLocker writeLock(&g_os_version_lock);
243
244         //Initialized now?
245         if(g_os_version_initialized)
246         {
247                 return g_os_version_info;
248         }
249
250         //Detect OS version
251         unsigned int major, minor; bool overrideFlg;
252         if(get_real_os_version(&major, &minor, &overrideFlg))
253         {
254                 g_os_version_info.type = Version::OS_WINDOWS;
255                 g_os_version_info.versionMajor = major;
256                 g_os_version_info.versionMinor = minor;
257                 g_os_version_info.overrideFlag = overrideFlg;
258         }
259         else
260         {
261                 qWarning("Failed to determin the operating system version!");
262         }
263
264         g_os_version_initialized = true;
265         return g_os_version_info;
266 }
267
268 const char *MUtils::OS::os_friendly_name(const MUtils::OS::Version::os_version_t &os_version)
269 {
270         for(size_t i = 0; g_os_version_lut[i].version != MUtils::OS::Version::UNKNOWN_OPSYS; i++)
271         {
272                 if(os_version == g_os_version_lut[i].version)
273                 {
274                         return g_os_version_lut[i].friendlyName;
275                 }
276         }
277
278         return NULL;
279 }
280
281 ///////////////////////////////////////////////////////////////////////////////
282 // KNWON FOLDERS
283 ///////////////////////////////////////////////////////////////////////////////
284
285 typedef QMap<size_t, QString> KFMap;
286 typedef HRESULT (WINAPI *SHGetKnownFolderPath_t)(const GUID &rfid, DWORD dwFlags, HANDLE hToken, PWSTR *ppszPath);
287 typedef HRESULT (WINAPI *SHGetFolderPath_t)(HWND hwndOwner, int nFolder, HANDLE hToken, DWORD dwFlags, LPWSTR pszPath);
288
289 static QScopedPointer<KFMap>  g_known_folders_map;
290 static SHGetKnownFolderPath_t g_known_folders_fpGetKnownFolderPath;
291 static SHGetFolderPath_t      g_known_folders_fpGetFolderPath;
292 static QReadWriteLock         g_known_folders_lock;
293
294 const QString &MUtils::OS::known_folder(known_folder_t folder_id)
295 {
296         static const int CSIDL_FLAG_CREATE = 0x8000;
297         typedef enum { KF_FLAG_CREATE = 0x00008000 } kf_flags_t;
298         
299         struct
300         {
301                 const int csidl;
302                 const GUID guid;
303         }
304         static s_folders[] =
305         {
306                 { 0x001c, {0xF1B32785,0x6FBA,0x4FCF,{0x9D,0x55,0x7B,0x8E,0x7F,0x15,0x70,0x91}} },  //CSIDL_LOCAL_APPDATA
307                 { 0x0026, {0x905e63b6,0xc1bf,0x494e,{0xb2,0x9c,0x65,0xb7,0x32,0xd3,0xd2,0x1a}} },  //CSIDL_PROGRAM_FILES
308                 { 0x0024, {0xF38BF404,0x1D43,0x42F2,{0x93,0x05,0x67,0xDE,0x0B,0x28,0xFC,0x23}} },  //CSIDL_WINDOWS_FOLDER
309                 { 0x0025, {0x1AC14E77,0x02E7,0x4E5D,{0xB7,0x44,0x2E,0xB1,0xAE,0x51,0x98,0xB7}} },  //CSIDL_SYSTEM_FOLDER
310         };
311
312         size_t folderId = size_t(-1);
313
314         switch(folder_id)
315         {
316                 case FOLDER_LOCALAPPDATA: folderId = 0; break;
317                 case FOLDER_PROGRAMFILES: folderId = 1; break;
318                 case FOLDER_SYSTROOT_DIR: folderId = 2; break;
319                 case FOLDER_SYSTEMFOLDER: folderId = 3; break;
320         }
321
322         if(folderId == size_t(-1))
323         {
324                 qWarning("Invalid 'known' folder was requested!");
325                 return *reinterpret_cast<QString*>(NULL);
326         }
327
328         QReadLocker readLock(&g_known_folders_lock);
329
330         //Already in cache?
331         if(!g_known_folders_map.isNull())
332         {
333                 if(g_known_folders_map->contains(folderId))
334                 {
335                         return g_known_folders_map->operator[](folderId);
336                 }
337         }
338
339         //Obtain write lock to initialize
340         readLock.unlock();
341         QWriteLocker writeLock(&g_known_folders_lock);
342
343         //Still not in cache?
344         if(!g_known_folders_map.isNull())
345         {
346                 if(g_known_folders_map->contains(folderId))
347                 {
348                         return g_known_folders_map->operator[](folderId);
349                 }
350         }
351
352         //Initialize on first call
353         if(g_known_folders_map.isNull())
354         {
355                 QLibrary shell32("shell32.dll");
356                 if(shell32.load())
357                 {
358                         g_known_folders_fpGetFolderPath =      (SHGetFolderPath_t)      shell32.resolve("SHGetFolderPathW");
359                         g_known_folders_fpGetKnownFolderPath = (SHGetKnownFolderPath_t) shell32.resolve("SHGetKnownFolderPath");
360                 }
361                 g_known_folders_map.reset(new QMap<size_t, QString>());
362         }
363
364         QString folderPath;
365
366         //Now try to get the folder path!
367         if(g_known_folders_fpGetKnownFolderPath)
368         {
369                 WCHAR *path = NULL;
370                 if(g_known_folders_fpGetKnownFolderPath(s_folders[folderId].guid, KF_FLAG_CREATE, NULL, &path) == S_OK)
371                 {
372                         //MessageBoxW(0, path, L"SHGetKnownFolderPath", MB_TOPMOST);
373                         QDir folderTemp = QDir(QDir::fromNativeSeparators(MUTILS_QSTR(path)));
374                         if(folderTemp.exists())
375                         {
376                                 folderPath = folderTemp.canonicalPath();
377                         }
378                         CoTaskMemFree(path);
379                 }
380         }
381         else if(g_known_folders_fpGetFolderPath)
382         {
383                 QScopedArrayPointer<WCHAR> path(new WCHAR[4096]);
384                 if(g_known_folders_fpGetFolderPath(NULL, s_folders[folderId].csidl | CSIDL_FLAG_CREATE, NULL, NULL, path.data()) == S_OK)
385                 {
386                         //MessageBoxW(0, path, L"SHGetFolderPathW", MB_TOPMOST);
387                         QDir folderTemp = QDir(QDir::fromNativeSeparators(MUTILS_QSTR(path.data())));
388                         if(folderTemp.exists())
389                         {
390                                 folderPath = folderTemp.canonicalPath();
391                         }
392                 }
393         }
394
395         //Update cache
396         g_known_folders_map->insert(folderId, folderPath);
397         return g_known_folders_map->operator[](folderId);
398 }
399
400 ///////////////////////////////////////////////////////////////////////////////
401 // CURRENT DATA (SAFE)
402 ///////////////////////////////////////////////////////////////////////////////
403
404 QDate MUtils::OS::current_date(void)
405 {
406         const DWORD MAX_PROC = 1024;
407         QScopedArrayPointer<DWORD> processes(new DWORD[MAX_PROC]);
408         DWORD bytesReturned = 0;
409         
410         if(!EnumProcesses(processes.data(), sizeof(DWORD) * MAX_PROC, &bytesReturned))
411         {
412                 return QDate::currentDate();
413         }
414
415         const DWORD procCount = bytesReturned / sizeof(DWORD);
416         ULARGE_INTEGER lastStartTime;
417         memset(&lastStartTime, 0, sizeof(ULARGE_INTEGER));
418
419         for(DWORD i = 0; i < procCount; i++)
420         {
421                 if(HANDLE hProc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, processes[i]))
422                 {
423                         FILETIME processTime[4];
424                         if(GetProcessTimes(hProc, &processTime[0], &processTime[1], &processTime[2], &processTime[3]))
425                         {
426                                 ULARGE_INTEGER timeCreation;
427                                 timeCreation.LowPart  = processTime[0].dwLowDateTime;
428                                 timeCreation.HighPart = processTime[0].dwHighDateTime;
429                                 if(timeCreation.QuadPart > lastStartTime.QuadPart)
430                                 {
431                                         lastStartTime.QuadPart = timeCreation.QuadPart;
432                                 }
433                         }
434                         CloseHandle(hProc);
435                 }
436         }
437         
438         FILETIME lastStartTime_fileTime;
439         lastStartTime_fileTime.dwHighDateTime = lastStartTime.HighPart;
440         lastStartTime_fileTime.dwLowDateTime  = lastStartTime.LowPart;
441
442         FILETIME lastStartTime_localTime;
443         if(!FileTimeToLocalFileTime(&lastStartTime_fileTime, &lastStartTime_localTime))
444         {
445                 memcpy(&lastStartTime_localTime, &lastStartTime_fileTime, sizeof(FILETIME));
446         }
447         
448         SYSTEMTIME lastStartTime_system;
449         if(!FileTimeToSystemTime(&lastStartTime_localTime, &lastStartTime_system))
450         {
451                 memset(&lastStartTime_system, 0, sizeof(SYSTEMTIME));
452                 lastStartTime_system.wYear = 1970; lastStartTime_system.wMonth = lastStartTime_system.wDay = 1;
453         }
454
455         const QDate currentDate = QDate::currentDate();
456         const QDate processDate = QDate(lastStartTime_system.wYear, lastStartTime_system.wMonth, lastStartTime_system.wDay);
457         return (currentDate >= processDate) ? currentDate : processDate;
458 }
459
460 ///////////////////////////////////////////////////////////////////////////////
461 // NETWORK STATE
462 ///////////////////////////////////////////////////////////////////////////////
463
464 int MUtils::OS::network_status(void)
465 {
466         DWORD dwFlags;
467         const BOOL ret = IsNetworkAlive(&dwFlags);
468         if(GetLastError() == 0)
469         {
470                 return (ret != FALSE) ? NETWORK_TYPE_YES : NETWORK_TYPE_NON;
471         }
472         return NETWORK_TYPE_ERR;
473 }
474
475 ///////////////////////////////////////////////////////////////////////////////
476 // FATAL EXIT
477 ///////////////////////////////////////////////////////////////////////////////
478
479 static MUtils::Internal::CriticalSection g_fatal_exit_lock;
480 static volatile bool g_fatal_exit_flag = true;
481
482 static DWORD WINAPI fatal_exit_helper(LPVOID lpParameter)
483 {
484         MUtils::OS::system_message_err(L"GURU MEDITATION", (LPWSTR) lpParameter);
485         return 0;
486 }
487
488 void MUtils::OS::fatal_exit(const wchar_t* const errorMessage)
489 {
490         g_fatal_exit_lock.enter();
491         
492         if(!g_fatal_exit_flag)
493         {
494                 return; /*prevent recursive invocation*/
495         }
496
497         g_fatal_exit_flag = false;
498
499         if(g_main_thread_id != GetCurrentThreadId())
500         {
501                 if(HANDLE hThreadMain = OpenThread(THREAD_SUSPEND_RESUME, FALSE, g_main_thread_id))
502                 {
503                         SuspendThread(hThreadMain); /*stop main thread*/
504                 }
505         }
506
507         if(HANDLE hThread = CreateThread(NULL, 0, fatal_exit_helper, (LPVOID) errorMessage, 0, NULL))
508         {
509                 WaitForSingleObject(hThread, INFINITE);
510         }
511
512         for(;;)
513         {
514                 TerminateProcess(GetCurrentProcess(), 666);
515         }
516 }
517
518 ///////////////////////////////////////////////////////////////////////////////