diff --git a/project/VS2010Express/XBMC.vcxproj b/project/VS2010Express/XBMC.vcxproj index 7e873102bb5c6..dd310fdd45307 100644 --- a/project/VS2010Express/XBMC.vcxproj +++ b/project/VS2010Express/XBMC.vcxproj @@ -1460,6 +1460,7 @@ + Create Create @@ -2147,6 +2148,7 @@ + diff --git a/project/VS2010Express/XBMC.vcxproj.filters b/project/VS2010Express/XBMC.vcxproj.filters index 9f19d9fc19791..840fb87be2588 100644 --- a/project/VS2010Express/XBMC.vcxproj.filters +++ b/project/VS2010Express/XBMC.vcxproj.filters @@ -3092,6 +3092,9 @@ utils + + win32 + network\httprequesthandler @@ -6021,6 +6024,9 @@ cores + + win32 + filesystem diff --git a/xbmc/Application.cpp b/xbmc/Application.cpp index 840d0fcbca111..87b19e18a7fb2 100644 --- a/xbmc/Application.cpp +++ b/xbmc/Application.cpp @@ -734,6 +734,10 @@ bool CApplication::Create() return false; } +#ifdef TARGET_WINDOWS + CWIN32Util::SetThreadLocalLocale(true); // enable independent locale for each thread, see https://connect.microsoft.com/VisualStudio/feedback/details/794122 +#endif // TARGET_WINDOWS + // start the AudioEngine if (!CAEFactory::StartEngine()) { diff --git a/xbmc/LangInfo.cpp b/xbmc/LangInfo.cpp index 208cd2ccd6a77..9589a5f7f31d5 100644 --- a/xbmc/LangInfo.cpp +++ b/xbmc/LangInfo.cpp @@ -198,6 +198,7 @@ void CLangInfo::CRegion::SetGlobalLocale() strLocale = "C"; } + g_langInfo.m_locale = current_locale; // TODO: move to CLangInfo class locale::global(current_locale); #endif g_charsetConverter.resetSystemCharset(); @@ -421,6 +422,8 @@ void CLangInfo::SetDefaults() // Set the default region, we may be unable to load langinfo.xml m_currentRegion=&m_defaultRegion; + + m_locale = std::locale::classic(); m_languageCodeGeneral = "eng"; } diff --git a/xbmc/LangInfo.h b/xbmc/LangInfo.h index aa499fde8af12..6c9898c41fde2 100644 --- a/xbmc/LangInfo.h +++ b/xbmc/LangInfo.h @@ -25,6 +25,7 @@ #include #include #include +#include #ifdef TARGET_WINDOWS #ifdef GetDateFormat @@ -130,6 +131,9 @@ class CLangInfo : public ISettingCallback void SetCurrentRegion(const std::string& strName); const std::string& GetCurrentRegion() const; + const std::locale& GetLocale() const + { return m_locale; } + static bool CheckLanguage(const std::string& language); static void LoadTokens(const TiXmlNode* pTokens, std::vector& vecTokens); @@ -179,6 +183,7 @@ class CLangInfo : public ISettingCallback MAPREGIONS m_regions; CRegion* m_currentRegion; // points to the current region CRegion m_defaultRegion; // default, will be used if no region available via langinfo.xml + std::locale m_locale; // current locale, matching GUI settings std::string m_audioLanguage; std::string m_subtitleLanguage; diff --git a/xbmc/threads/platform/win/ThreadImpl.cpp b/xbmc/threads/platform/win/ThreadImpl.cpp index 76b60fa83e4c0..843839fd7537d 100644 --- a/xbmc/threads/platform/win/ThreadImpl.cpp +++ b/xbmc/threads/platform/win/ThreadImpl.cpp @@ -19,18 +19,22 @@ */ #include +#include #include "threads/platform/win/Win32Exception.h" +#include "../../win32/WIN32Util.h" void CThread::SpawnThread(unsigned stacksize) { // Create in the suspended state, so that no matter the thread priorities and scheduled order, the handle will be assigned // before the new thread exits. - m_ThreadOpaque.handle = CreateThread(NULL, stacksize, (LPTHREAD_START_ROUTINE)&staticThread, this, CREATE_SUSPENDED, &m_ThreadId); + unsigned threadId; + m_ThreadOpaque.handle = (HANDLE)_beginthreadex(NULL, stacksize, &staticThread, this, CREATE_SUSPENDED, &threadId); if (m_ThreadOpaque.handle == NULL) { if (logger) logger->Log(LOGERROR, "%s - fatal error %d creating thread", __FUNCTION__, GetLastError()); return; } + m_ThreadId = threadId; if (ResumeThread(m_ThreadOpaque.handle) == -1) if (logger) logger->Log(LOGERROR, "%s - fatal error %d resuming thread", __FUNCTION__, GetLastError()); @@ -70,6 +74,8 @@ void CThread::SetThreadInfo() { } + CWIN32Util::SetThreadLocalLocale(true); // avoid crashing with setlocale(), see https://connect.microsoft.com/VisualStudio/feedback/details/794122 + win32_exception::install_handler(); } diff --git a/xbmc/threads/platform/win/ThreadImpl.h b/xbmc/threads/platform/win/ThreadImpl.h index ca423e737cd8d..b670df3cb239b 100644 --- a/xbmc/threads/platform/win/ThreadImpl.h +++ b/xbmc/threads/platform/win/ThreadImpl.h @@ -30,7 +30,7 @@ struct threadOpaque typedef DWORD ThreadIdentifier; typedef threadOpaque ThreadOpaque; -typedef DWORD THREADFUNC; +#define THREADFUNC unsigned __stdcall namespace XbmcThreads { diff --git a/xbmc/utils/JSONVariantWriter.cpp b/xbmc/utils/JSONVariantWriter.cpp index 61fdd4cb173ca..0d0a7aa32f8a0 100644 --- a/xbmc/utils/JSONVariantWriter.cpp +++ b/xbmc/utils/JSONVariantWriter.cpp @@ -33,13 +33,23 @@ string CJSONVariantWriter::Write(const CVariant &value, bool compact) yajl_gen_config(g, yajl_gen_indent_string, "\t"); // Set locale to classic ("C") to ensure valid JSON numbers +#ifndef TARGET_WINDOWS const char *currentLocale = setlocale(LC_NUMERIC, NULL); std::string backupLocale; - if (currentLocale != NULL) + if (currentLocale != NULL && (currentLocale[0] != 'C' || currentLocale[1] != 0)) { backupLocale = currentLocale; setlocale(LC_NUMERIC, "C"); } +#else // TARGET_WINDOWS + const wchar_t* const currentLocale = _wsetlocale(LC_NUMERIC, NULL); + std::wstring backupLocale; + if (currentLocale != NULL && (currentLocale[0] != L'C' || currentLocale[1] != 0)) + { + backupLocale = currentLocale; + _wsetlocale(LC_NUMERIC, L"C"); + } +#endif // TARGET_WINDOWS if (InternalWrite(g, value)) { @@ -51,8 +61,13 @@ string CJSONVariantWriter::Write(const CVariant &value, bool compact) } // Re-set locale to what it was before using yajl +#ifndef TARGET_WINDOWS if (!backupLocale.empty()) setlocale(LC_NUMERIC, backupLocale.c_str()); +#else // TARGET_WINDOWS + if (!backupLocale.empty()) + _wsetlocale(LC_NUMERIC, backupLocale.c_str()); +#endif // TARGET_WINDOWS yajl_gen_clear(g); yajl_gen_free(g); diff --git a/xbmc/utils/StringUtils.cpp b/xbmc/utils/StringUtils.cpp index 5963bf3d1fba1..6950bf66f5930 100644 --- a/xbmc/utils/StringUtils.cpp +++ b/xbmc/utils/StringUtils.cpp @@ -32,6 +32,7 @@ #include "StringUtils.h" #include "utils/fstrcmp.h" #include "Util.h" +#include "LangInfo.h" #include #include @@ -740,7 +741,7 @@ int64_t StringUtils::AlphaNumericCompare(const wchar_t *left, const wchar_t *rig wchar_t *ld, *rd; wchar_t lc, rc; int64_t lnum, rnum; - const collate& coll = use_facet< collate >( locale() ); + const collate& coll = use_facet< collate >( g_langInfo.GetLocale() ); int cmp_res = 0; while (*l != 0 && *r != 0) { diff --git a/xbmc/win32/WIN32Util.cpp b/xbmc/win32/WIN32Util.cpp index 662b6663f19a3..89755a1a96517 100644 --- a/xbmc/win32/WIN32Util.cpp +++ b/xbmc/win32/WIN32Util.cpp @@ -42,6 +42,7 @@ #include "utils/Environment.h" #include "utils/URIUtils.h" #include "utils/StringUtils.h" +#include "win32/crts_caller.h" #include @@ -53,6 +54,8 @@ "special://xbmc/system/webserver/;" \ "special://xbmc/" +#include + extern HWND g_hWnd; using namespace std; @@ -1606,3 +1609,10 @@ std::string CWIN32Util::WUSysMsg(DWORD dwError) else return StringUtils::Format("Unknown error (0x%X)", dwError); } + +bool CWIN32Util::SetThreadLocalLocale(bool enable /* = true */) +{ + const int param = enable ? _ENABLE_PER_THREAD_LOCALE : _DISABLE_PER_THREAD_LOCALE; + return CALL_IN_CRTS(_configthreadlocale, param) != -1; +} + diff --git a/xbmc/win32/WIN32Util.h b/xbmc/win32/WIN32Util.h index 572823ba266c2..55b1ee8efb039 100644 --- a/xbmc/win32/WIN32Util.h +++ b/xbmc/win32/WIN32Util.h @@ -94,6 +94,8 @@ class CWIN32Util static bool IsUsbDevice(const std::wstring &strWdrive); static std::string WUSysMsg(DWORD dwError); + + static bool SetThreadLocalLocale(bool enable = true); private: static DEVINST GetDrivesDevInstByDiskNumber(long DiskNumber); }; diff --git a/xbmc/win32/crts_caller.cpp b/xbmc/win32/crts_caller.cpp new file mode 100644 index 0000000000000..a30e0e593d444 --- /dev/null +++ b/xbmc/win32/crts_caller.cpp @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2015 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +/** + * \file win32\crts_caller.h + * \brief Implements crts_caller class for calling same function for all loaded CRTs. + * \author Karlson2k + */ + +#include "crts_caller.h" +#ifndef WIN32_LEAN_AND_MEAN +#define WIN32_LEAN_AND_MEAN 1 +#endif // WIN32_LEAN_AND_MEAN +#include +#include + +namespace win32_utils +{ + +static const wchar_t* const s_listOfCrts[] = +{ + { L"msvcrt.dll" }, // Visual Studio 6.0 / MinGW[-w64] + { L"msvcr70.dll" }, // Visual Studio 2002 + { L"msvcr71.dll" }, // Visual Studio 2003 + { L"msvcr80.dll" }, // Visual Studio 2005 + { L"msvcr90.dll" }, // Visual Studio 2008 +#ifdef _DEBUG + { L"msvcr90d.dll" }, // Visual Studio 2008 (debug) +#endif + { L"msvcr100.dll" }, // Visual Studio 2010 +#ifdef _DEBUG + { L"msvcr100d.dll" },// Visual Studio 2010 (debug) +#endif + { L"msvcr110.dll" }, // Visual Studio 2012 +#ifdef _DEBUG + { L"msvcr110d.dll" },// Visual Studio 2012 (debug) +#endif + { L"msvcr120.dll" }, // Visual Studio 2013 +#ifdef _DEBUG + { L"msvcr120d.dll" },// Visual Studio 2013 (debug) +#endif +}; + +std::vector crts_caller::getCrtNames() +{ + return std::vector(s_listOfCrts, s_listOfCrts + (sizeof(s_listOfCrts) / sizeof(s_listOfCrts[0]))); +} + + +crts_caller::crts_caller(const char* func_name) +{ + assert(func_name); + assert(func_name[0]); + if (func_name == NULL) + return; + + for (const wchar_t* const crtName : s_listOfCrts) + { + HMODULE hCrt = NULL; + if (!GetModuleHandleExW(0, crtName, &hCrt) || hCrt == NULL) // Flag 0 ensures that CRL will not be unloaded while we are using it here + continue; // Module not loaded + + void* func_ptr = GetProcAddress(hCrt, func_name); + if (func_ptr != NULL) + { + m_crts.push_back(hCrt); + m_funcPointers.push_back(func_ptr); + } + else + FreeLibrary(hCrt); // this CRT will not be used + } +} + +crts_caller::~crts_caller() +{ + for (void* hCrt : m_crts) + FreeLibrary((HMODULE)hCrt); +} + +} diff --git a/xbmc/win32/crts_caller.h b/xbmc/win32/crts_caller.h new file mode 100644 index 0000000000000..6fe06722a3a3c --- /dev/null +++ b/xbmc/win32/crts_caller.h @@ -0,0 +1,74 @@ +#pragma once +/* + * Copyright (C) 2015 Team Kodi + * http://kodi.tv + * + * This Program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2, or (at your option) + * any later version. + * + * This Program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with XBMC; see the file COPYING. If not, see + * . + * + */ + +/** + * \file win32\crts_caller.h + * \brief Declares crts_caller class for calling same function for all loaded CRTs. + * \author Karlson2k + */ + +#include +#include + +#include + +namespace win32_utils +{ + +class crts_caller +{ +public: + crts_caller(const char* func_name); + const std::vector& get_pointers(void) + { return m_funcPointers; } + ~crts_caller(); + + template + static typename ret_type call_in_all_crts(const char* func_name, ret_type(*cur_fnc_ptr) (param_types...), param_types... params) + { + typedef ret_type(*ptr_type)(param_types...); + + if (cur_fnc_ptr == NULL) + return (ret_type)0; // cur_fnc_ptr must point to process default CRT function + + crts_caller crts(func_name); + for (void* func_ptr : crts.m_funcPointers) + { + ptr_type func = (ptr_type)func_ptr; + if (func != cur_fnc_ptr) + (void)func(params...); // ignoring result of function call + } + + return cur_fnc_ptr(params...); // return result of calling process's CRT function + } + + static std::vector getCrtNames(); +private: + std::vector m_funcPointers; + std::vector m_crts; // actually contains HMODULE +}; + +// Call function in all loaded CRTs +// Function must have same return type and same parameters in all CRTs +#define CALL_IN_CRTS(function,...) ::win32_utils::crts_caller::call_in_all_crts(BOOST_PP_STRINGIZE(function),&(function),##__VA_ARGS__) + + +}