Merge tag '1.11.0-beta2' into jgrpp
# Conflicts: # .github/workflows/ci-build.yml # .github/workflows/release.yml # CMakeLists.txt # src/blitter/32bpp_optimized.cpp # src/debug.cpp # src/gfx.cpp # src/gfx_func.h # src/lang/czech.txt # src/lang/english.txt # src/lang/italian.txt # src/lang/swedish.txt # src/lang/ukrainian.txt # src/network/network_server.cpp # src/os/windows/crashlog_win.cpp # src/os/windows/win32.cpp # src/pathfinder/follow_track.hpp # src/screenshot.cpp # src/settings_type.h # src/spritecache.cpp # src/vehicle_gui.cpp # src/video/sdl2_v.cpp # src/video/video_driver.cpp # src/video/video_driver.hpp # src/video/win32_v.cpp
This commit is contained in:
@@ -48,7 +48,7 @@ static DWORD _tlsdata_key;
|
||||
|
||||
struct TLSData {
|
||||
char utf8_buf[512];
|
||||
TCHAR system_buf[512];
|
||||
wchar_t system_buf[512];
|
||||
char locale_retbuf[6];
|
||||
};
|
||||
|
||||
@@ -76,14 +76,12 @@ bool MyShowCursor(bool show, bool toggle)
|
||||
|
||||
/**
|
||||
* Helper function needed by dynamically loading libraries
|
||||
* XXX: Hurray for MS only having an ANSI GetProcAddress function
|
||||
* on normal windows and no Wide version except for in Windows Mobile/CE
|
||||
*/
|
||||
bool LoadLibraryList(Function proc[], const char *dll)
|
||||
{
|
||||
while (*dll != '\0') {
|
||||
HMODULE lib;
|
||||
lib = LoadLibrary(MB_TO_WIDE(dll));
|
||||
lib = LoadLibrary(OTTD2FS(dll));
|
||||
|
||||
if (lib == nullptr) return false;
|
||||
for (;;) {
|
||||
@@ -103,12 +101,12 @@ bool LoadLibraryList(Function proc[], const char *dll)
|
||||
void ShowOSErrorBox(const char *buf, bool system)
|
||||
{
|
||||
MyShowCursor(true);
|
||||
MessageBox(GetActiveWindow(), OTTD2FS(buf), _T("Error!"), MB_ICONSTOP | MB_TASKMODAL);
|
||||
MessageBox(GetActiveWindow(), OTTD2FS(buf), L"Error!", MB_ICONSTOP | MB_TASKMODAL);
|
||||
}
|
||||
|
||||
void OSOpenBrowser(const char *url)
|
||||
{
|
||||
ShellExecute(GetActiveWindow(), _T("open"), OTTD2FS(url), nullptr, nullptr, SW_SHOWNORMAL);
|
||||
ShellExecute(GetActiveWindow(), L"open", OTTD2FS(url), nullptr, nullptr, SW_SHOWNORMAL);
|
||||
}
|
||||
|
||||
/* Code below for windows version of opendir/readdir/closedir copied and
|
||||
@@ -158,7 +156,7 @@ static inline void dir_free(DIR *d)
|
||||
}
|
||||
}
|
||||
|
||||
DIR *opendir(const TCHAR *path)
|
||||
DIR *opendir(const wchar_t *path)
|
||||
{
|
||||
DIR *d;
|
||||
UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box
|
||||
@@ -167,12 +165,12 @@ DIR *opendir(const TCHAR *path)
|
||||
if ((fa != INVALID_FILE_ATTRIBUTES) && (fa & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
d = dir_calloc();
|
||||
if (d != nullptr) {
|
||||
TCHAR search_path[MAX_PATH];
|
||||
bool slash = path[_tcslen(path) - 1] == '\\';
|
||||
wchar_t search_path[MAX_PATH];
|
||||
bool slash = path[wcslen(path) - 1] == '\\';
|
||||
|
||||
/* build search path for FindFirstFile, try not to append additional slashes
|
||||
* as it throws Win9x off its groove for root directories */
|
||||
_sntprintf(search_path, lengthof(search_path), _T("%s%s*"), path, slash ? _T("") : _T("\\"));
|
||||
_snwprintf(search_path, lengthof(search_path), L"%s%s*", path, slash ? L"" : L"\\");
|
||||
*lastof(search_path) = '\0';
|
||||
d->hFind = FindFirstFile(search_path, &d->fd);
|
||||
|
||||
@@ -230,8 +228,8 @@ bool FiosIsRoot(const char *file)
|
||||
|
||||
void FiosGetDrives(FileList &file_list)
|
||||
{
|
||||
TCHAR drives[256];
|
||||
const TCHAR *s;
|
||||
wchar_t drives[256];
|
||||
const wchar_t *s;
|
||||
|
||||
GetLogicalDriveStrings(lengthof(drives), drives);
|
||||
for (s = drives; *s != '\0';) {
|
||||
@@ -270,15 +268,10 @@ bool FiosIsHiddenFile(const struct dirent *ent)
|
||||
bool FiosGetDiskFreeSpace(const char *path, uint64 *tot)
|
||||
{
|
||||
UINT sem = SetErrorMode(SEM_FAILCRITICALERRORS); // disable 'no-disk' message box
|
||||
bool retval = false;
|
||||
TCHAR root[4];
|
||||
DWORD spc, bps, nfc, tnc;
|
||||
|
||||
_sntprintf(root, lengthof(root), _T("%c:") _T(PATHSEP), path[0]);
|
||||
if (tot != nullptr && GetDiskFreeSpace(root, &spc, &bps, &nfc, &tnc)) {
|
||||
*tot = ((spc * bps) * (uint64)nfc);
|
||||
retval = true;
|
||||
}
|
||||
ULARGE_INTEGER bytes_free;
|
||||
bool retval = GetDiskFreeSpaceEx(OTTD2FS(path), &bytes_free, nullptr, nullptr);
|
||||
if (retval) *tot = bytes_free.QuadPart;
|
||||
|
||||
SetErrorMode(sem); // reset previous setting
|
||||
return retval;
|
||||
@@ -392,7 +385,7 @@ static INT_PTR CALLBACK HelpDialogFunc(HWND wnd, UINT msg, WPARAM wParam, LPARAM
|
||||
*q = '\0';
|
||||
/* We need to put the text in a separate buffer because the default
|
||||
* buffer in OTTD2FS might not be large enough (512 chars). */
|
||||
TCHAR help_msg_buf[8192];
|
||||
wchar_t help_msg_buf[8192];
|
||||
SetDlgItemText(wnd, 11, convert_to_fs(help_msg, help_msg_buf, lengthof(help_msg_buf)));
|
||||
SendDlgItemMessage(wnd, 11, WM_SETFONT, (WPARAM)GetStockObject(ANSI_FIXED_FONT), FALSE);
|
||||
} return TRUE;
|
||||
@@ -426,8 +419,8 @@ void ShowInfo(const char *str)
|
||||
} else {
|
||||
/* We need to put the text in a separate buffer because the default
|
||||
* buffer in OTTD2FS might not be large enough (512 chars). */
|
||||
TCHAR help_msg_buf[8192];
|
||||
MessageBox(GetActiveWindow(), convert_to_fs(str, help_msg_buf, lengthof(help_msg_buf)), _T("OpenTTD"), MB_ICONINFORMATION | MB_OK);
|
||||
wchar_t help_msg_buf[8192];
|
||||
MessageBox(GetActiveWindow(), convert_to_fs(str, help_msg_buf, lengthof(help_msg_buf)), L"OpenTTD", MB_ICONINFORMATION | MB_OK);
|
||||
}
|
||||
MyShowCursor(old);
|
||||
}
|
||||
@@ -445,11 +438,6 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
|
||||
|
||||
CrashLog::InitialiseCrashLog();
|
||||
|
||||
#if defined(UNICODE)
|
||||
/* Check if a win9x user started the win32 version */
|
||||
if (HasBit(GetVersion(), 31)) usererror("This version of OpenTTD doesn't run on windows 95/98/ME.\nPlease download the win9x binary and try again.");
|
||||
#endif
|
||||
|
||||
/* Convert the command line to UTF-8. We need a dedicated buffer
|
||||
* for this because argv[] points into this buffer and this needs to
|
||||
* be available between subsequent calls to FS2OTTD(). */
|
||||
@@ -480,7 +468,7 @@ int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLi
|
||||
|
||||
char *getcwd(char *buf, size_t size)
|
||||
{
|
||||
TCHAR path[MAX_PATH];
|
||||
wchar_t path[MAX_PATH];
|
||||
GetCurrentDirectory(MAX_PATH - 1, path);
|
||||
convert_from_fs(path, buf, size);
|
||||
return buf;
|
||||
@@ -492,9 +480,9 @@ void DetermineBasePaths(const char *exe)
|
||||
{
|
||||
extern std::array<std::string, NUM_SEARCHPATHS> _searchpaths;
|
||||
|
||||
TCHAR path[MAX_PATH];
|
||||
wchar_t path[MAX_PATH];
|
||||
#ifdef WITH_PERSONAL_DIR
|
||||
if (SUCCEEDED(OTTDSHGetFolderPath(nullptr, CSIDL_PERSONAL, nullptr, SHGFP_TYPE_CURRENT, path))) {
|
||||
if (SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_PERSONAL, nullptr, SHGFP_TYPE_CURRENT, path))) {
|
||||
std::string tmp(FS2OTTD(path));
|
||||
AppendPathSeparator(tmp);
|
||||
tmp += PERSONAL_DIR;
|
||||
@@ -508,7 +496,7 @@ void DetermineBasePaths(const char *exe)
|
||||
_searchpaths[SP_PERSONAL_DIR].clear();
|
||||
}
|
||||
|
||||
if (SUCCEEDED(OTTDSHGetFolderPath(nullptr, CSIDL_COMMON_DOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, path))) {
|
||||
if (SUCCEEDED(SHGetFolderPath(nullptr, CSIDL_COMMON_DOCUMENTS, nullptr, SHGFP_TYPE_CURRENT, path))) {
|
||||
std::string tmp(FS2OTTD(path));
|
||||
AppendPathSeparator(tmp);
|
||||
tmp += PERSONAL_DIR;
|
||||
@@ -530,8 +518,8 @@ void DetermineBasePaths(const char *exe)
|
||||
_searchpaths[SP_WORKING_DIR] = cwd_s;
|
||||
} else {
|
||||
/* Use the folder of the config file as working directory. */
|
||||
TCHAR config_dir[MAX_PATH];
|
||||
_tcsncpy(path, convert_to_fs(_config_file.c_str(), path, lengthof(path)), lengthof(path));
|
||||
wchar_t config_dir[MAX_PATH];
|
||||
wcsncpy(path, convert_to_fs(_config_file.c_str(), path, lengthof(path)), lengthof(path));
|
||||
if (!GetFullPathName(path, lengthof(config_dir), config_dir, nullptr)) {
|
||||
DEBUG(misc, 0, "GetFullPathName failed (%lu)\n", GetLastError());
|
||||
_searchpaths[SP_WORKING_DIR].clear();
|
||||
@@ -548,8 +536,8 @@ void DetermineBasePaths(const char *exe)
|
||||
DEBUG(misc, 0, "GetModuleFileName failed (%lu)\n", GetLastError());
|
||||
_searchpaths[SP_BINARY_DIR].clear();
|
||||
} else {
|
||||
TCHAR exec_dir[MAX_PATH];
|
||||
_tcsncpy(path, convert_to_fs(exe, path, lengthof(path)), lengthof(path));
|
||||
wchar_t exec_dir[MAX_PATH];
|
||||
wcsncpy(path, convert_to_fs(exe, path, lengthof(path)), lengthof(path));
|
||||
if (!GetFullPathName(path, lengthof(exec_dir), exec_dir, nullptr)) {
|
||||
DEBUG(misc, 0, "GetFullPathName failed (%lu)\n", GetLastError());
|
||||
_searchpaths[SP_BINARY_DIR].clear();
|
||||
@@ -582,17 +570,6 @@ bool GetClipboardContents(char *buffer, const char *last)
|
||||
CloseClipboard();
|
||||
|
||||
if (out_len == 0) return false;
|
||||
#if !defined(UNICODE)
|
||||
} else if (IsClipboardFormatAvailable(CF_TEXT)) {
|
||||
OpenClipboard(nullptr);
|
||||
cbuf = GetClipboardData(CF_TEXT);
|
||||
|
||||
ptr = (const char*)GlobalLock(cbuf);
|
||||
strecpy(buffer, FS2OTTD(ptr), last);
|
||||
|
||||
GlobalUnlock(cbuf);
|
||||
CloseClipboard();
|
||||
#endif /* UNICODE */
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
@@ -602,10 +579,7 @@ bool GetClipboardContents(char *buffer, const char *last)
|
||||
|
||||
|
||||
/**
|
||||
* Convert to OpenTTD's encoding from that of the local environment.
|
||||
* When the project is built in UNICODE, the system codepage is irrelevant and
|
||||
* the input string is wide. In ANSI mode, the string is in the
|
||||
* local codepage which we'll convert to wide-char, and then to UTF-8.
|
||||
* Convert to OpenTTD's encoding from wide characters.
|
||||
* OpenTTD internal encoding is UTF8.
|
||||
* The returned value's contents can only be guaranteed until the next call to
|
||||
* this function. So if the value is needed for anything else, use convert_from_fs
|
||||
@@ -614,17 +588,14 @@ bool GetClipboardContents(char *buffer, const char *last)
|
||||
* @see the current code-page comes from video\win32_v.cpp, event-notification
|
||||
* WM_INPUTLANGCHANGE
|
||||
*/
|
||||
const char *FS2OTTD(const TCHAR *name)
|
||||
const char *FS2OTTD(const wchar_t *name)
|
||||
{
|
||||
TLSData *data = GetTLSData();
|
||||
return convert_from_fs(name, data->utf8_buf, lengthof(data->utf8_buf));
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert from OpenTTD's encoding to that of the local environment.
|
||||
* When the project is built in UNICODE the system codepage is irrelevant and
|
||||
* the converted string is wide. In ANSI mode, the UTF8 string is converted
|
||||
* to multi-byte.
|
||||
* Convert from OpenTTD's encoding to wide characters.
|
||||
* OpenTTD internal encoding is UTF8.
|
||||
* The returned value's contents can only be guaranteed until the next call to
|
||||
* this function. So if the value is needed for anything else, use convert_from_fs
|
||||
@@ -632,7 +603,7 @@ const char *FS2OTTD(const TCHAR *name)
|
||||
* @param console_cp convert to the console encoding instead of the normal system encoding.
|
||||
* @return pointer to the converted string; if failed string is of zero-length
|
||||
*/
|
||||
const TCHAR *OTTD2FS(const char *name, bool console_cp)
|
||||
const wchar_t *OTTD2FS(const char *name, bool console_cp)
|
||||
{
|
||||
TLSData *data = GetTLSData();
|
||||
return convert_to_fs(name, data->system_buf, lengthof(data->system_buf), console_cp);
|
||||
@@ -641,27 +612,15 @@ const TCHAR *OTTD2FS(const char *name, bool console_cp)
|
||||
|
||||
/**
|
||||
* Convert to OpenTTD's encoding from that of the environment in
|
||||
* UNICODE. OpenTTD encoding is UTF8, local is wide
|
||||
* UNICODE. OpenTTD encoding is UTF8, local is wide.
|
||||
* @param name pointer to a valid string that will be converted
|
||||
* @param utf8_buf pointer to a valid buffer that will receive the converted string
|
||||
* @param buflen length in characters of the receiving buffer
|
||||
* @return pointer to utf8_buf. If conversion fails the string is of zero-length
|
||||
*/
|
||||
char *convert_from_fs(const TCHAR *name, char *utf8_buf, size_t buflen)
|
||||
char *convert_from_fs(const wchar_t *name, char *utf8_buf, size_t buflen)
|
||||
{
|
||||
#if defined(UNICODE)
|
||||
const WCHAR *wide_buf = name;
|
||||
#else
|
||||
/* Convert string from the local codepage to UTF-16. */
|
||||
int wide_len = MultiByteToWideChar(CP_ACP, 0, name, -1, nullptr, 0);
|
||||
if (wide_len == 0) {
|
||||
utf8_buf[0] = '\0';
|
||||
return utf8_buf;
|
||||
}
|
||||
|
||||
WCHAR *wide_buf = AllocaM(WCHAR, wide_len);
|
||||
MultiByteToWideChar(CP_ACP, 0, name, -1, wide_buf, wide_len);
|
||||
#endif
|
||||
const wchar_t *wide_buf = name;
|
||||
|
||||
/* Convert UTF-16 string to UTF-8. */
|
||||
int len = WideCharToMultiByte(CP_UTF8, 0, wide_buf, -1, utf8_buf, (int)buflen, nullptr, nullptr);
|
||||
@@ -673,7 +632,7 @@ char *convert_from_fs(const TCHAR *name, char *utf8_buf, size_t buflen)
|
||||
|
||||
/**
|
||||
* Convert from OpenTTD's encoding to that of the environment in
|
||||
* UNICODE. OpenTTD encoding is UTF8, local is wide
|
||||
* UNICODE. OpenTTD encoding is UTF8, local is wide.
|
||||
* @param name pointer to a valid string that will be converted
|
||||
* @param system_buf pointer to a valid wide-char buffer that will receive the
|
||||
* converted string
|
||||
@@ -681,93 +640,14 @@ char *convert_from_fs(const TCHAR *name, char *utf8_buf, size_t buflen)
|
||||
* @param console_cp convert to the console encoding instead of the normal system encoding.
|
||||
* @return pointer to system_buf. If conversion fails the string is of zero-length
|
||||
*/
|
||||
TCHAR *convert_to_fs(const char *name, TCHAR *system_buf, size_t buflen, bool console_cp)
|
||||
wchar_t *convert_to_fs(const char *name, wchar_t *system_buf, size_t buflen, bool console_cp)
|
||||
{
|
||||
#if defined(UNICODE)
|
||||
int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, system_buf, (int)buflen);
|
||||
if (len == 0) system_buf[0] = '\0';
|
||||
#else
|
||||
int len = MultiByteToWideChar(CP_UTF8, 0, name, -1, nullptr, 0);
|
||||
if (len == 0) {
|
||||
system_buf[0] = '\0';
|
||||
return system_buf;
|
||||
}
|
||||
|
||||
WCHAR *wide_buf = AllocaM(WCHAR, len);
|
||||
MultiByteToWideChar(CP_UTF8, 0, name, -1, wide_buf, len);
|
||||
|
||||
len = WideCharToMultiByte(console_cp ? CP_OEMCP : CP_ACP, 0, wide_buf, len, system_buf, (int)buflen, nullptr, nullptr);
|
||||
if (len == 0) system_buf[0] = '\0';
|
||||
#endif
|
||||
|
||||
return system_buf;
|
||||
}
|
||||
|
||||
/**
|
||||
* Our very own SHGetFolderPath function for support of windows operating
|
||||
* systems that don't have this function (eg Win9x, etc.). We try using the
|
||||
* native function, and if that doesn't exist we will try a more crude approach
|
||||
* of environment variables and hope for the best
|
||||
*/
|
||||
HRESULT OTTDSHGetFolderPath(HWND hwnd, int csidl, HANDLE hToken, DWORD dwFlags, LPTSTR pszPath)
|
||||
{
|
||||
static HRESULT (WINAPI *SHGetFolderPath)(HWND, int, HANDLE, DWORD, LPTSTR) = nullptr;
|
||||
static bool first_time = true;
|
||||
|
||||
/* We only try to load the library one time; if it fails, it fails */
|
||||
if (first_time) {
|
||||
#if defined(UNICODE)
|
||||
# define W(x) x "W"
|
||||
#else
|
||||
# define W(x) x "A"
|
||||
#endif
|
||||
/* The function lives in shell32.dll for all current Windows versions, but it first started to appear in SHFolder.dll. */
|
||||
if (!LoadLibraryList((Function*)&SHGetFolderPath, "shell32.dll\0" W("SHGetFolderPath") "\0\0")) {
|
||||
if (!LoadLibraryList((Function*)&SHGetFolderPath, "SHFolder.dll\0" W("SHGetFolderPath") "\0\0")) {
|
||||
DEBUG(misc, 0, "Unable to load " W("SHGetFolderPath") "from either shell32.dll or SHFolder.dll");
|
||||
}
|
||||
}
|
||||
#undef W
|
||||
first_time = false;
|
||||
}
|
||||
|
||||
if (SHGetFolderPath != nullptr) return SHGetFolderPath(hwnd, csidl, hToken, dwFlags, pszPath);
|
||||
|
||||
/* SHGetFolderPath doesn't exist, try a more conservative approach,
|
||||
* eg environment variables. This is only included for legacy modes
|
||||
* MSDN says: that 'pszPath' is a "Pointer to a null-terminated string of
|
||||
* length MAX_PATH which will receive the path" so let's assume that
|
||||
* Windows 95 with Internet Explorer 5.0, Windows 98 with Internet Explorer 5.0,
|
||||
* Windows 98 Second Edition (SE), Windows NT 4.0 with Internet Explorer 5.0,
|
||||
* Windows NT 4.0 with Service Pack 4 (SP4) */
|
||||
{
|
||||
DWORD ret;
|
||||
switch (csidl) {
|
||||
case CSIDL_FONTS: // Get the system font path, eg %WINDIR%\Fonts
|
||||
ret = GetEnvironmentVariable(_T("WINDIR"), pszPath, MAX_PATH);
|
||||
if (ret == 0) break;
|
||||
_tcsncat(pszPath, _T("\\Fonts"), MAX_PATH);
|
||||
|
||||
return (HRESULT)0;
|
||||
|
||||
case CSIDL_PERSONAL:
|
||||
case CSIDL_COMMON_DOCUMENTS: {
|
||||
HKEY key;
|
||||
if (RegOpenKeyEx(csidl == CSIDL_PERSONAL ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE, REGSTR_PATH_SPECIAL_FOLDERS, 0, KEY_READ, &key) != ERROR_SUCCESS) break;
|
||||
DWORD len = MAX_PATH;
|
||||
ret = RegQueryValueEx(key, csidl == CSIDL_PERSONAL ? _T("Personal") : _T("Common Documents"), nullptr, nullptr, (LPBYTE)pszPath, &len);
|
||||
RegCloseKey(key);
|
||||
if (ret == ERROR_SUCCESS) return (HRESULT)0;
|
||||
break;
|
||||
}
|
||||
|
||||
/* XXX - other types to go here when needed... */
|
||||
}
|
||||
}
|
||||
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
/** Determine the current user's locale. */
|
||||
const char *GetCurrentLocale(const char *)
|
||||
{
|
||||
@@ -824,7 +704,7 @@ int OTTDStringCompare(const char *s1, const char *s2)
|
||||
#endif
|
||||
|
||||
if (first_time) {
|
||||
_CompareStringEx = (PFNCOMPARESTRINGEX)GetProcAddress(GetModuleHandle(_T("Kernel32")), "CompareStringEx");
|
||||
_CompareStringEx = (PFNCOMPARESTRINGEX)GetProcAddress(GetModuleHandle(L"Kernel32"), "CompareStringEx");
|
||||
first_time = false;
|
||||
}
|
||||
|
||||
@@ -845,7 +725,7 @@ int OTTDStringCompare(const char *s1, const char *s2)
|
||||
}
|
||||
}
|
||||
|
||||
TCHAR s1_buf[512], s2_buf[512];
|
||||
wchar_t s1_buf[512], s2_buf[512];
|
||||
convert_to_fs(s1, s1_buf, lengthof(s1_buf));
|
||||
convert_to_fs(s2, s2_buf, lengthof(s2_buf));
|
||||
|
||||
@@ -888,6 +768,38 @@ int GetCurrentThreadName(char *str, const char *last)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Is the current Windows version Vista or later?
|
||||
* @return True if the current Windows is Vista or later.
|
||||
*/
|
||||
bool IsWindowsVistaOrGreater()
|
||||
{
|
||||
typedef BOOL (WINAPI * LPVERIFYVERSIONINFO)(LPOSVERSIONINFOEX, DWORD, DWORDLONG);
|
||||
typedef ULONGLONG (NTAPI * LPVERSETCONDITIONMASK)(ULONGLONG, DWORD, BYTE);
|
||||
#ifdef UNICODE
|
||||
static LPVERIFYVERSIONINFO _VerifyVersionInfo = (LPVERIFYVERSIONINFO)GetProcAddress(GetModuleHandle(_T("Kernel32")), "VerifyVersionInfoW");
|
||||
#else
|
||||
static LPVERIFYVERSIONINFO _VerifyVersionInfo = (LPVERIFYVERSIONINFO)GetProcAddress(GetModuleHandle(_T("Kernel32")), "VerifyVersionInfoA");
|
||||
#endif
|
||||
static LPVERSETCONDITIONMASK _VerSetConditionMask = (LPVERSETCONDITIONMASK)GetProcAddress(GetModuleHandle(_T("Kernel32")), "VerSetConditionMask");
|
||||
|
||||
if (_VerifyVersionInfo != nullptr && _VerSetConditionMask != nullptr) {
|
||||
OSVERSIONINFOEX osvi = { sizeof(osvi), 0, 0, 0, 0, {0}, 0, 0 };
|
||||
DWORDLONG dwlConditionMask = 0;
|
||||
dwlConditionMask = _VerSetConditionMask(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
|
||||
dwlConditionMask = _VerSetConditionMask(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
|
||||
dwlConditionMask = _VerSetConditionMask(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
|
||||
|
||||
osvi.dwMajorVersion = 6;
|
||||
osvi.dwMinorVersion = 0;
|
||||
osvi.wServicePackMajor = 0;
|
||||
|
||||
return _VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR, dwlConditionMask) != FALSE;
|
||||
} else {
|
||||
return LOBYTE(GetVersion()) >= 6;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef _MSC_VER
|
||||
/* Based on code from MSDN: https://msdn.microsoft.com/en-us/library/xcb2z8hs.aspx */
|
||||
const DWORD MS_VC_EXCEPTION = 0x406D1388;
|
||||
|
Reference in New Issue
Block a user