dotnet/runtime

System.Drawing.ImageConverter raise the System.PlatformNotSupportedException on Linux

Closed this issue · 13 comments

dotnet --version
3.1.100
nuget packeges:

  • System.Drawing.Common 4.7.0
  • System.Configuration.ConfigurationManager 4.7.0
    OS: Linux

Steps to reproduce:
Environment: Linux with dotnet 3.1.100

  1. dotnet new console
  2. dotnet add package System.Drawing.Common
    Installed version: 4.7.0
  3. modify Program.cs file
    add new lines:
    var converter = System.ComponentModel.TypeDescriptor.GetConverter(typeof(System.Drawing.Image)); Console.WriteLine($"{converter?.GetType()}");
  4. dotnet run
    Console output:

"System.ComponentModel.TypeConverter"

  1. dotnet add package System.Configuration.ConfigurationManager
    Installed version: 4.7.0
  2. dotnet run
    Console output:

Unhandled exception. System.Reflection.TargetInvocationException: Exception has been thrown by the target of an invocation.
---> System.PlatformNotSupportedException: System.Windows.Extensions types are not supported on this platform.
at System.Drawing.ImageConverter..ctor()
--- End of inner exception stack trace ---
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean wrapExceptions, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& hasNoDefaultCtor)
at System.RuntimeType.CreateInstanceDefaultCtorSlow(Boolean publicOnly, Boolean wrapExceptions, Boolean fillCache)
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, Boolean wrapExceptions)
at System.Activator.CreateInstance(Type type, Boolean nonPublic, Boolean wrapExceptions)
at System.Activator.CreateInstance(Type type)
at System.ComponentModel.ReflectTypeDescriptionProvider.CreateInstance(Type objectType, Type callingType)
at System.ComponentModel.ReflectTypeDescriptionProvider.ReflectedTypeData.GetConverter(Object instance)
at System.ComponentModel.TypeDescriptor.TypeDescriptionNode.DefaultTypeDescriptor.System.ComponentModel.ICustomTypeDescriptor.GetConverter()
at System.ComponentModel.TypeDescriptor.GetConverter(Type type)

@ericstj was most recently thinking about type converters

Have there been any insights with this issue? I'm stuck. It all worked with .NET Core 2.2 and System.Drawing.Common, 4.5.0.

It all worked with .NET Core 2.2 and System.Drawing.Common, 4.5.0.

So in 2.2 there was no TypeConverter registered, so you'd get a default TypeConverter that can't convert anything.

In 3.0 ImageConverter was added in System.Windows.Extensions. That library isn't supported on linux today, so it throws. dotnet/corefx#33092 /cc @safern

To restore the 2.x behavior, you can apply a TypeConverter attribute to roll it back to the default (or even, if you wanted, implement your own ImageConverter).

TypeDescriptor.AddAttributes(typeof(System.Drawing.Image), new TypeConverterAttribute(typeof(TypeConverter)));

We should consider this another ref-count on https://github.com/dotnet/corefx/issues/36949

Ok cool. My particular set of conflicting packages is between system.drawing.common and
Microsoft.AspNetCore.SpaServices.Extensions but I'm gathering it's the same issue. Thanks!

We should just bring the TypeConverters for System.Drawing into System.Drawing.Common (out of System.Windows.Extensions). I believe these are sufficiently decoupled from the pure drawing usage (attribute + seperate types) to avoid bringing TypeConverter into the working set of an application that only uses drawing types.

We should just bring the TypeConverters for System.Drawing into System.Drawing.Common (out of System.Windows.Extensions)

I believe the reason why we put them on System.Windows.Extensions was to avoid bringing System.ComponentModel.TypeConverters into the System.Drawing.Common closure.

I don't think that's the right measure. As I mentioned above: I believe these are sufficiently decoupled from the pure drawing usage (attribute + separate types) to avoid bringing TypeConverter into the working set of an application that only uses drawing types. Working set is a better measure. If the working set changes for an app that isn't otherwise using the typeconverters that might be a concern.

If the working set changes for an app that isn't otherwise using the typeconverters that might be a concern.

Makes sense.

cc: @JeremyKuhne

@ericstj another hit of this in Powershell (PowerShell/PowerShell#12513) -- interesting enough, we bumped the Assembly versions, so System.Runtime.WindowsExtensions is now 5.0.0.0 and the TypeConverterAttribute still points to 4.0.0.0, so it fails to load.

I think this is starting to be a bigger pain point and maybe we should take the dependency on TypeConverter in System.Drawing.

Pointing at 4.0.0.0 is fine (and in fact, using the older System.Windows.Extensions with newer shared framework is technically supported), assembly loader should load the 5.0.0.0 version if that is what is present in the app. IMHO this another symptom of Powershell doing something fragile and trying to implement assembly loading policy on their own.

Makes sense. I thought they were using the regular TypeConverter routine and not trying to load the type themselves.

I chatted with @eerhardt about how linker treats TypeConverter last week, and we believe this is safe to bring into System.Drawing.Common now.

Sounds good. FWIW we already brought typeconverter into System.Drawing.Common closure for .net 5. I can take care of this one.