diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..a9c528c
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,16 @@
+# EditorConfig is awesome: https://EditorConfig.org
+
+
+# 代码文件尽可能的使用 UTF-8,因为英语字符占据大多数。
+# 数据文件比如 XML,json统一缩进 2,因为外部规范往往如此
+
+[*]
+end_of_line = crlf
+insert_final_newline = true
+charset = utf-8-bom
+indent_style = space
+indent_size = 4
+
+[*.{vcxproj,xml}]
+indent_style = space
+indent_size = 2
diff --git a/Build.proj b/Build.proj
index a98fad7..0c835f8 100644
--- a/Build.proj
+++ b/Build.proj
@@ -76,9 +76,9 @@
-
-
-
+
+
+
@@ -210,4 +210,4 @@
WorkingDirectory="$(MSBuildThisFileDirectory)Tools\$(WindowsTargetPlatformMinVersion)\$(Platform)"
Condition="Exists('$(MSBuildThisFileDirectory)Tools\$(WindowsTargetPlatformMinVersion)\$(Platform)\Build_libucrt_shared.cmd')"/>
-
\ No newline at end of file
+
diff --git a/Sources/crt/vcruntime/ltl/threads.cpp b/Sources/crt/vcruntime/ltl/threads.cpp
new file mode 100644
index 0000000..fbd366c
--- /dev/null
+++ b/Sources/crt/vcruntime/ltl/threads.cpp
@@ -0,0 +1,901 @@
+#include
+
+#include
+
+#include
+#include
+#include
+#include
+#include
+
+#include
+
+#include "framework.h"
+
+// 模仿 vctools\crt\vcruntime\src\internal\threads.cpp 实现
+
+#pragma warning(disable: 28251)
+
+namespace VC_LTL
+{
+ constexpr auto kDtorTableBufferLength = 0x400;
+
+ class srwlock_shared_guard
+ {
+ private:
+ SRWLOCK* pLock;
+
+ public:
+ srwlock_shared_guard(SRWLOCK& _Lock)
+ : pLock(&_Lock)
+ {
+ AcquireSRWLockShared(pLock);
+ }
+
+ srwlock_shared_guard(const srwlock_shared_guard&) = delete;
+
+ ~srwlock_shared_guard()
+ {
+ Reset();
+ }
+
+ void Reset()
+ {
+ if (pLock)
+ {
+ ReleaseSRWLockShared(pLock);
+ pLock = nullptr;
+ }
+ }
+
+ void Attach(SRWLOCK* _pLock)
+ {
+ if (pLock == _pLock)
+ return;
+
+ if (pLock)
+ ReleaseSRWLockShared(pLock);
+
+ pLock = _pLock;
+ AcquireSRWLockShared(pLock);
+ }
+
+ void operator=(std::nullptr_t)
+ {
+ Reset();
+ }
+
+ void operator=(SRWLOCK& _Lock)
+ {
+ Attach(&_Lock);
+ }
+ };
+
+ class srwlock_guard
+ {
+ private:
+ SRWLOCK* pLock;
+
+ public:
+ srwlock_guard(SRWLOCK& _Lock)
+ : pLock(&_Lock)
+ {
+ AcquireSRWLockExclusive(pLock);
+ }
+
+ srwlock_guard(const srwlock_guard&) = delete;
+
+ ~srwlock_guard()
+ {
+ ReleaseSRWLockExclusive(pLock);
+ }
+ };
+
+ struct tss_ptd
+ {
+ tss_ptd* next;
+ tss_ptd* prev;
+ void* data[kDtorTableBufferLength];
+ bool tss_dtor_used;
+ };
+
+ class tss_global_data_t
+ {
+ private:
+ // 0
+ SRWLOCK lock = SRWLOCK_INIT;
+ // 0x4
+ DWORD tss_ptd_idx = TLS_OUT_OF_INDEXES;
+ // 0x8
+ tss_dtor_t* dtor_table = nullptr;
+ // 0xC
+ tss_ptd* ptd_list = nullptr;
+ // 0x10
+ uint32_t last_idx = 0;
+
+ public:
+ constexpr tss_global_data_t() = default;
+
+ tss_global_data_t(const tss_global_data_t&) = delete;
+
+ int create(tss_t* _Key, tss_dtor_t _Dtor)
+ {
+ if (!_Dtor)
+ _Dtor = noop_dtor;
+
+ int _iResult = thrd_success;
+ AcquireSRWLockExclusive(&lock);
+ do
+ {
+ if (!dtor_table)
+ {
+ dtor_table = static_cast(calloc(kDtorTableBufferLength, sizeof(tss_dtor_t)));
+ if (!dtor_table)
+ {
+ _iResult = thrd_error;
+ break;
+ }
+ }
+
+ if (tss_ptd_idx == TLS_OUT_OF_INDEXES)
+ {
+ tss_ptd_idx = TlsAlloc();
+ if (tss_ptd_idx == TLS_OUT_OF_INDEXES)
+ {
+ free(dtor_table);
+ dtor_table = nullptr;
+ _iResult = thrd_error;
+ break;
+ }
+ }
+
+ for (; dtor_table[last_idx];)
+ {
+ last_idx = (last_idx + 1) % kDtorTableBufferLength;
+ if (last_idx == 0)
+ {
+ // dtor_table已经满了
+ _iResult = thrd_error;
+ break;
+ }
+ }
+
+ dtor_table[last_idx] = _Dtor;
+ _Key->_Idx = last_idx;
+ } while (false);
+ ReleaseSRWLockExclusive(&lock);
+ return _iResult;
+ }
+
+ void tss_delete(tss_t _Key)
+ {
+ AcquireSRWLockExclusive(&lock);
+
+ _ASSERT_EXPR(dtor_table, "tss dtor table not allocated.");
+ _ASSERT_EXPR(dtor_table[_Key._Idx] != nullptr, "tss dtor table indicates delete called on nonexistant tss index.");
+
+ dtor_table[_Key._Idx] = nullptr;
+
+ for (auto _pItem = ptd_list; _pItem; _pItem = _pItem->next)
+ {
+ _pItem->data[_Key._Idx] = nullptr;
+ }
+
+ ReleaseSRWLockExclusive(&lock);
+ }
+
+ void* get(tss_t _Key)
+ {
+ void* _pValue = nullptr;
+
+ AcquireSRWLockShared(&lock);
+ _ASSERT_EXPR(dtor_table, "tss dtor table not allocated.");
+ _ASSERT_EXPR(dtor_table[_Key._Idx] != nullptr, "tss dtor table indicates delete called on nonexistant tss index.");
+
+ if (tss_ptd_idx != TLS_OUT_OF_INDEXES)
+ {
+ auto _pTssData = (tss_ptd*)TlsGetValue(tss_ptd_idx);
+ if (_pTssData)
+ {
+ _pValue = _pTssData->data[_Key._Idx];
+ }
+ }
+
+ ReleaseSRWLockShared(&lock);
+ return _pValue;
+ }
+
+ int set(tss_t _Key, void* _Val)
+ {
+ {
+ srwlock_shared_guard _AutoSharedLock(lock);
+ _ASSERT_EXPR(tss_ptd_idx != TLS_OUT_OF_INDEXES, "tss_set called but the tss ptd index was never allcoated.");
+
+ auto _pTssData = (tss_ptd*)TlsGetValue(tss_ptd_idx);
+ _ASSERT_EXPR(GetLastError() == ERROR_SUCCESS, "tss_set could not get the value of the tss ptd.");
+
+ if (_pTssData)
+ {
+ if (dtor_table[_Key._Idx] != noop_dtor)
+ {
+ _pTssData->tss_dtor_used = true;
+ }
+
+ _pTssData->data[_Key._Idx] = _Val;
+ return thrd_success;
+ }
+
+ if (!_Val)
+ {
+ return thrd_success;
+ }
+ }
+
+ auto _pTssData = (tss_ptd*)calloc(1, sizeof(tss_ptd));
+ if (!_pTssData)
+ return thrd_error;
+
+ TlsSetValue(tss_ptd_idx, _pTssData);
+ {
+ srwlock_guard _AutoExclusiveLock(lock);
+
+ _pTssData->next = ptd_list;
+ if (ptd_list)
+ {
+ ptd_list->prev = _pTssData;
+ }
+ ptd_list = _pTssData;
+ }
+
+ srwlock_shared_guard _AutoSharedLock(lock);
+ if (dtor_table[_Key._Idx] != noop_dtor)
+ {
+ _pTssData->tss_dtor_used = true;
+ }
+
+ _pTssData->data[_Key._Idx] = _Val;
+ return thrd_success;
+ }
+
+ void cleanup(void)
+ {
+ tss_ptd* ptd = nullptr;
+ {
+ srwlock_shared_guard _AutoLock(lock);
+ if (!dtor_table)
+ {
+ return;
+ }
+
+ _ASSERTE((tss_ptd_idx != TLS_OUT_OF_INDEXES, "cleanup called but the tss ptd index was never allcoated."));
+
+ ptd = (tss_ptd*)TlsGetValue(tss_ptd_idx);
+ _ASSERTE((GetLastError() == ERROR_SUCCESS, "cleanup could not get the value of the tss ptd."));
+ if (!ptd)
+ {
+ return;
+ }
+
+ if (ptd->tss_dtor_used)
+ {
+ for (int i = 0; i != kDtorTableBufferLength; ++i)
+ {
+ auto _pFun = dtor_table[i];
+ if (_pFun && _pFun != &tss_global_data_t::noop_dtor)
+ {
+ auto _pUserData = ptd->data[i];
+ ptd->data[i] = nullptr;
+ _AutoLock = nullptr;
+ _pFun(_pUserData);
+ _AutoLock = lock;
+ }
+ }
+ }
+ }
+
+ srwlock_guard AutoLock(lock);
+ if (ptd->prev)
+ {
+ ptd->prev = ptd->next;
+ }
+ else
+ {
+ _ASSERTE((ptd_list == ptd, "ptd list is malformed."));
+ ptd_list = ptd->next;
+ }
+ if (ptd->next)
+ {
+ ptd->next->prev = ptd->next;
+ }
+ free(ptd);
+ }
+
+ void cleanup_global(void)
+ {
+ if (dtor_table)
+ {
+ free(dtor_table);
+ dtor_table = reinterpret_cast(-1);
+ }
+
+ if (tss_ptd_idx != TLS_OUT_OF_INDEXES)
+ {
+ const auto _bSuccess = TlsFree(tss_ptd_idx);
+ tss_ptd_idx = TLS_OUT_OF_INDEXES - 1;
+ _ASSERTE((_bSuccess, "failed to free ptd index."));
+ }
+ }
+
+ private:
+ static void __cdecl noop_dtor(void*)
+ {
+ // 什么也不用做
+ }
+ };
+}
+
+using namespace VC_LTL;
+
+extern "C" void __cdecl mtx_destroy(mtx_t * _Mtx)
+{
+ // 什么也不做
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(mtx_destroy);
+
+extern "C" int __cdecl mtx_init(_Out_ mtx_t* _Mtx, int _Type)
+{
+ _Mtx->_Type = _Type;
+ _Mtx->_Ptr = nullptr;
+ _Mtx->_Cv = nullptr;
+ _Mtx->_Owner = 0;
+ _Mtx->_Cnt = 0;
+ return thrd_success;
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(mtx_init);
+
+static int __cdecl mtx_dolock(mtx_t * _Mtx)
+{
+ const auto _uCurrentThreadId = GetCurrentThreadId();
+ if (_uCurrentThreadId == _Mtx->_Owner)
+ {
+ if ((_Mtx->_Type & mtx_recursive) == 0)
+ {
+ abort();
+ }
+ _Mtx->_Cnt++;
+ return thrd_success;
+ }
+
+ for(; _Mtx->_Owner;)
+ {
+ if (!SleepConditionVariableSRW(
+ reinterpret_cast(&_Mtx->_Cv),
+ reinterpret_cast(&_Mtx->_Ptr),
+ INFINITE, 0))
+ {
+ return thrd_error;
+ }
+ }
+
+ _Mtx->_Owner = _uCurrentThreadId;
+ _Mtx->_Cnt = 1;
+ return thrd_success;
+}
+
+extern "C" int __cdecl mtx_lock(mtx_t* _Mtx)
+{
+ AcquireSRWLockExclusive(reinterpret_cast(& _Mtx->_Ptr));
+ auto _iResult = mtx_dolock(_Mtx);
+ ReleaseSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr));
+ return _iResult;
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(mtx_lock);
+
+template
+static int64_t __fastcall duration_timespec_to_ms(const timespec& _Duration)
+{
+ return _Duration.tv_sec * 1000ll + _Duration.tv_nsec / 1000000;
+}
+
+static int64_t __fastcall abstime_timespec_to_duration_ms(const struct _timespec32* __restrict _Ts)
+{
+ _timespec32 _Now;
+ if (_timespec32_get(&_Now, TIME_UTC) == 0)
+ {
+ return INT64_MAX;
+ }
+
+ _Now.tv_sec = _Ts->tv_sec - _Now.tv_sec;
+ _Now.tv_nsec = _Ts->tv_nsec - _Now.tv_nsec;
+ return duration_timespec_to_ms(_Now);
+}
+
+static int64_t __fastcall abstime_timespec_to_duration_ms(const struct _timespec64* __restrict _Ts)
+{
+ _timespec64 _Now;
+ if (_timespec64_get(&_Now, TIME_UTC) == 0)
+ {
+ return INT64_MAX;
+ }
+
+ _Now.tv_sec = _Ts->tv_sec - _Now.tv_sec;
+ _Now.tv_nsec = _Ts->tv_nsec - _Now.tv_nsec;
+ return duration_timespec_to_ms(_Now);
+}
+
+template
+static int __cdecl common_mtx_timedlock(
+ mtx_t* __restrict _Mtx, const timespec* __restrict _Ts)
+{
+ int _iResult = 0;
+ const auto _uCurrentThreadId = GetCurrentThreadId();
+
+ AcquireSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr));
+
+ do
+ {
+ const auto _uCurrentThreadId = GetCurrentThreadId();
+ if (_uCurrentThreadId == _Mtx->_Owner)
+ {
+ if ((_Mtx->_Type & mtx_recursive) == 0)
+ {
+ abort();
+ }
+ _Mtx->_Cnt++;
+ _iResult = thrd_success;
+ break;
+ }
+
+ for (;;)
+ {
+ if (_Mtx->_Owner == 0)
+ {
+ _Mtx->_Owner = _uCurrentThreadId;
+ _Mtx->_Cnt = 1;
+ _iResult = thrd_success;
+ break;
+ }
+ const auto _iDuration = abstime_timespec_to_duration_ms(_Ts);
+ if (_iDuration < 0ll)
+ {
+ _iResult = thrd_timedout;
+ break;
+ }
+ else if (_iDuration > int64_t(UINT32_MAX))
+ {
+ _iResult = thrd_error;
+ break;
+ }
+
+ if (SleepConditionVariableSRW(
+ reinterpret_cast(&_Mtx->_Cv),
+ reinterpret_cast(&_Mtx->_Ptr),
+ static_cast(_iDuration), 0))
+ {
+ continue;
+ }
+
+ if (GetLastError() != ERROR_TIMEOUT)
+ {
+ _iResult = thrd_error;
+ break;
+ }
+ }
+
+ break;
+ } while (false);
+
+ ReleaseSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr));
+ return _iResult;
+}
+
+extern "C" int __cdecl _mtx_timedlock32(
+ mtx_t * __restrict _Mtx, const struct _timespec32* __restrict _Ts)
+{
+ return common_mtx_timedlock(_Mtx, _Ts);
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(_mtx_timedlock32);
+
+extern "C" int __cdecl _mtx_timedlock64(
+ mtx_t* __restrict _Mtx, const struct _timespec64* __restrict _Ts)
+{
+ return common_mtx_timedlock(_Mtx, _Ts);
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(_mtx_timedlock64);
+
+extern "C" int __cdecl mtx_trylock(mtx_t* _Mtx)
+{
+ int _iResult = thrd_success;
+ const auto _uCurrentThreadId = GetCurrentThreadId();
+
+ AcquireSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr));
+
+ if (_Mtx->_Owner == 0)
+ {
+ _Mtx->_Owner = _uCurrentThreadId;
+ _Mtx->_Cnt = 1;
+ _iResult = thrd_success;
+ }
+ else if (_uCurrentThreadId == _Mtx->_Owner)
+ {
+ if ((_Mtx->_Type & mtx_recursive) == 0)
+ {
+ _iResult = thrd_busy;
+ }
+ else
+ {
+ _Mtx->_Cnt++;
+ _iResult = thrd_success;
+ }
+ }
+ else
+ {
+ _iResult = thrd_busy;
+ }
+
+ ReleaseSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr));
+ return _iResult;
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(mtx_trylock);
+
+static int __cdecl mtx_dounlock(mtx_t* _Mtx)
+{
+ if (GetCurrentThreadId() != _Mtx->_Owner)
+ {
+ abort();
+ }
+
+ if (--(_Mtx->_Cnt) == 0)
+ {
+ _Mtx->_Owner = 0;
+ }
+ WakeConditionVariable(reinterpret_cast(&_Mtx->_Cv));
+ return thrd_success;
+}
+
+extern "C" int __cdecl mtx_unlock(mtx_t* _Mtx)
+{
+ AcquireSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr));
+ const auto _iResult = mtx_dounlock(_Mtx);
+ ReleaseSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr));
+ return _iResult;
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(mtx_unlock);
+
+extern "C" int __cdecl cnd_broadcast(cnd_t * _Cond)
+{
+ WakeAllConditionVariable(reinterpret_cast(&_Cond->_Ptr));
+ return thrd_success;
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(cnd_broadcast);
+
+extern "C" void __cdecl cnd_destroy(cnd_t * _Cond)
+{
+ // 什么也不做
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(cnd_destroy);
+
+extern "C" int __cdecl cnd_init(cnd_t * _Cond)
+{
+ InitializeConditionVariable(reinterpret_cast(&_Cond->_Ptr));
+ return thrd_success;
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(cnd_init);
+
+extern "C" int __cdecl cnd_signal(cnd_t * _Cond)
+{
+ WakeConditionVariable(reinterpret_cast(&_Cond->_Ptr));
+ return thrd_success;
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(cnd_signal);
+
+template
+static int __cdecl common_cnd_timedwait(cnd_t* _Cond, mtx_t* _Mtx, const timespec* _Ts)
+{
+ int _iResult = thrd_success;
+ AcquireSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr));
+
+ do
+ {
+ if (mtx_dounlock(_Mtx) != thrd_success)
+ {
+ _iResult = thrd_error;
+ break;
+ }
+
+ for (;;)
+ {
+ const auto _iDuration = abstime_timespec_to_duration_ms(_Ts);
+ if (_iDuration <= 0ll)
+ {
+ _iResult = thrd_timedout;
+ break;
+ }
+ else if (_iDuration > int64_t(UINT32_MAX))
+ {
+ _iResult = thrd_error;
+ break;
+ }
+
+ if (SleepConditionVariableSRW(reinterpret_cast(&_Cond->_Ptr), reinterpret_cast(&_Mtx->_Ptr), static_cast(_iDuration), 0))
+ {
+ _iResult = thrd_success;
+ break;
+ }
+ }
+
+ if (mtx_dolock(_Mtx) != thrd_success)
+ {
+ _iResult = thrd_error;
+ break;
+ }
+ } while (false);
+
+
+ ReleaseSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr));
+ return _iResult;
+}
+
+extern "C" int __cdecl _cnd_timedwait32(cnd_t * _Cond, mtx_t * _Mtx, const struct _timespec32* _Ts)
+{
+ return common_cnd_timedwait(_Cond, _Mtx, _Ts);
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(_cnd_timedwait32);
+
+extern "C" int __cdecl _cnd_timedwait64(cnd_t* _Cond, mtx_t* _Mtx, const struct _timespec64* _Ts)
+{
+ return common_cnd_timedwait(_Cond, _Mtx, _Ts);
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(_cnd_timedwait64);
+
+extern "C" int cnd_wait(cnd_t* _Cond, mtx_t* _Mtx)
+{
+ int _iResult = thrd_success;
+ AcquireSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr));
+
+ do
+ {
+ if (mtx_dounlock(_Mtx) != thrd_success)
+ {
+ _iResult = thrd_error;
+ break;
+ }
+
+ if (!SleepConditionVariableSRW(reinterpret_cast(&_Cond->_Ptr), reinterpret_cast(&_Mtx->_Ptr), INFINITE, 0))
+ {
+ _iResult = thrd_error;
+ }
+
+ if (mtx_dolock(_Mtx) != thrd_success)
+ {
+ _iResult = thrd_error;
+ break;
+ }
+ } while (false);
+
+ ReleaseSRWLockExclusive(reinterpret_cast(&_Mtx->_Ptr));
+ return _iResult;
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(cnd_wait);
+
+extern "C" int __cdecl thrd_create(thrd_t * _Thr, thrd_start_t _Func, void* _Arg)
+{
+#if defined(_X86_)
+ // x86平台 __cdecl 与 __stdcall是不同的调用约定
+ struct thrd_start_data
+ {
+ thrd_start_t Func;
+ void* Arg;
+ };
+
+ auto _pThreadData = (thrd_start_data*)malloc(sizeof(thrd_start_data));
+ if (!_pThreadData)
+ return thrd_nomem;
+
+ _pThreadData->Func = _Func;
+ _pThreadData->Arg = _Arg;
+
+ errno = 0;
+ _Thr->_Handle = reinterpret_cast(_beginthreadex(nullptr, 0,
+ [](void* _pUserData) -> unsigned
+ {
+ auto _pStartData = static_cast(_pUserData);
+ auto _uResult = _pStartData->Func(_pStartData->Arg);
+ free(_pStartData);
+ return _uResult;
+ }, _pThreadData, 0, &_Thr->_Tid));
+
+ if (_Thr->_Handle)
+ {
+ return thrd_success;
+ }
+
+ // 微软原版存在一点瑕疵,应该先取错误代码,然后再释放内存
+ // 避免free时 errno 修改风险。
+ const auto _iResult = (errno == 0 || errno == ENOMEM) ? thrd_nomem : thrd_error;
+ free(_pThreadData);
+ return _iResult;
+#else
+ // 其他平台 __cdecl 与 __stdcall等价,所以不用额外跳转直接就可以用。
+ errno = 0;
+ _Thr->_Handle = reinterpret_cast(_beginthreadex(nullptr, 0, reinterpret_cast<_beginthreadex_proc_type>(_Func), _Arg, 0, &_Thr->_Tid));
+ if (_Thr->_Handle)
+ {
+ return thrd_success;
+ }
+
+ return (errno == 0 || errno == ENOMEM) ? thrd_nomem : thrd_error;
+#endif
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(thrd_create);
+
+extern "C" thrd_t __cdecl thrd_current(void)
+{
+ thrd_t _Result;
+ _Result._Handle = nullptr;
+ _Result._Tid = GetCurrentThreadId();
+ return _Result;
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(thrd_current);
+
+extern "C" int __cdecl thrd_detach(thrd_t _Thr)
+{
+ return CloseHandle(_Thr._Handle) ? thrd_success : thrd_error;
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(thrd_detach);
+
+extern "C" int __cdecl thrd_equal(thrd_t _Thr0, thrd_t _Thr1)
+{
+ return _Thr0._Tid == _Thr1._Tid;
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(thrd_equal);
+
+extern "C" void __cdecl thrd_exit(int _Res)
+{
+ ExitThread(static_cast(_Res));
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(thrd_exit);
+
+extern "C" int __cdecl thrd_join(thrd_t _Thr, int* _Res)
+{
+ DWORD _uExitCode;
+ if (WaitForSingleObject(_Thr._Handle, INFINITE) != /*WAIT_OBJECT_0*/ 0 || GetExitCodeThread(_Thr._Handle, &_uExitCode) == FALSE)
+ {
+ return thrd_error;
+ }
+
+ if (_Res)
+ {
+ *_Res = static_cast(_uExitCode);
+ }
+ return CloseHandle(_Thr._Handle) ? thrd_success : thrd_error;
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(thrd_join);
+
+extern "C" int __cdecl _thrd_sleep32(
+ const struct _timespec32* duration, struct _timespec32* remaining)
+{
+ const auto _iDuration = duration_timespec_to_ms(*duration);
+ if (_iDuration >= 0ll && _iDuration <= int64_t(UINT32_MAX))
+ {
+ Sleep(static_cast(_iDuration));
+ return 0;
+ }
+ else
+ {
+ return -2;
+ }
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(_thrd_sleep32);
+
+extern "C" int __cdecl _thrd_sleep64(
+ const struct _timespec64* duration, struct _timespec64* remaining)
+{
+ const auto _iDuration = duration_timespec_to_ms(*duration);
+ if (_iDuration >= 0ll && _iDuration <= int64_t(UINT32_MAX))
+ {
+ Sleep(static_cast(_iDuration));
+ return 0;
+ }
+ else
+ {
+ return -2;
+ }
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(_thrd_sleep64);
+
+extern "C" void __cdecl thrd_yield(void)
+{
+ SwitchToThread();
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(thrd_yield);
+
+static tss_global_data_t tss_global_data;
+
+extern "C" int __cdecl tss_create(tss_t * _Key, tss_dtor_t _Dtor)
+{
+ return tss_global_data.create(_Key, _Dtor);
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(tss_create);
+
+extern "C" void __cdecl tss_delete(tss_t _Key)
+{
+ tss_global_data.tss_delete(_Key);
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(tss_delete);
+
+extern "C" void* __cdecl tss_get(tss_t _Key)
+{
+ return tss_global_data.get(_Key);
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(tss_get);
+
+extern "C" int __cdecl tss_set(tss_t _Key, void* _Val)
+{
+ return tss_global_data.set(_Key, _Val);
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(tss_set);
+
+extern "C" void __cdecl call_once(once_flag* _Flag, void(*_Func)(void))
+{
+ InitOnceExecuteOnce(reinterpret_cast(_Flag),
+ [](_In_ PINIT_ONCE InitOnce,
+ _In_ PVOID Parameter,
+ _In_opt_ PVOID* Context) ->BOOL
+ {
+ auto _Func = static_cast(Parameter);
+ _Func();
+ return TRUE;
+ },
+ _Func,
+ nullptr);
+}
+
+_LCRT_DEFINE_IAT_SYMBOL(call_once);
+
+#pragma section(".CRT$XLE", long, read)
+
+static void NTAPI ClearTss(
+ PVOID DllHandle,
+ DWORD Reason,
+ PVOID Reserved)
+{
+ switch (Reason)
+ {
+ case DLL_PROCESS_DETACH:
+ tss_global_data.cleanup_global();
+ break;
+ case DLL_THREAD_DETACH:
+ tss_global_data.cleanup();
+ break;
+ }
+}
+
+static _CRTALLOC(".CRT$XLE") PIMAGE_TLS_CALLBACK volmd = &ClearTss;
diff --git a/UnitTest/C11ThreadsUnitTest.cpp b/UnitTest/C11ThreadsUnitTest.cpp
new file mode 100644
index 0000000..42e6b15
--- /dev/null
+++ b/UnitTest/C11ThreadsUnitTest.cpp
@@ -0,0 +1,527 @@
+#include "pch.h"
+#include "CppUnitTest.h"
+
+#include
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+namespace C11Threads
+{
+ constexpr auto kReturnCode = 8848;
+
+ TEST_CLASS(Threads)
+ {
+ public:
+ TEST_METHOD(thrd_create与thrd_detach)
+ {
+ thrd_t _Thrd;
+ auto _iResult = ::thrd_create(&_Thrd, [](void* _pUserData) -> int
+ {
+ return kReturnCode;
+ }, nullptr);
+
+ Assert::AreEqual(_iResult, 0);
+
+ _iResult = thrd_detach(_Thrd);
+ Assert::AreEqual(_iResult, 0);
+ }
+
+ TEST_METHOD(thrd_current)
+ {
+ auto _Result = ::thrd_current();
+ Assert::AreEqual(_Result._Tid, static_cast(GetCurrentThreadId()));
+ }
+
+ TEST_METHOD(thrd_join)
+ {
+ int _iOut = 0;
+ thrd_t _Thrd;
+ auto _iResult = ::thrd_create(&_Thrd, [](void* _pUserData) -> int
+ {
+ *(int*)_pUserData = kReturnCode;
+ Sleep(500);
+ return kReturnCode;
+ }, &_iOut);
+ Assert::AreEqual(_iResult, 0);
+
+ int _iExitCode = 0;
+ _iResult = ::thrd_join(_Thrd, &_iExitCode);
+ Assert::AreEqual(_iResult, 0);
+ Assert::AreEqual(_iExitCode, kReturnCode);
+ Assert::AreEqual(_iOut, kReturnCode);
+ }
+
+ TEST_METHOD(thrd_equal)
+ {
+ {
+ thrd_t _Left = { reinterpret_cast(888), 111 };
+ thrd_t _Right = { reinterpret_cast(5), 111 };;
+
+ auto _bResult = ::thrd_equal(_Left, _Right);
+ Assert::IsTrue(_bResult);
+ }
+
+ {
+ thrd_t _Left = { reinterpret_cast(888), 111 };
+ thrd_t _Right = { reinterpret_cast(888), 22 };;
+
+ auto _bResult = ::thrd_equal(_Left, _Right);
+ Assert::IsFalse(_bResult);
+ }
+ }
+
+ TEST_METHOD(thrd_exit)
+ {
+ thrd_t _Thrd;
+ auto _iResult = ::thrd_create(&_Thrd, [](void*) -> int
+ {
+ ::thrd_exit(kReturnCode);
+ return 1;
+ }, nullptr);
+ Assert::AreEqual(_iResult, 0);
+
+ int _iExitCode = 0;
+ _iResult = ::thrd_join(_Thrd, &_iExitCode);
+ Assert::AreEqual(_iResult, 0);
+ Assert::AreEqual(_iExitCode, kReturnCode);
+ }
+
+ TEST_METHOD(_thrd_sleep64)
+ {
+ // 等待500ms
+ {
+ _timespec64 _Duration = { 0, 1000000 * 500 };
+
+ const auto _uStart = GetTickCount64();
+ auto _iResult = ::_thrd_sleep64(&_Duration, nullptr);
+ const auto _uTick = GetTickCount64() - _uStart;
+ Assert::AreEqual(_iResult, 0);
+ Assert::IsTrue(_uTick >= 400 && _uTick <= 700);
+ }
+
+ // 等待 1.5秒
+ {
+ _timespec64 _Duration = { 1, 1000000 * 500 };
+
+ const auto _uStart = GetTickCount64();
+ auto _iResult = ::_thrd_sleep64(&_Duration, nullptr);
+ const auto _uTick = GetTickCount64() - _uStart;
+ Assert::AreEqual(_iResult, 0);
+ Assert::IsTrue(_uTick >= 1400 && _uTick <= 1700);
+ }
+
+ // 超大等待时间,返回失败
+ {
+ _timespec64 _Duration = { UINT32_MAX, 0 };
+ auto _iResult = ::_thrd_sleep64(&_Duration, nullptr);
+ Assert::AreNotEqual(_iResult, 0);
+ }
+ }
+ };
+
+ static void* g_pClearupData = 0;
+
+ TEST_CLASS(ThreadSpecificStorage)
+ {
+ public:
+ TEST_METHOD(tss_create与tss_delete)
+ {
+ tss_t _Key;
+ auto _iResult = ::tss_create(&_Key, nullptr);
+ Assert::AreEqual(_iResult, 0);
+
+ ::tss_delete(_Key);
+ }
+
+ TEST_METHOD(Tss清理机制)
+ {
+ g_pClearupData = nullptr;
+ tss_t _Key;
+ auto _iResult = ::tss_create(&_Key, [](void* _pData)
+ {
+ g_pClearupData = _pData;
+ });
+ Assert::AreEqual(_iResult, 0);
+
+ auto _hThread = (HANDLE)_beginthreadex(
+ 0,
+ 0,
+ [](void* _pUserData) -> unsigned
+ {
+ auto& _Key = *static_cast(_pUserData);
+ auto _iResult = ::tss_set(_Key, (void*)7);
+ Assert::AreEqual(_iResult, 0);
+ return 0;
+ }, &_Key, 0, nullptr);
+
+ Assert::IsNotNull(_hThread);
+
+ Assert::AreEqual(WaitForSingleObject(_hThread, 5 * 1000), (DWORD)WAIT_OBJECT_0);
+
+ CloseHandle(_hThread);
+
+ Assert::AreEqual(g_pClearupData, (void*)7);
+
+ ::tss_delete(_Key);
+ }
+
+ TEST_METHOD(单线程tss_get与tss_set)
+ {
+ tss_t _Key;
+ auto _iResult = ::tss_create(&_Key, nullptr);
+ Assert::AreEqual(_iResult, 0);
+
+ Assert::AreEqual(::tss_get(_Key), (void*)NULL);
+
+ _iResult = ::tss_set(_Key, (void*)5);
+ Assert::AreEqual(_iResult, 0);
+ Assert::AreEqual(::tss_get(_Key), (void*)5);
+
+ _iResult = ::tss_set(_Key, (void*)7);
+ Assert::AreEqual(_iResult, 0);
+ Assert::AreEqual(::tss_get(_Key), (void*)7);
+
+ ::tss_delete(_Key);
+ }
+
+ TEST_METHOD(多线程tss_get与tss_set)
+ {
+ tss_t _Key;
+ auto _iResult = ::tss_create(&_Key, nullptr);
+ _iResult = ::tss_set(_Key, (void*)5);
+ Assert::AreEqual(_iResult, 0);
+ Assert::AreEqual(::tss_get(_Key), (void*)5);
+
+
+ auto _hThread = (HANDLE)_beginthreadex(
+ 0,
+ 0,
+ [](void* _pUserData) -> unsigned
+ {
+ auto& _Key = *static_cast(_pUserData);
+ Assert::AreEqual(::tss_get(_Key), (void*)0);
+ auto _iResult = ::tss_set(_Key, (void*)7);
+ Assert::AreEqual(_iResult, 0);
+ Assert::AreEqual(::tss_get(_Key), (void*)7);
+ return 0;
+ }, &_Key, 0, nullptr);
+
+ Assert::IsNotNull(_hThread);
+
+ Assert::AreEqual(WaitForSingleObject(_hThread, 5 * 1000), (DWORD)WAIT_OBJECT_0);
+
+ CloseHandle(_hThread);
+
+ Assert::AreEqual(::tss_get(_Key), (void*)5);
+ }
+ };
+
+ TEST_CLASS(Mutexes)
+ {
+ public:
+ TEST_METHOD(mtx_init)
+ {
+ {
+ mtx_t _Mtx;
+ auto _iResult = ::mtx_init(&_Mtx, 0);
+ Assert::AreEqual(_iResult, 0);
+ mtx_destroy(&_Mtx);
+ }
+
+ {
+ mtx_t _Mtx;
+ auto _iResult = ::mtx_init(&_Mtx, mtx_recursive);
+ Assert::AreEqual(_iResult, 0);
+ mtx_destroy(&_Mtx);
+ }
+ }
+
+ TEST_METHOD(mtx_lock与mtx_unlock)
+ {
+ // 锁定后其他线程会等待
+ {
+ mtx_t _Mtx;
+ ::mtx_init(&_Mtx, 0);
+
+ auto _iResult = ::mtx_lock(&_Mtx);
+ Assert::AreEqual(_iResult, 0);
+
+ auto _hThread = (HANDLE)_beginthreadex(
+ 0,
+ 0,
+ [](void* _pUserData) -> unsigned
+ {
+ auto& _Mtx = *static_cast(_pUserData);
+
+ auto _uStart = GetTickCount64();
+ auto _iResult = ::mtx_lock(&_Mtx);
+ auto _uTick = GetTickCount64() - _uStart;
+
+ _iResult = ::mtx_unlock(&_Mtx);
+ Assert::AreEqual(_iResult, 0);
+ return static_cast(_uTick);
+ }, &_Mtx, 0, nullptr);
+
+ Assert::IsNotNull(_hThread);
+
+ Sleep(500);
+ _iResult = ::mtx_unlock(&_Mtx);
+ Assert::AreEqual(_iResult, 0);
+
+ Assert::AreEqual(WaitForSingleObject(_hThread, 5 * 1000), (DWORD)WAIT_OBJECT_0);
+
+ DWORD _uCode = 0;
+ GetExitCodeThread(_hThread, &_uCode);
+ CloseHandle(_hThread);
+
+ Assert::IsTrue(_uCode <= 700 && _uCode >= 400);
+ }
+
+ // 递归时多次加锁解锁不会有事情
+ {
+ mtx_t _Mtx;
+ ::mtx_init(&_Mtx, mtx_recursive);
+
+ auto _iResult = ::mtx_lock(&_Mtx);
+ Assert::AreEqual(_iResult, 0);
+ _iResult = ::mtx_lock(&_Mtx);
+ Assert::AreEqual(_iResult, 0);
+ _iResult = ::mtx_unlock(&_Mtx);
+ Assert::AreEqual(_iResult, 0);
+ _iResult = ::mtx_unlock(&_Mtx);
+ Assert::AreEqual(_iResult, 0);
+ }
+ }
+
+ TEST_METHOD(mtx_trylock)
+ {
+ {
+ mtx_t _Mtx;
+ ::mtx_init(&_Mtx, 0);
+
+ auto _iResult = ::mtx_trylock(&_Mtx);
+ Assert::AreEqual(_iResult, 0);
+
+ _iResult = ::mtx_trylock(&_Mtx);
+ Assert::AreNotEqual(_iResult, 0);
+
+ _iResult = ::mtx_unlock(&_Mtx);
+ Assert::AreEqual(_iResult, 0);
+
+ _iResult = ::mtx_trylock(&_Mtx);
+ Assert::AreEqual(_iResult, 0);
+ }
+
+ {
+ mtx_t _Mtx;
+ auto _iResult = ::mtx_init(&_Mtx, mtx_recursive);
+ Assert::AreEqual(_iResult, 0);
+
+ _iResult = ::mtx_trylock(&_Mtx);
+ Assert::AreEqual(_iResult, 0);
+
+ _iResult = ::mtx_trylock(&_Mtx);
+ Assert::AreEqual(_iResult, 0);
+ }
+ }
+
+ TEST_METHOD(_mtx_timedlock64)
+ {
+ mtx_t _Mtx;
+ auto _iResult = ::mtx_init(&_Mtx, 0);
+ Assert::AreEqual(_iResult, 0);
+
+ _timespec64 _Now;
+ _iResult = _timespec64_get(&_Now, TIME_UTC);
+ Assert::AreEqual(_iResult, TIME_UTC);
+
+ _iResult = ::_mtx_timedlock64(&_Mtx, &_Now);
+ Assert::AreEqual(_iResult, 0);
+
+ auto _hThread = (HANDLE)_beginthreadex(
+ 0,
+ 0,
+ [](void* _pUserData) -> unsigned
+ {
+ auto& _Mtx = *static_cast(_pUserData);
+ auto _uStart = GetTickCount64();
+
+ _timespec64 _Now;
+ auto _iResult = _timespec64_get(&_Now, TIME_UTC);
+ Assert::AreEqual(_iResult, TIME_UTC);
+ _Now.tv_sec += 1;
+
+ _iResult = ::_mtx_timedlock64(&_Mtx, &_Now);
+ Assert::AreEqual(_iResult, 2);
+ auto _uTick = GetTickCount64() - _uStart;
+ return static_cast(_uTick);
+ }, &_Mtx, 0, nullptr);
+
+ Assert::IsNotNull(_hThread);
+ Assert::AreEqual(WaitForSingleObject(_hThread, 5 * 1000), (DWORD)WAIT_OBJECT_0);
+
+ DWORD _uCode = 0;
+ GetExitCodeThread(_hThread, &_uCode);
+ CloseHandle(_hThread);
+
+ Assert::IsTrue(_uCode <= 1200 && _uCode >= 900);
+ }
+ };
+
+ TEST_CLASS(ConditionVariables)
+ {
+ public:
+ TEST_METHOD(cnd_init)
+ {
+ cnd_t _Cond;
+ auto _iResult = ::cnd_init(&_Cond);
+ Assert::AreEqual(_iResult, 0);
+ }
+
+ TEST_METHOD(cnd_signal与cnd_broadcast与cnd_wait)
+ {
+ struct ThreadData
+ {
+ mtx_t Mtx;
+ cnd_t Cond;
+ };
+
+ ThreadData _ThreadData;
+
+ auto _iResult = ::mtx_init(&_ThreadData.Mtx, 0);
+ Assert::AreEqual(_iResult, 0);
+
+ _iResult = ::cnd_init(&_ThreadData.Cond);
+ Assert::AreEqual(_iResult, 0);
+
+ HANDLE _hThreads[5];
+ for (auto& _hThread : _hThreads)
+ {
+ _hThread = (HANDLE)_beginthreadex(
+ 0,
+ 0,
+ [](void* _pUserData) -> unsigned
+ {
+ auto& _ThreadData = *static_cast(_pUserData);
+ auto _iResult = ::mtx_lock(&_ThreadData.Mtx);
+ Assert::AreEqual(_iResult, 0);
+ ::cnd_wait(&_ThreadData.Cond, &_ThreadData.Mtx);
+ _iResult = ::mtx_unlock(&_ThreadData.Mtx);
+ Assert::AreEqual(_iResult, 0);
+ return 1;
+ }, &_ThreadData, 0, nullptr);
+
+ Assert::IsNotNull(_hThread);
+ }
+
+ Sleep(100);
+
+ _iResult = cnd_signal(&_ThreadData.Cond);
+ Assert::AreEqual(_iResult, 0);
+
+ uint32_t _uSignalCount = 0;
+
+ for (auto& _hThread : _hThreads)
+ {
+ if (!_hThread)
+ continue;
+ if (WaitForSingleObject(_hThread, 100) == WAIT_OBJECT_0)
+ {
+ ++_uSignalCount;
+ CloseHandle(_hThread);
+ _hThread = nullptr;
+ }
+ }
+
+ Assert::AreEqual(_uSignalCount, 1u);
+ _iResult = cnd_signal(&_ThreadData.Cond);
+ Assert::AreEqual(_iResult, 0);
+
+ for (auto& _hThread : _hThreads)
+ {
+ if (!_hThread)
+ continue;
+ if (WaitForSingleObject(_hThread, 100) == WAIT_OBJECT_0)
+ {
+ ++_uSignalCount;
+
+ CloseHandle(_hThread);
+ _hThread = nullptr;
+ }
+ }
+ Assert::AreEqual(_uSignalCount, 2u);
+
+ _iResult = cnd_broadcast(&_ThreadData.Cond);
+ Assert::AreEqual(_iResult, 0);
+
+ for (auto& _hThread : _hThreads)
+ {
+ if (!_hThread)
+ continue;
+ if (WaitForSingleObject(_hThread, 100) == WAIT_OBJECT_0)
+ {
+ ++_uSignalCount;
+
+ CloseHandle(_hThread);
+ _hThread = nullptr;
+ }
+ }
+
+ Assert::AreEqual(_uSignalCount, (uint32_t)std::size(_hThreads));
+ }
+
+ TEST_METHOD(_cnd_timedwait64)
+ {
+ struct ThreadData
+ {
+ mtx_t Mtx;
+ cnd_t Cond;
+ };
+
+ ThreadData _ThreadData;
+
+ auto _iResult = ::mtx_init(&_ThreadData.Mtx, 0);
+ Assert::AreEqual(_iResult, 0);
+
+ _iResult = ::cnd_init(&_ThreadData.Cond);
+ Assert::AreEqual(_iResult, 0);
+
+ _iResult = ::mtx_lock(&_ThreadData.Mtx);
+ Assert::AreEqual(_iResult, 0);
+
+ auto _hThread = (HANDLE)_beginthreadex(
+ 0,
+ 0,
+ [](void* _pUserData) -> unsigned
+ {
+ Sleep(5 * 1000);
+ auto& _ThreadData = *static_cast(_pUserData);
+ auto _iResult = cnd_signal(&_ThreadData.Cond);
+ Assert::AreEqual(_iResult, 0);
+ return 1;
+ }, &_ThreadData, 0, nullptr);
+
+ Assert::IsNotNull(_hThread);
+ _timespec64 _Now;
+
+ const auto _uStart0 = GetTickCount64();
+
+ _iResult = _timespec64_get(&_Now, TIME_UTC);
+ Assert::AreEqual(_iResult, TIME_UTC);
+
+ _Now.tv_sec += 1;
+ _iResult = ::_cnd_timedwait64(&_ThreadData.Cond, &_ThreadData.Mtx, &_Now);
+ const auto _uTick1 = GetTickCount64() - _uStart0;
+ Assert::AreEqual(_iResult, (int)thrd_timedout);
+ Assert::IsTrue(_uTick1 >= 900 && _uTick1 <= 1200);
+
+ _Now.tv_sec += 5;
+ _iResult = ::_cnd_timedwait64(&_ThreadData.Cond, &_ThreadData.Mtx, &_Now);
+ const auto _uTick2 = GetTickCount64() - _uStart0;
+ Assert::AreEqual(_iResult, (int)thrd_success);
+ Assert::IsTrue(_uTick2 >= 4800 && _uTick2 <= 5300);
+
+ ::mtx_unlock(&_ThreadData.Mtx);
+ }
+ };
+}
diff --git a/UnitTest/UnitTest.vcxproj b/UnitTest/UnitTest.vcxproj
index 573846b..47d7323 100644
--- a/UnitTest/UnitTest.vcxproj
+++ b/UnitTest/UnitTest.vcxproj
@@ -162,6 +162,7 @@
+
Create
diff --git a/UnitTest/UnitTest.vcxproj.filters b/UnitTest/UnitTest.vcxproj.filters
index 1a2ad13..4d87292 100644
--- a/UnitTest/UnitTest.vcxproj.filters
+++ b/UnitTest/UnitTest.vcxproj.filters
@@ -39,6 +39,9 @@
源文件\VC-LTL_HelperUnitTest\PowerShell
+
+ 源文件
+
diff --git a/UnitTest/pch.cpp b/UnitTest/pch.cpp
index 0f09cf7..dac253a 100644
--- a/UnitTest/pch.cpp
+++ b/UnitTest/pch.cpp
@@ -73,15 +73,12 @@ LSTATUS RunCmd(LPCWSTR FilePath, CString CmdString, CString* pOutString, CString
CloseHandle(pi.hThread);
CloseHandle(pi.hProcess);
-
- //*OutString._Mylast() = NULL;
if (pOutString)
{
- *pOutString = OutString;
+ const auto _iUtf8Length = OutString.GetLength();
+ auto _iUtf16Length = MultiByteToWideChar(CP_UTF8, 0, OutString.GetString(), _iUtf8Length, pOutString->GetBuffer(_iUtf8Length), _iUtf8Length);
+ pOutString->ReleaseBufferSetLength(_iUtf16Length);
}
-
-
- //EXECDOSCMD.
return lStatus;
}
diff --git a/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj b/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj
index 36888b9..73a1de2 100644
--- a/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj
+++ b/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj
@@ -696,6 +696,9 @@
true
+
+ false
+
true
diff --git a/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj.filters b/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj.filters
index a12ac2b..7a482c3 100644
--- a/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj.filters
+++ b/vcruntime.msvcrt/vcruntime.msvcrt.vcxproj.filters
@@ -101,6 +101,9 @@
源文件\ltl
+
+ 源文件\ltl
+
diff --git a/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj b/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj
index f4917fa..889500f 100644
--- a/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj
+++ b/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj
@@ -835,6 +835,9 @@
true
+
+ false
+
stdcpp17
true
diff --git a/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj.filters b/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj.filters
index d9aa2a6..f33ab06 100644
--- a/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj.filters
+++ b/vcruntime.ucrtbase/vcruntime.ucrtbase.vcxproj.filters
@@ -62,5 +62,8 @@
源文件\ltl
+
+ 源文件\ltl
+
\ No newline at end of file