问题
Imagine the following setup:
UWP Library:
MinVersion: 10240
TargetVersion: 16299
This library checks at runtime if the UniversalApiContract Version 5 is present.
If yes, it will use the new NavigationView control that.
UWP App:
MinVersion: 10240
TargetVersion: 10240
This app references the UWP Library project.
When I run this app on my Computer, which has Windows 10 Version 16299 installed, the following happens:
The UWP Library checks at runtime for the api contract. As I have the newest version of Windows 10, yes it is present.
Then it tries to create the NavigationView control, and I get a TypeLoadException with the message Could not find Windows Runtime type 'Windows.UI.Xaml.Controls.NavigationView'.
What? Why? Does the ApiInformation class not respect the target version of the running app?
What can I do to work around this issue?
I thought ApiInformation was the way to avoid this, but apparently not?!
Here is a Github repository showcasing the error:
https://github.com/haefele/ApiInformationTargetVersionFail
If you set the target-version of the MyApp project to 16299, everything works fine.
回答1:
[Edit May 29 2018]
The methods of the ApiInformation type are based on a trivial lookup of the WinRT metadata on disk -- if the metadata is there, the call succeeds. This enables "light-up" of new features on new platforms without increasing your minimum version. What's important though is that ApiInformation
knows nothing about the implementation of the API: sometimes it might be missing (eg on early "Insider" builds of the OS) and sometimes it might not work due to "quirks" (see example below). .NET also had a different view of the world due to the way the JIT and .NET Native toolchains work.
This can cause problems...
.NET apps use a concept of a "Union WinMD" which is the union of all known types (including Extension SDKs) that exist in the Windows SDK that corresponds to the MaxVersionTested
setting of the app. If you're running the app on a down-level platform, ApiInformation
will tell you the API doesn't exist, but .NET can still JIT methods based on the Union WinMD and perform some reflection tasks. If you actually try and call the API (because you forgot the ApiInformation
check) you will get a MissingMethodException
at runtime because the API doesn't really exist.
A different problem can occur if you include a higher-versioned .NET library inside a lower-versioned app and then try to run it on the higher-versioned build of the OS. In this case, ApiInformation
will succeed because the type exists in the system metadata, but .NET will throw a MissingMethodException
at runtime because the type didn't exist in the Union WinMD used to build the app.
Important: This is based on the Target Version (aka MaxVersionTested
) of the app, not the library!
If you build a release version of the app, you will even see the .NET Native toolchain display a warning such as this in the Output window:
warning : ILTransform : warning ILT0003: Method 'Foo.Bar()' will always throw an exception due to the missing method 'SomeNewType.NewMethod()'. There may have been a missing assembly.
There is no good way around this, other than to build your application with the same target version as the library (so that it can resolve all the references).
Another problem you can encounter is when your app (or a library it consumes) use APIs "from the future" that didn't exist in the OS listed as the MaxVersionTested
of the app. Many of the APIs will work, but some don't due to incompatibilities with the simulated legacy mode the app is running in.
Hypothetical example of the library problem
Imagine that version X of the OS only supported black-and-white apps, where the background is always white and text, graphics, etc. are always black. Apps are built using this underlying assumption - including having graphics buffers that allocate only 1-bit-per-pixel, or never worrying about text being invisible because the background and foreground colours are the same. Everything is fine.
Now version Y of the OS comes out, and it supports colour graphics (say, 8-bits-per-pixel). Along with this new feature comes a pair of new APIs, SetForegroundColor()
and SetBackgroundColor()
that let you choose whatever colour you want. Any app (or library) that asks ApiInformation
whether these two new APIs exists will succeed on version Y of the OS, and any app with a MaxVersionTested
of at least Y can use them successfully. But for compatibility reasons they cannot work in an app that only targeted version X because it has no idea colours exist. Their graphics buffers are the wrong size, their text might become invisible, and so on. So the APIs will fail at runtime when used in an X-targeted app, even though the OS has the metadata (and the implementation) to support them.
Unfortunately there is no good way of handling this situation today, but it is a relatively rare occurrence. It is equivalent to a legacy Win32 library using LoadLibrary
/ GetProcAddress
(or a legacy .NET library using reflection) to discover APIs that are "from the future."
来源:https://stackoverflow.com/questions/47540577/does-apiinformation-not-respect-the-app-target-version