How can I check whether a Windows Runtime object supports a member before I try to use it?

Raymond Chen

Raymond

Last time, we saw that an invalid cast exception can mean that you tried to use a class member that isn’t supported by the current operating system. What can you do to avoid this problem?

You might be running into this problem because your program is running on a version of Windows that you never intended to support. For example, your program is totally dependent upon some feature added in Windows 10 version 1903, and there’s no point trying to run it on anything earlier. You can go to your manifest and set the Min­Version in your Target­Device­Family element to the minimum version that supports the thing you need. Your application will be deployed only to systems that satisfy your manifest’s minimum requirements.

You can get the minimum version from MSDN under Additional features and requirements:

Device familyWindows 10, version 1803 (introduced v10.0.17134.0)

In this case, the magic number to put in your manifest is 10.0.17134.0. Naturally, if you have many minimum requirements, then the one you pick for your manifest is the highest.

<TargetDeviceFamily Name="Windows.Universal"
    MinVersion="10.0.17134.0"
    MaxVersionTested="10.0.18362.0" />

Another option is to perform a runtime check before trying to use the possibly-nonexistent member. The most direct way is to check specifically for the thing:

// C#
if (ApiInformation.IsMethodPresent("MyNamespace.MyClass", "SomeMember"))
{
  // There is a method called SomeMember
  myObject.SomeMember();
}

If overloads were added at different times, you will need to use the check that takes an arity.

if (ApiInformation.IsMethodPresent("MyNamespace.MyClass", "SomeMember", 1))
{
  // it is okay to call the overload of the SomeMember method with 1 parameter
  myObject.SomeMember(true);
}

You can use C#’s nameof operator and Type.FullName property to avoid hard-coding strings. In C++/WinRT, you can use winrt::name_of<T>().

You can also check for the existence of properties, and even narrow your check to read-only properties or writable properties.

Another option for the runtime check is to check for the presence of the corresponding contract that introduced support for it. This information is also provided in MSDN under Additional features and requirements:

API contractWindows.Foundation.UniversalApiContract (introduced v6)

The contract name and version are what you pass to the Is­Api­Contract­Present method.

// C#
if (ApiInformation.IsApiContractPresent(
    "Windows.Foundation.UniversalApiContract", 6))
{
  // it is okay to use things that were introduced in
  // Windows.Foundation.UniversalApiContract version 6.
}

Bonus chatter: What about JavaScript? In JavaScript, an attempt to read a nonexistent member succeeds but returns undefined. This behavior is consistent with the overall design of the JavaScript language.

 

6 comments

Comments are closed.

    • Raymond Chen
      Raymond ChenMicrosoft logo

      You won’t know if the exception was because SomeMember is missing (possibly recoverable, operation never started), or because SomeMember itself threw an exception (probably not recoverable, operation may have partly completed, leaving myObject in indeterminate state). Also because many debuggers stop on first-chance exceptions (before they are caught).

  • Avatar
    Neil Rashbrook

    I don’t know the optimal design pattern for extending an interface, so I assume these interfaces have been created as independent interfaces and don’t inherit from each other, otherwise I was wondering whether it would have been possible to change the default interface depending on the minimum Windows version.

    • Raymond Chen
      Raymond ChenMicrosoft logo

      You can’t change the default interface, because that would break interop between old and new components. The old component passes IMyClass to represent a MyClass, but the new component thinks it got a IMyClass2, calls method 5 on the vtable, and calls the wrong method.