Writing Windows Runtime (WinRT) code in C that needs to run on older, downlevel versions of Windows, while still taking advantage of new system APIs if present, is a pain. This library makes it easy to check for APIs in the supported fashion with high performance. I've used code in production apps that have shipped to millions of users that's based on this library.
- Copy
ApiCheck.cpp
,ApiCheck.h
, andApiCheckCore.h
into your project. - (Optional) In
ApiCheckCore.h
, setRequiredVersion
to the minimum version of Windows that your app supports. - Use the
ApiCheck::
functions accordingly.
This library is in standard C and requires no additional frameworks beyond the Windows SDK. Your project can use standard C (with WRL, for example), C /CX, or C /WinRT. As long as it links to the Windows Runtime and calls RoInitialize
, it should work.
ApiCheck.cpp
is only needed if you want the specific helper functions in that file. If you don't need those, delete ApiCheck.cpp
and remove the corresponding function declarations from ApiCheck.h
. Boom—header-only library!
Add the following to each of your source files that need it, or to your precompiled header:
#include "ApiCheck.h"
There are convenient helper functions for many versions of Windows:
if (ApiCheck::IsAtLeastRS3()) { /* ... */ }
Alternately, you can use this more general form:
if (ApiCheck::IsAtLeast<ApiCheck::WindowsVersion::RS3>()) { /* ... */ }
The supported values for WindowsVersion
are in ApiCheckCore.h
:
WindowsVersion |
means... |
---|---|
TH1 |
Threshold 1 = version 1507 = build 10240 |
TH2 |
Threshold 2 = version 1511 = build 10586 |
RS1 |
Redstone 1 = Anniversary Update = version 1607 = build 14393 |
RS2 |
Redstone 2 = Creators Update = version 1703 = build 15063 (or 15254 for final feature2 build of Windows 10 Mobile) |
RS3 |
Redstone 3 = Fall Creators Update = version 1709 = build 16299 |
RS4 |
Redstone 4 = April 2018 Update = version 1803 = build 17134 |
RS5 |
Redstone 5 = October 2018 Update = version 1809 = build 17763 |
CY19H1 |
May 2019 Update = version 1903 = build 18362 |
CY20H1 |
May 2020 Update = version 2004 = build 19041 |
None |
default value |
Hey, don't blame me for the absolute insanity of Windows version naming.
Instead of calling Windows.Foundation.Metadata.ApiInformation.IsTypePresent
directly, call ApiCheck::IsTypePresent
. See ApiCheck.cpp
for a few examples of this.
if (ApiCheck::IsTypePresent<RuntimeClass_Windows_UI_Xaml_Media_RevealBrush,
ApiCheck::WindowsVersion::RS3>())
{
/* ... */
}
IsTypePresent
takes two type parameters:
RuntimeClass
: The RuntimeClass (full API name) of the API.IntroducedIn
: Optionally, the version of Windows that the API was introduced in, if it's in the universal contract.
The example above checks to see if the RevealBrush
type is available. But, if the user is running a version of Windows after RS3 (aka Fall Creators Update, aka Version 1709, aka build 16299), then it knows that that check can be skipped. (If the type is part of an extension SDK and not the universal contract, don't include IntroducedIn
. That's the case for APIs that are available only on, say, Windows Phone or HoloLens, such as Windows.UI.ViewManagement.StatusBar
, which is not present on any desktop version of Windows.)
ApiCheck also provides wrappers for IsPropertyPresent
, IsMethodPresent
, IsEventPresent
, and IsEnumNamedValuePresent
. These work the same as IsTypePresent
except they take an additional type parameter.
extern const wchar_t PreviewKeyDown[] = L"PreviewKeyDown";
if (ApiCheck::IsPropertyPresent<RuntimeClass_Windows_UI_Xaml_UIElement,
PreviewKeyDown,
ApiCheck::WindowsVersion::RS3>())
{
/* ... */
}
These wrappers take three type parameters:
RuntimeClass
: The RuntimeClass (full API name) of the type.Property
/Method
/Event
/NamedValue
: The member of the type to look for.IntroducedIn
: Optionally, the version of Windows that the member was introduced in, if it's in the universal contract.
The supported way to check for the existence of an API is to use the Windows.Foundation.Metadata.ApiInformation
API. Under the hood, that requires looking up and instantiating the activation factory for IApiInformationStatics
by name, and then calling the API, which does a string lookup of the API which requires loading metadata (.winmd) files.
This library:
- Is faster to code for, since it's just simple standard C function calls that just "do the right thing"
- Caches the activation factory for you
- Caches API existence checks for you so that they're only performed once
- Can often avoid checking for the API entirely, if:
- The user is running a version of Windows that is guaranteed to always have the API
- Your app's manifest dictates that the minimum version of Windows required to install the app already has the API
Under the hood, it uses magic statics and function templates. Each unique API checked adds about 83 bytes to your executable, and the cached checks only take about 1 μs on my machine. (In comparison, the small metadata file that Windows loads for an API check would easily be 500× that size or more.)
© 2021 Travis Spomer. MIT license.