November 17th, 2025
heart1 reaction

How can I detect that Windows is running in S-Mode, redux

Igor Levicki asked for a plain C version of the sample code to detect whether Windows is running in S-Mode. I didn’t write one for two reasons. First, I didn’t realize that so many people still tried to use COM from plain C. And second, I didn’t realize that the people who try to use COM from plain C are not sufficiently familiar with how COM works at the ABI level to perform the mechanical conversion themselves.

  • p->Method(args) becomes p->lpVtbl->Method(p, args).
  • Copying a C++ smart COM pointer consists of copying the raw pointer and performing an AddRef if the raw pointer is non-null.
  • Destroying a C++ smart COM pointer consists of performing a Release if the raw pointer is non-null.
  • Before overwriting a C++ smart COM pointer, remember the old pointer value, and if it is non-null, Release it after you AddRef the new non-null pointer value.

The wrinkle added by the Windows Runtime is that C doesn’t support namespaces, so the Windows Runtime type names are decorated by their namespaces.

And since you’re not using WRL, then you don’t get the WRL helpers for creating HSTRINGs, so you have to call the low-level HSTRING functions yourself.

#include <Windows.System.Profile.h>

HRESULT ShouldSuggestCompanion(BOOL* suggestCompanion)
{
    HSTRING_HEADER header;
    HSTRING className;
    HRESULT hr;

    hr = WindowsCreateStringReference(RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy,
                ARRAYSIZE(RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy) - 1,
                &header, &className);
    if (SUCCEEDED(hr))
    {
        __x_ABI_CWindows_CSystem_CProfile_CIWindowsIntegrityPolicyStatics* statics;
        hr = RoGetActivationFactory(className, &IID___x_ABI_CWindows_CSystem_CProfile_CIWindowsIntegrityPolicyStatics, (void**)&statics);
        if (SUCCEEDED(hr))
        {
            boolean isEnabled;
            hr = statics->lpVtbl->get_IsEnabled(statics, &isEnabled);
            if (SUCCEEDED(hr))
            {
                if (isEnabled)
                {
                    // System is in S-Mode
                    boolean canDisable;
                    hr = statics->lpVtbl->get_CanDisable(statics, &canDisable);
                    if (SUCCEEDED(hr))
                    {
                        // System is in S-Mode but can be taken out of S-Mode
                        *suggestCompanion = TRUE;
                    }
                    else
                    {
                        // System is locked into S-Mode
                        *suggestCompanion = FALSE;
                    }
                }
                else
                {
                    // System is not in S-Mode
                    *suggestCompanion = TRUE;
                }
            }
            statics->lpVtbl->Release(statics);
        }
    }

    return hr;
}

There is a micro-optimization here: We don’t need to call Windows­Delete­String(hstring) at the end because the string we created is a string reference, and those are not reference-counted. (All of the memory is preallocated; there is nothing to clean up.) That said, it doesn’t hurt to call Windows­Delete­String on a string reference; it’s just a nop.

It wasn’t that exciting. It was merely annoying. So that’s another reason I didn’t bother including a plain C sample.

Baltasar García offered a simplification to the original code:

bool s_mode = WindowsIntegrityPolicy.IsEnabled;
bool unlockable_s_mode = WindowsIntegrityPolicy.CanDisable;
bool suggestCompanion = !s_mode || (s_mode && unlockable_s_mode);

and Csaba Varga simplified it further:

bool suggestCompanion = !s_mode || unlockable_s_mode;

I agree that these are valid simplifications, but I spelled it out the long way to make the multi-step logic more explicit, and to allow you to insert other logic into the blocks that right now merely contain an explanatory comment and a Boolean assignment.

Topics
Code

Author

Raymond has been involved in the evolution of Windows for more than 30 years. In 2003, he began a Web site known as The Old New Thing which has grown in popularity far beyond his wildest imagination, a development which still gives him the heebie-jeebies. The Web site spawned a book, coincidentally also titled The Old New Thing (Addison Wesley 2007). He occasionally appears on the Windows Dev Docs Twitter account to tell stories which convey no useful information.

5 comments

Sort by :
  • Yexuan Xiao

    Perhaps someday in the future, someone or I might need to translate ancient C code into C++ and will come across this article. I recently translated some C# code that uses COM into C++ and found it quite straightforward.

  • skSdnW

    WinRT makes it available to the other languages/UWP. Win32 developers just have to suck it up and suffer. Would have been a nice fit for the IsOS function in shlwapi…

  • Igor Levicki

    @Raymond,

    I guess I owe you an apology for not phrasing my question better.

    Yes, it is nice to see a C version, but what I actually meant was “C(++) without Windows Runtime / COM” — in other words I was hoping for native Win32 API or registry key or EFI variable or whatever which can sidestep all the COM nonsense for such a trivial question about OS configuration state. The amount of boilerplate to read a single config bool is staggering and whoever did not think to expose this via a single API call should be ashamed.

    • Yexuan Xiao

      I don’t think using COM is a problem (I use C++/WinRT and WRL), but I don’t understand when it’s necessary to initialize COM and when to uninitialize it. Currently, the behavior of CRT/Win32 functions is complex, and there’s no clear guide telling people when and how to do these things (this is somewhat similar to what can be done during DLL initialization).

    • skSdnW

      What is with the ugly __x_ABI prefix? Double underscore makes it a compiler reserved name and could in theory clash with something in Clang/GCC. Why wasn’t a better prefix chosen? WINRTABI? WINABINS?