Let me start by stating the obvious, if you find yourself needing to get your thread's stack size as part of your code, more likely than not, you are doing something wrong. Unless it is part of your core logic, getting into OS's business is often an invitation to trouble.
That's said, there are legitimate, yet rare, cases where you may need to get your stack's true stack size. Whether you have specific logic that requires more custom or careful handling of stack overflow situations, or you want to query your stack size and change your program's behavior when you approach your stack's limit to avoid or better handle SO situations. Or maybe you have some complex memory handling that necessitate writing your own guards and checks against buffer overruns.
The OS layers, Win32, UWP, or .NET libraries, do extensive work to take care of all these scenarios for you. So it should be fairly rare that you end up needing to meddle with the stack yourself. And if you ever do, exercise extreme care and caution.
When you run an executable file (PE) in Windows, the OS creates a kernel object and a set of structures that hold information related to the running of your program. One important structure, is the Thread Execution Block (TEB). There, the OS saves information related to the threads associated with your process. In fact, as far as Windows goes, threads are the actual units of execution. The process is simply a container of threads (to put it in an oversimplified term). Every process comes with a default thread which would run first when your executable is invoked, but applications often create additional threads to scale and stay responsive, among other reasons.
To keep threads isolated, Windows allocates a dedicated space to each thread. That space is what is commonly referred to as "the stack", not to confuse with the stack data structure, although the OS's stack allocates data in a stack data structure format. The stack, in simple terms, is a sandboxed contiguous memory playground for a given thread. It contains its local variables, parameters, and return values. The size of the stack information lives in TEB, and is defined at linking time at the executable level. The linker sets these values at the PE (your executable) header. The default value for the stack size is 1MB.
To be frugal on memory usage, the OS only allocates a portion of the requested stack to the device's physical memory. The allocated space is called "committed" memory. The rest of space is only "reserved". Similar to when you reserve a dedicated space in a parking lot, your spot is not necessarily occupied by your car all the time, but it is always yours to use, and no one else can access it. So the OS, acting as the parking authority here, provides a guarantee that the space would be available to the thread whenever needed. If any other party tries to access the thread's reserved memory, it'd get an access violation exception, their parking violation ticket if we want to use the parking space analogy. When the owner thread finally needs access to the reserved space, the referenced memory will move to become part of the committed set. The default committed size by the linker is 4K, which is a single page of memory. Both reserved and committed stack space for a PE can be overwritten at linking time by passing a flag to the linker.
So how can you know how much stack space you have left? The most straightforward way is to call GetCurrentThreadStackLimits.
ULONG_PTR stackBase= 0;
ULONG_PTR stackLimit = 0;
Then your delta for available space is:
spaceDelta = stackBase - stackLimit
Both the stack base and the stack limit are properties saved within the system's TEB data structure for the given thread.