{"id":111795,"date":"2025-11-17T07:00:00","date_gmt":"2025-11-17T15:00:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/oldnewthing\/?p=111795"},"modified":"2025-11-17T09:32:07","modified_gmt":"2025-11-17T17:32:07","slug":"20251117-00","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/oldnewthing\/20251117-00\/?p=111795","title":{"rendered":"How can I detect that Windows is running in S-Mode, redux"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250807-00\/?p=111444&amp;commentid=143064#comment-143064\"> Igor Levicki asked for a plain C version<\/a> of the sample code to detect whether Windows is running in S-Mode. I didn&#8217;t write one for two reasons. First, I didn&#8217;t realize that so many people still tried to use COM from plain C. And second, I didn&#8217;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>\n<ul>\n<li><code>p-&gt;Method(args)<\/code> becomes <code>p-&gt;lpVtbl-&gt;Method(p, args)<\/code>.<\/li>\n<li>Copying a C++ smart COM pointer consists of copying the raw pointer and performing an <code>AddRef<\/code> if the raw pointer is non-null.<\/li>\n<li>Destroying a C++ smart COM pointer consists of performing a <code>Release<\/code> if the raw pointer is non-null.<\/li>\n<li>Before overwriting a C++ smart COM pointer, remember the old pointer value, and if it is non-null, <code>Release<\/code> it <a title=\"Reference counting is hard.\" href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20040406-00\/?p=39903\">after you <code>AddRef<\/code> the new non-null pointer value<\/a>.<\/li>\n<\/ul>\n<p>The wrinkle added by the Windows Runtime is that C doesn&#8217;t support namespaces, so the Windows Runtime type names are decorated by their namespaces.<\/p>\n<p>And since you&#8217;re not using WRL, then you don&#8217;t get the WRL helpers for creating <code>HSTRING<\/code>s, so you have to call the low-level <code>HSTRING<\/code> functions yourself.<\/p>\n<pre>#include &lt;Windows.System.Profile.h&gt;\r\n\r\nHRESULT ShouldSuggestCompanion(BOOL* suggestCompanion)\r\n{\r\n    HSTRING_HEADER header;\r\n    HSTRING className;\r\n    HRESULT hr;\r\n\r\n    hr = WindowsCreateStringReference(RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy,\r\n                ARRAYSIZE(RuntimeClass_Windows_System_Profile_WindowsIntegrityPolicy) - 1,\r\n                &amp;header, &amp;className);\r\n    if (SUCCEEDED(hr))\r\n    {\r\n        <span style=\"border: solid 1px currentcolor;\">__x_ABI_CWindows_CSystem_CProfile_CIWindowsIntegrityPolicyStatics<\/span>* statics;\r\n        hr = RoGetActivationFactory(className, &amp;IID_<span style=\"border: solid 1px currentcolor;\">__x_ABI_CWindows_CSystem_CProfile_CIWindowsIntegrityPolicyStatics<\/span>, (void**)&amp;statics);\r\n        if (SUCCEEDED(hr))\r\n        {\r\n            boolean isEnabled;\r\n            hr = statics-&gt;<span style=\"border: solid 1px currentcolor;\">lpVtbl-&gt;<\/span>get_IsEnabled(<span style=\"border: solid 1px currentcolor;\">statics<\/span>, &amp;isEnabled);\r\n            if (SUCCEEDED(hr))\r\n            {\r\n                if (isEnabled)\r\n                {\r\n                    \/\/ System is in S-Mode\r\n                    boolean canDisable;\r\n                    hr = statics-&gt;<span style=\"border: solid 1px currentcolor;\">lpVtbl-&gt;<\/span>get_CanDisable(<span style=\"border: solid 1px currentcolor;\">statics<\/span>, &amp;canDisable);\r\n                    if (SUCCEEDED(hr))\r\n                    {\r\n                        \/\/ System is in S-Mode but can be taken out of S-Mode\r\n                        *suggestCompanion = TRUE;\r\n                    }\r\n                    else\r\n                    {\r\n                        \/\/ System is locked into S-Mode\r\n                        *suggestCompanion = FALSE;\r\n                    }\r\n                }\r\n                else\r\n                {\r\n                    \/\/ System is not in S-Mode\r\n                    *suggestCompanion = TRUE;\r\n                }\r\n            }\r\n            statics-&gt;<span style=\"border: solid 1px currentcolor;\">lpVtbl-&gt;<\/span>Release(<span style=\"border: solid 1px currentcolor;\">statics<\/span>);\r\n        }\r\n    }\r\n\r\n    return hr;\r\n}\r\n<\/pre>\n<p>There is a micro-optimization here: We don&#8217;t need to call <code>Windows\u00adDelete\u00adString(hstring)<\/code> 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&#8217;t hurt to call <code>Windows\u00adDelete\u00adString<\/code> on a string reference; it&#8217;s just a nop.<\/p>\n<p>It wasn&#8217;t that exciting. It was merely annoying. So that&#8217;s another reason I didn&#8217;t bother including a plain C sample.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250807-00\/?p=111444&amp;commentid=143068#comment-143068\"> Baltasar Garc\u00eda offered a simplification to the original code<\/a>:<\/p>\n<pre>bool s_mode = WindowsIntegrityPolicy.IsEnabled;\r\nbool unlockable_s_mode = WindowsIntegrityPolicy.CanDisable;\r\nbool suggestCompanion = !s_mode || (s_mode &amp;&amp; unlockable_s_mode);\r\n<\/pre>\n<p>and <a href=\"https:\/\/devblogs.microsoft.com\/oldnewthing\/20250807-00\/?p=111444&amp;commentid=143072#comment-143072\"> Csaba Varga simplified it further<\/a>:<\/p>\n<pre>bool suggestCompanion = !s_mode || unlockable_s_mode;\r\n<\/pre>\n<p>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.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Doing it on hard mode.<\/p>\n","protected":false},"author":1069,"featured_media":111744,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[1],"tags":[25],"class_list":["post-111795","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-oldnewthing","tag-code"],"acf":[],"blog_post_summary":"<p>Doing it on hard mode.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111795","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/users\/1069"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/comments?post=111795"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/posts\/111795\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media\/111744"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/media?parent=111795"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/categories?post=111795"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/oldnewthing\/wp-json\/wp\/v2\/tags?post=111795"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}