diff --git a/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs b/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs
index 628f73ca80..420c56111f 100644
--- a/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs
+++ b/src/Avalonia.Controls/Platform/ExportWindowingSubsystemAttribute.cs
@@ -9,16 +9,19 @@ namespace Avalonia.Platform
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class ExportWindowingSubsystemAttribute : Attribute
{
- public ExportWindowingSubsystemAttribute(OperatingSystemType requiredRuntimePlatform, int priority, string name, Type initializationType, string initializationMethod)
+ public ExportWindowingSubsystemAttribute(OperatingSystemType requiredRuntimePlatform, int priority, string name, Type initializationType,
+ string initializationMethod, Type environmentChecker = null)
{
Name = name;
InitializationType = initializationType;
InitializationMethod = initializationMethod;
+ EnvironmentChecker = environmentChecker;
RequiredOS = requiredRuntimePlatform;
Priority = priority;
}
public string InitializationMethod { get; private set; }
+ public Type EnvironmentChecker { get; }
public Type InitializationType { get; private set; }
public string Name { get; private set; }
public int Priority { get; private set; }
diff --git a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
index f643bfe6e2..fc7fdd2431 100644
--- a/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
+++ b/src/Avalonia.DotNetFrameworkRuntime/AppBuilder.cs
@@ -31,6 +31,20 @@ namespace Avalonia
Instance = app;
}
+ bool CheckEnvironment(Type checkerType)
+ {
+ if (checkerType == null)
+ return true;
+ try
+ {
+ return ((IModuleEnvironmentChecker) Activator.CreateInstance(checkerType)).IsCompatible;
+ }
+ catch
+ {
+ return false;
+ }
+ }
+
///
/// Instructs the to use the best settings for the platform.
///
@@ -43,13 +57,13 @@ namespace Avalonia
var windowingSubsystemAttribute = (from assembly in RuntimePlatform.GetLoadedAssemblies()
from attribute in assembly.GetCustomAttributes()
- where attribute.RequiredOS == os
+ where attribute.RequiredOS == os && CheckEnvironment(attribute.EnvironmentChecker)
orderby attribute.Priority ascending
select attribute).First();
var renderingSubsystemAttribute = (from assembly in RuntimePlatform.GetLoadedAssemblies()
from attribute in assembly.GetCustomAttributes()
- where attribute.RequiredOS == os
+ where attribute.RequiredOS == os && CheckEnvironment(attribute.EnvironmentChecker)
where attribute.RequiresWindowingSubsystem == null
|| attribute.RequiresWindowingSubsystem == windowingSubsystemAttribute.Name
orderby attribute.Priority ascending
diff --git a/src/Avalonia.Visuals/Avalonia.Visuals.csproj b/src/Avalonia.Visuals/Avalonia.Visuals.csproj
index dd43f1d8f0..a0ed19d2b4 100644
--- a/src/Avalonia.Visuals/Avalonia.Visuals.csproj
+++ b/src/Avalonia.Visuals/Avalonia.Visuals.csproj
@@ -102,6 +102,7 @@
+
diff --git a/src/Avalonia.Visuals/Platform/ExportRenderingSubsystemAttribute.cs b/src/Avalonia.Visuals/Platform/ExportRenderingSubsystemAttribute.cs
index a020bf9b1c..73526d7622 100644
--- a/src/Avalonia.Visuals/Platform/ExportRenderingSubsystemAttribute.cs
+++ b/src/Avalonia.Visuals/Platform/ExportRenderingSubsystemAttribute.cs
@@ -9,16 +9,19 @@ namespace Avalonia.Platform
[AttributeUsage(AttributeTargets.Assembly, AllowMultiple = true)]
public class ExportRenderingSubsystemAttribute : Attribute
{
- public ExportRenderingSubsystemAttribute(OperatingSystemType requiredOS, int priority, string name, Type initializationType, string initializationMethod)
+ public ExportRenderingSubsystemAttribute(OperatingSystemType requiredOS, int priority, string name, Type initializationType, string initializationMethod,
+ Type environmentChecker = null)
{
Name = name;
InitializationType = initializationType;
InitializationMethod = initializationMethod;
+ EnvironmentChecker = environmentChecker;
RequiredOS = requiredOS;
Priority = priority;
}
public string InitializationMethod { get; private set; }
+ public Type EnvironmentChecker { get; }
public Type InitializationType { get; private set; }
public string Name { get; private set; }
public int Priority { get; private set; }
diff --git a/src/Avalonia.Visuals/Platform/IModuleEnvironmentChecker.cs b/src/Avalonia.Visuals/Platform/IModuleEnvironmentChecker.cs
new file mode 100644
index 0000000000..d8d3a19af6
--- /dev/null
+++ b/src/Avalonia.Visuals/Platform/IModuleEnvironmentChecker.cs
@@ -0,0 +1,7 @@
+namespace Avalonia.Platform
+{
+ public interface IModuleEnvironmentChecker
+ {
+ bool IsCompatible { get; }
+ }
+}
\ No newline at end of file
diff --git a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
index a84a56d0d7..a055a337eb 100644
--- a/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
+++ b/src/Windows/Avalonia.Direct2D1/Avalonia.Direct2D1.csproj
@@ -83,6 +83,7 @@
+
diff --git a/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs b/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs
index 3ab086b156..63d9c5bfe3 100644
--- a/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs
+++ b/src/Windows/Avalonia.Direct2D1/Properties/AssemblyInfo.cs
@@ -6,5 +6,6 @@ using Avalonia.Platform;
using Avalonia.Direct2D1;
[assembly: AssemblyTitle("Avalonia.Direct2D1")]
-[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 1, "Direct2D1", typeof(Direct2D1Platform), nameof(Direct2D1Platform.Initialize))]
+[assembly: ExportRenderingSubsystem(OperatingSystemType.WinNT, 1, "Direct2D1", typeof(Direct2D1Platform), nameof(Direct2D1Platform.Initialize),
+ typeof(WindowsVersionChecker))]
diff --git a/src/Windows/Avalonia.Direct2D1/WindowsVersionChecker.cs b/src/Windows/Avalonia.Direct2D1/WindowsVersionChecker.cs
new file mode 100644
index 0000000000..875167b71a
--- /dev/null
+++ b/src/Windows/Avalonia.Direct2D1/WindowsVersionChecker.cs
@@ -0,0 +1,15 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using Avalonia.Platform;
+
+namespace Avalonia.Direct2D1
+{
+ class WindowsVersionChecker : IModuleEnvironmentChecker
+ {
+ //Direct2D backend doesn't work with Win7 anymore
+ public bool IsCompatible => Environment.OSVersion.Version >= new Version(6, 2);
+ }
+}
diff --git a/src/Windows/Avalonia.Win32.NetStandard/Avalonia.Win32.NetStandard.csproj b/src/Windows/Avalonia.Win32.NetStandard/Avalonia.Win32.NetStandard.csproj
index e4a34a8b91..95d1b8a345 100644
--- a/src/Windows/Avalonia.Win32.NetStandard/Avalonia.Win32.NetStandard.csproj
+++ b/src/Windows/Avalonia.Win32.NetStandard/Avalonia.Win32.NetStandard.csproj
@@ -75,6 +75,8 @@
+
+
diff --git a/src/Windows/Avalonia.Win32.NetStandard/ComStreamWrapper.cs b/src/Windows/Avalonia.Win32.NetStandard/ComStreamWrapper.cs
new file mode 100644
index 0000000000..083021f58d
--- /dev/null
+++ b/src/Windows/Avalonia.Win32.NetStandard/ComStreamWrapper.cs
@@ -0,0 +1,197 @@
+//
+// System.Drawing.ComIStreamWrapper.cs
+//
+// Author:
+// Kornél Pál
+//
+// Copyright (C) 2005-2008 Kornél Pál
+//
+
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.IO;
+using System.Reflection;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+using STATSTG = System.Runtime.InteropServices.ComTypes.STATSTG;
+
+namespace Avalonia.Win32
+{
+ // Stream to IStream wrapper for COM interop
+ internal sealed class ComIStreamWrapper : IStream
+ {
+ private const int STG_E_INVALIDFUNCTION = unchecked((int)0x80030001);
+
+ private readonly Stream baseStream;
+ private long position = -1;
+
+ internal ComIStreamWrapper(Stream stream)
+ {
+ baseStream = stream;
+ }
+
+ private void SetSizeToPosition()
+ {
+ if (position != -1)
+ {
+ if (position > baseStream.Length)
+ baseStream.SetLength(position);
+ baseStream.Position = position;
+ position = -1;
+ }
+ }
+
+ public void Read(byte[] pv, int cb, IntPtr pcbRead)
+ {
+ int read = 0;
+
+ if (cb != 0)
+ {
+ SetSizeToPosition();
+ read = baseStream.Read(pv, 0, cb);
+ }
+
+ if (pcbRead != IntPtr.Zero)
+ Marshal.WriteInt32(pcbRead, read);
+ }
+
+ public void Write(byte[] pv, int cb, IntPtr pcbWritten)
+ {
+ if (cb != 0)
+ {
+ SetSizeToPosition();
+ baseStream.Write(pv, 0, cb);
+ }
+
+ if (pcbWritten != IntPtr.Zero)
+ Marshal.WriteInt32(pcbWritten, cb);
+ }
+
+ public void Seek(long dlibMove, int dwOrigin, IntPtr plibNewPosition)
+ {
+ long length = baseStream.Length;
+ long newPosition;
+
+ switch ((SeekOrigin)dwOrigin)
+ {
+ case SeekOrigin.Begin:
+ newPosition = dlibMove;
+ break;
+ case SeekOrigin.Current:
+ if (position == -1)
+ newPosition = baseStream.Position + dlibMove;
+ else
+ newPosition = position + dlibMove;
+ break;
+ case SeekOrigin.End:
+ newPosition = length + dlibMove;
+ break;
+ default:
+ throw new COMException(null, STG_E_INVALIDFUNCTION);
+ }
+
+ if (newPosition > length)
+ position = newPosition;
+ else
+ {
+ baseStream.Position = newPosition;
+ position = -1;
+ }
+
+ if (plibNewPosition != IntPtr.Zero)
+ Marshal.WriteInt64(plibNewPosition, newPosition);
+ }
+
+ public void SetSize(long libNewSize)
+ {
+ baseStream.SetLength(libNewSize);
+ }
+
+ public void CopyTo(IStream pstm, long cb, IntPtr pcbRead, IntPtr pcbWritten)
+ {
+ byte[] buffer;
+ long written = 0;
+ int read;
+ int count;
+
+ if (cb != 0)
+ {
+ if (cb < 4096)
+ count = (int)cb;
+ else
+ count = 4096;
+ buffer = new byte[count];
+ SetSizeToPosition();
+ while (true)
+ {
+ if ((read = baseStream.Read(buffer, 0, count)) == 0)
+ break;
+ pstm.Write(buffer, read, IntPtr.Zero);
+ written += read;
+ if (written >= cb)
+ break;
+ if (cb - written < 4096)
+ count = (int)(cb - written);
+ }
+ }
+
+ if (pcbRead != IntPtr.Zero)
+ Marshal.WriteInt64(pcbRead, written);
+ if (pcbWritten != IntPtr.Zero)
+ Marshal.WriteInt64(pcbWritten, written);
+ }
+
+ public void Commit(int grfCommitFlags)
+ {
+ baseStream.Flush();
+ SetSizeToPosition();
+ }
+
+ public void Revert()
+ {
+ throw new COMException(null, STG_E_INVALIDFUNCTION);
+ }
+
+ public void LockRegion(long libOffset, long cb, int dwLockType)
+ {
+ throw new COMException(null, STG_E_INVALIDFUNCTION);
+ }
+
+ public void UnlockRegion(long libOffset, long cb, int dwLockType)
+ {
+ throw new COMException(null, STG_E_INVALIDFUNCTION);
+ }
+
+ public void Stat(out STATSTG pstatstg, int grfStatFlag)
+ {
+ pstatstg = new STATSTG();
+ pstatstg.cbSize = baseStream.Length;
+ }
+
+ public void Clone(out IStream ppstm)
+ {
+ ppstm = null;
+ throw new COMException(null, STG_E_INVALIDFUNCTION);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Windows/Avalonia.Win32.NetStandard/Gdip.cs b/src/Windows/Avalonia.Win32.NetStandard/Gdip.cs
new file mode 100644
index 0000000000..b3d1c28689
--- /dev/null
+++ b/src/Windows/Avalonia.Win32.NetStandard/Gdip.cs
@@ -0,0 +1,128 @@
+//
+// Code copy-pasted from from Mono / System.Drawing.*.cs
+// Original license below:
+//
+// Authors:
+// Alexandre Pigolkine (pigolkine@gmx.de)
+// Jordi Mas (jordi@ximian.com)
+// Sanjay Gupta (gsanjay@novell.com)
+// Ravindra (rkumar@novell.com)
+// Peter Dennis Bartok (pbartok@novell.com)
+// Sebastien Pouliot
+//
+//
+// Copyright (C) 2004, 2007 Novell, Inc (http://www.novell.com)
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+//
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Avalonia.Win32
+{
+ static class Gdip
+ {
+ public enum Status
+ {
+ Ok = 0,
+ GenericError = 1,
+ InvalidParameter = 2,
+ OutOfMemory = 3,
+ ObjectBusy = 4,
+ InsufficientBuffer = 5,
+ NotImplemented = 6,
+ Win32Error = 7,
+ WrongState = 8,
+ Aborted = 9,
+ FileNotFound = 10,
+ ValueOverflow = 11,
+ AccessDenied = 12,
+ UnknownImageFormat = 13,
+ FontFamilyNotFound = 14,
+ FontStyleNotFound = 15,
+ NotTrueTypeFont = 16,
+ UnsupportedGdiplusVersion = 17,
+ GdiplusNotInitialized = 18,
+ PropertyNotFound = 19,
+ PropertyNotSupported = 20,
+ ProfileNotFound = 21
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct GdiplusStartupInput
+ {
+ // internalted to silent compiler
+ internal uint GdiplusVersion;
+ internal IntPtr DebugEventCallback;
+ internal int SuppressBackgroundThread;
+ internal int SuppressExternalCodecs;
+
+ internal static GdiplusStartupInput MakeGdiplusStartupInput()
+ {
+ GdiplusStartupInput result = new GdiplusStartupInput();
+ result.GdiplusVersion = 1;
+ result.DebugEventCallback = IntPtr.Zero;
+ result.SuppressBackgroundThread = 0;
+ result.SuppressExternalCodecs = 0;
+ return result;
+ }
+ }
+
+ [StructLayout(LayoutKind.Sequential)]
+ internal struct GdiplusStartupOutput
+ {
+ internal IntPtr NotificationHook;
+ internal IntPtr NotificationUnhook;
+
+ internal static GdiplusStartupOutput MakeGdiplusStartupOutput()
+ {
+ GdiplusStartupOutput result = new GdiplusStartupOutput();
+ result.NotificationHook = result.NotificationUnhook = IntPtr.Zero;
+ return result;
+ }
+ }
+
+
+ [DllImport("gdiplus.dll")]
+ public static extern Status GdiplusStartup(ref ulong token, ref GdiplusStartupInput input, ref GdiplusStartupOutput output);
+
+ [DllImport("gdiplus.dll", ExactSpelling = true, CharSet = CharSet.Unicode)]
+ public static extern Status GdipLoadImageFromStream([MarshalAs(UnmanagedType.Interface, MarshalTypeRef = typeof(IStream))] IStream stream, out IntPtr image);
+ [DllImport("gdiplus.dll")]
+ public static extern Status GdipCreateHICONFromBitmap(IntPtr bmp, out IntPtr HandleIcon);
+
+ [DllImport("gdiplus.dll")]
+ internal static extern Status GdipDisposeImage(IntPtr image);
+
+ static Gdip()
+ {
+ ulong token = 0;
+ var input = GdiplusStartupInput.MakeGdiplusStartupInput();
+ var output = GdiplusStartupOutput.MakeGdiplusStartupOutput();
+ GdiplusStartup(ref token, ref input, ref output);
+ }
+ }
+}
diff --git a/src/Windows/Avalonia.Win32.NetStandard/IconImpl.cs b/src/Windows/Avalonia.Win32.NetStandard/IconImpl.cs
index f21737027f..49d039e655 100644
--- a/src/Windows/Avalonia.Win32.NetStandard/IconImpl.cs
+++ b/src/Windows/Avalonia.Win32.NetStandard/IconImpl.cs
@@ -2,6 +2,8 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Runtime.InteropServices;
+using System.Runtime.InteropServices.ComTypes;
using System.Text;
using System.Threading.Tasks;
using Avalonia.Platform;
@@ -10,10 +12,34 @@ namespace Avalonia.Win32
{
public class IconImpl : IWindowIconImpl
{
- public IntPtr HIcon { get; set; }
+ private readonly MemoryStream _ms;
+
+ public IconImpl(Stream data)
+ {
+ _ms = new MemoryStream();
+ data.CopyTo(_ms);
+ _ms.Seek(0, SeekOrigin.Begin);
+ IntPtr bitmap;
+ var status = Gdip.GdipLoadImageFromStream(new ComIStreamWrapper(_ms), out bitmap);
+ if (status != Gdip.Status.Ok)
+ throw new Exception("Unable to load icon, gdip status: " + (int) status);
+ IntPtr icon;
+ status = Gdip.GdipCreateHICONFromBitmap(bitmap, out icon);
+ if (status != Gdip.Status.Ok)
+ throw new Exception("Unable to create HICON, gdip status: " + (int)status);
+ Gdip.GdipDisposeImage(bitmap);
+ HIcon = icon;
+ }
+
+ public IntPtr HIcon { get;}
public void Save(Stream outputStream)
{
- throw new NotImplementedException();
+ lock (_ms)
+ {
+ _ms.Seek(0, SeekOrigin.Begin);
+ _ms.CopyTo(outputStream);
+ }
}
+
}
}
diff --git a/src/Windows/Avalonia.Win32.NetStandard/NativeWin32Platform.cs b/src/Windows/Avalonia.Win32.NetStandard/NativeWin32Platform.cs
index a269334be7..2695a5b8b6 100644
--- a/src/Windows/Avalonia.Win32.NetStandard/NativeWin32Platform.cs
+++ b/src/Windows/Avalonia.Win32.NetStandard/NativeWin32Platform.cs
@@ -11,10 +11,20 @@ namespace Avalonia.Win32
partial class Win32Platform
{
//TODO: An actual implementation
- public IWindowIconImpl LoadIcon(string fileName) => new IconImpl();
+ public IWindowIconImpl LoadIcon(string fileName)
+ {
+ //No file IO for netstandard, still waiting for proper net core tooling
+ throw new NotSupportedException();
+ }
- public IWindowIconImpl LoadIcon(Stream stream) => new IconImpl();
+ public IWindowIconImpl LoadIcon(Stream stream) => new IconImpl(stream);
- public IWindowIconImpl LoadIcon(IBitmapImpl bitmap) => new IconImpl();
+ public IWindowIconImpl LoadIcon(IBitmapImpl bitmap)
+ {
+ var ms = new MemoryStream();
+ bitmap.Save(ms);
+ ms.Seek(0, SeekOrigin.Begin);
+ return new IconImpl(ms);
+ }
}
}