This should fix most icon scaling issues

Improved the IniReader a bit and replaced some old code.
This commit is contained in:
Robin Krom 2020-02-11 22:41:55 +01:00
parent 41baf27d84
commit 4a958be8b5
35 changed files with 2767 additions and 301 deletions

View file

@ -16,7 +16,7 @@
<PublishRepositoryUrl>true</PublishRepositoryUrl> <PublishRepositoryUrl>true</PublishRepositoryUrl>
<!-- Optional: Embed source files that are not tracked by the source control manager in the PDB --> <!-- Optional: Embed source files that are not tracked by the source control manager in the PDB -->
<EmbedUntrackedSources>true</EmbedUntrackedSources> <EmbedUntrackedSources>true</EmbedUntrackedSources>
<TargetFramework>net471</TargetFramework> <TargetFramework>net471</TargetFramework>
</PropertyGroup> </PropertyGroup>
<!-- ILLinker and single file settings --> <!-- ILLinker and single file settings -->
@ -25,8 +25,8 @@
</ItemGroup> </ItemGroup>
<PropertyGroup Condition="$(MSBuildProjectName) == 'Greenshot'"> <PropertyGroup Condition="$(MSBuildProjectName) == 'Greenshot'">
<IncludeSymbolsInSingleFile>false</IncludeSymbolsInSingleFile> <IncludeSymbolsInSingleFile>false</IncludeSymbolsInSingleFile>
<ShowLinkerSizeComparison>true</ShowLinkerSizeComparison> <ShowLinkerSizeComparison>true</ShowLinkerSizeComparison>
</PropertyGroup> </PropertyGroup>
<PropertyGroup Condition="'$(Configuration)' != 'Debug' And !$(MSBuildProjectName.Contains('Test')) And !$(MSBuildProjectName.Contains('Demo'))"> <PropertyGroup Condition="'$(Configuration)' != 'Debug' And !$(MSBuildProjectName.Contains('Test')) And !$(MSBuildProjectName.Contains('Demo'))">

View file

@ -33,9 +33,9 @@ namespace Greenshot.Controls {
private static Image _scaledCheckbox; private static Image _scaledCheckbox;
protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) { protected override void OnRenderItemCheck(ToolStripItemImageRenderEventArgs e) {
if (_scaledCheckbox == null || _scaledCheckbox.Size != CoreConfig.IconSize) { if (_scaledCheckbox == null || _scaledCheckbox.Size != CoreConfig.ScaledIconSize) {
_scaledCheckbox?.Dispose(); _scaledCheckbox?.Dispose();
_scaledCheckbox = ImageHelper.ResizeImage(e.Image, true, CoreConfig.IconSize.Width, CoreConfig.IconSize.Height, null); _scaledCheckbox = ImageHelper.ResizeImage(e.Image, true, CoreConfig.ScaledIconSize.Width, CoreConfig.ScaledIconSize.Height, null);
} }
Rectangle old = e.ImageRectangle; Rectangle old = e.ImageRectangle;
ToolStripItemImageRenderEventArgs clone = new ToolStripItemImageRenderEventArgs(e.Graphics, e.Item, _scaledCheckbox, new Rectangle(old.X, 0, old.Width, old.Height)); ToolStripItemImageRenderEventArgs clone = new ToolStripItemImageRenderEventArgs(e.Graphics, e.Item, _scaledCheckbox, new Rectangle(old.X, 0, old.Width, old.Height));

View file

@ -102,7 +102,7 @@ namespace Greenshot.Drawing {
}; };
using GraphicsPath path = new GraphicsPath(); using GraphicsPath path = new GraphicsPath();
path.AddLine(Left, Top, Left + Width, Top + Height); path.AddLine(Left, Top, Left + Width, Top + Height);
return path.IsOutlineVisible(x, y, pen); return path.IsOutlineVisible(x, y, pen);
} }
return false; return false;
} }

View file

@ -293,7 +293,7 @@ namespace Greenshot {
// toolsToolStrip // toolsToolStrip
// //
this.toolsToolStrip.ClickThrough = true; this.toolsToolStrip.ClickThrough = true;
this.toolsToolStrip.ImageScalingSize = coreConfiguration.IconSize; this.toolsToolStrip.ImageScalingSize = coreConfiguration.ScaledIconSize;
this.toolsToolStrip.Dock = System.Windows.Forms.DockStyle.None; this.toolsToolStrip.Dock = System.Windows.Forms.DockStyle.None;
this.toolsToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; this.toolsToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
this.toolsToolStrip.Renderer = new CustomToolStripProfessionalRenderer(); this.toolsToolStrip.Renderer = new CustomToolStripProfessionalRenderer();
@ -529,7 +529,7 @@ namespace Greenshot {
// menuStrip1 // menuStrip1
// //
this.menuStrip1.ClickThrough = true; this.menuStrip1.ClickThrough = true;
this.menuStrip1.ImageScalingSize = coreConfiguration.IconSize; this.menuStrip1.ImageScalingSize = coreConfiguration.ScaledIconSize;
this.menuStrip1.Dock = System.Windows.Forms.DockStyle.Fill; this.menuStrip1.Dock = System.Windows.Forms.DockStyle.Fill;
this.menuStrip1.Stretch = true; this.menuStrip1.Stretch = true;
this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] {
@ -858,7 +858,7 @@ namespace Greenshot {
// destinationsToolStrip // destinationsToolStrip
// //
this.destinationsToolStrip.ClickThrough = true; this.destinationsToolStrip.ClickThrough = true;
this.destinationsToolStrip.ImageScalingSize = coreConfiguration.IconSize; this.destinationsToolStrip.ImageScalingSize = coreConfiguration.ScaledIconSize;
this.destinationsToolStrip.Dock = System.Windows.Forms.DockStyle.Fill; this.destinationsToolStrip.Dock = System.Windows.Forms.DockStyle.Fill;
this.destinationsToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; this.destinationsToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
this.destinationsToolStrip.Name = "toolStrip1"; this.destinationsToolStrip.Name = "toolStrip1";
@ -1014,10 +1014,10 @@ namespace Greenshot {
// //
this.propertiesToolStrip.AutoSize = false; this.propertiesToolStrip.AutoSize = false;
this.propertiesToolStrip.ClickThrough = true; this.propertiesToolStrip.ClickThrough = true;
this.propertiesToolStrip.ImageScalingSize = coreConfiguration.IconSize; this.propertiesToolStrip.ImageScalingSize = coreConfiguration.ScaledIconSize;
this.propertiesToolStrip.Dock = System.Windows.Forms.DockStyle.Fill; this.propertiesToolStrip.Dock = System.Windows.Forms.DockStyle.Fill;
this.propertiesToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden; this.propertiesToolStrip.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden;
this.propertiesToolStrip.MinimumSize = new System.Drawing.Size(150, coreConfiguration.IconSize.Height + 10); this.propertiesToolStrip.MinimumSize = new System.Drawing.Size(150, coreConfiguration.ScaledIconSize.Height + 10);
this.propertiesToolStrip.Name = "propertiesToolStrip"; this.propertiesToolStrip.Name = "propertiesToolStrip";
this.propertiesToolStrip.Stretch = true; this.propertiesToolStrip.Stretch = true;
this.propertiesToolStrip.TabIndex = 2; this.propertiesToolStrip.TabIndex = 2;

View file

@ -203,7 +203,7 @@ namespace Greenshot {
// contextmenu_quicksettings // contextmenu_quicksettings
// //
this.contextmenu_quicksettings.Name = "contextmenu_quicksettings"; this.contextmenu_quicksettings.Name = "contextmenu_quicksettings";
this.contextmenu_quicksettings.Size = new System.Drawing.Size(170, coreConfiguration.IconSize.Height + 8); this.contextmenu_quicksettings.Size = new System.Drawing.Size(170, coreConfiguration.ScaledIconSize.Height + 8);
// //
// contextmenu_settings // contextmenu_settings
// //

View file

@ -549,7 +549,7 @@ namespace Greenshot {
/// <param name="e"></param> /// <param name="e"></param>
private void OnIconSizeChanged(object sender, PropertyChangedEventArgs e) { private void OnIconSizeChanged(object sender, PropertyChangedEventArgs e) {
if (e.PropertyName == "IconSize") { if (e.PropertyName == "IconSize") {
contextMenu.ImageScalingSize = coreConfiguration.IconSize; contextMenu.ImageScalingSize = coreConfiguration.ScaledIconSize;
string ieExePath = PluginUtils.GetExePath("iexplore.exe"); string ieExePath = PluginUtils.GetExePath("iexplore.exe");
if (!string.IsNullOrEmpty(ieExePath)) { if (!string.IsNullOrEmpty(ieExePath)) {
contextmenu_captureie.Image = PluginUtils.GetCachedExeIcon(ieExePath, 0); contextmenu_captureie.Image = PluginUtils.GetCachedExeIcon(ieExePath, 0);
@ -614,7 +614,7 @@ namespace Greenshot {
/// <returns>true if onedrive has hotkeys turned on</returns> /// <returns>true if onedrive has hotkeys turned on</returns>
private static bool IsOneDriveBlockingHotkey() private static bool IsOneDriveBlockingHotkey()
{ {
if (!Environment.OSVersion.IsWindows10()) if (!WindowsVersion.IsWindows10OrLater)
{ {
return false; return false;
} }

View file

@ -334,6 +334,7 @@ namespace Greenshot {
listview_destinations.Items.Clear(); listview_destinations.Items.Clear();
listview_destinations.ListViewItemSorter = new ListviewWithDestinationComparer(); listview_destinations.ListViewItemSorter = new ListviewWithDestinationComparer();
ImageList imageList = new ImageList(); ImageList imageList = new ImageList();
imageList.ImageSize = coreConfiguration.ScaledIconSize;
listview_destinations.SmallImageList = imageList; listview_destinations.SmallImageList = imageList;
int imageNr = -1; int imageNr = -1;
foreach (IDestination currentDestination in DestinationHelper.GetAllDestinations()) { foreach (IDestination currentDestination in DestinationHelper.GetAllDestinations()) {
@ -422,7 +423,7 @@ namespace Greenshot {
numericUpDown_daysbetweencheck.Value = coreConfiguration.UpdateCheckInterval; numericUpDown_daysbetweencheck.Value = coreConfiguration.UpdateCheckInterval;
numericUpDown_daysbetweencheck.Enabled = !coreConfiguration.Values["UpdateCheckInterval"].IsFixed; numericUpDown_daysbetweencheck.Enabled = !coreConfiguration.Values["UpdateCheckInterval"].IsFixed;
numericUpdownIconSize.Value = coreConfiguration.IconSize.Width /16 * 16; numericUpdownIconSize.Value = coreConfiguration.ScaledIconSize.Width /16 * 16;
CheckDestinationSettings(); CheckDestinationSettings();
} }

View file

@ -50,9 +50,9 @@ namespace Greenshot.Forms {
Identifier = identifier; Identifier = identifier;
CheckOnClick = false; CheckOnClick = false;
_multiCheckAllowed = allowMultiCheck; _multiCheckAllowed = allowMultiCheck;
if (_defaultImage == null || _defaultImage.Size != CoreConfig.IconSize) { if (_defaultImage == null || _defaultImage.Size != CoreConfig.ScaledIconSize) {
_defaultImage?.Dispose(); _defaultImage?.Dispose();
_defaultImage = ImageHelper.CreateEmpty(CoreConfig.IconSize.Width, CoreConfig.IconSize.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb, Color.Transparent, 96f, 96f); _defaultImage = ImageHelper.CreateEmpty(CoreConfig.ScaledIconSize.Width, CoreConfig.ScaledIconSize.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb, Color.Transparent, 96f, 96f);
} }
Image = _defaultImage; Image = _defaultImage;
} }

View file

@ -153,7 +153,7 @@ namespace GreenshotJiraPlugin.Forms {
jiraListView.Columns.Add(translation); jiraListView.Columns.Add(translation);
} }
var imageList = new ImageList { var imageList = new ImageList {
ImageSize = CoreConfig.IconSize ImageSize = CoreConfig.ScaledIconSize
}; };
jiraListView.SmallImageList = imageList; jiraListView.SmallImageList = imageList;
jiraListView.LargeImageList = imageList; jiraListView.LargeImageList = imageList;

View file

@ -56,7 +56,7 @@ namespace GreenshotJiraPlugin {
{ {
if (args.PropertyName == nameof(CoreConfig.IconSize)) if (args.PropertyName == nameof(CoreConfig.IconSize))
{ {
JiraPlugin.Instance.JiraConnector._jiraClient?.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.IconSize.Width, Height = CoreConfig.IconSize.Height }); JiraPlugin.Instance.JiraConnector._jiraClient?.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.ScaledIconSize.Width, Height = CoreConfig.ScaledIconSize.Height });
} }
}; };
@ -99,7 +99,7 @@ namespace GreenshotJiraPlugin {
return false; return false;
} }
_jiraClient = JiraClient.Create(new Uri(JiraConfig.Url)); _jiraClient = JiraClient.Create(new Uri(JiraConfig.Url));
_jiraClient.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.IconSize.Width, Height = CoreConfig.IconSize.Height }); _jiraClient.Behaviour.SetConfig(new SvgConfiguration { Width = CoreConfig.ScaledIconSize.Width, Height = CoreConfig.ScaledIconSize.Height });
_jiraClient.SetBasicAuthentication(user, password); _jiraClient.SetBasicAuthentication(user, password);
_issueTypeBitmapCache = new IssueTypeBitmapCache(_jiraClient); _issueTypeBitmapCache = new IssueTypeBitmapCache(_jiraClient);

View file

@ -16,16 +16,34 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Interop.Microsoft.Office.Interop.OneNote" Version="1.1.0" /> <PackageReference Include="Interop.Microsoft.Office.Interop.OneNote" Version="1.1.0">
<PackageReference Include="Microsoft.Office.Interop.Excel" Version="15.0.4795.1000" /> <EmbedInteropTypes>true</EmbedInteropTypes>
<PackageReference Include="Microsoft.Office.Interop.Outlook" Version="15.0.4797.1003" /> <ExcludeAssets>runtime</ExcludeAssets>
<PackageReference Include="Microsoft.Office.Interop.PowerPoint" Version="15.0.4420.1017" /> </PackageReference>
<PackageReference Include="Microsoft.Office.Interop.Word" Version="15.0.4797.1003" /> <PackageReference Include="Microsoft.Office.Interop.Excel" Version="15.0.4795.1000">
<PackageReference Include="MicrosoftOfficeCore" Version="15.0.0" /> <EmbedInteropTypes>true</EmbedInteropTypes>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Office.Interop.Outlook" Version="15.0.4797.1003">
<EmbedInteropTypes>true</EmbedInteropTypes>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Office.Interop.PowerPoint" Version="15.0.4420.1017">
<EmbedInteropTypes>true</EmbedInteropTypes>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Office.Interop.Word" Version="15.0.4797.1003">
<EmbedInteropTypes>true</EmbedInteropTypes>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
<PackageReference Include="MicrosoftOfficeCore" Version="15.0.0">
<EmbedInteropTypes>True</EmbedInteropTypes>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference>
<PackageReference Include="Unofficial.Microsoft.mshtml" Version="7.0.3300"> <PackageReference Include="Unofficial.Microsoft.mshtml" Version="7.0.3300">
<EmbedInteropTypes>True</EmbedInteropTypes> <EmbedInteropTypes>True</EmbedInteropTypes>
<ExcludeAssets>runtime</ExcludeAssets>
</PackageReference> </PackageReference>
<PackageReference Include="log4net" Version="2.0.8" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View file

@ -150,7 +150,7 @@ namespace GreenshotPlugin.Core {
ExportInformation exportInformation = new ExportInformation(Designation, Language.GetString("settings_destination_picker")); ExportInformation exportInformation = new ExportInformation(Designation, Language.GetString("settings_destination_picker"));
var menu = new ContextMenuStrip var menu = new ContextMenuStrip
{ {
ImageScalingSize = CoreConfig.IconSize, ImageScalingSize = CoreConfig.ScaledIconSize,
Tag = null, Tag = null,
TopLevel = true TopLevel = true
}; };

View file

@ -267,7 +267,7 @@ namespace GreenshotPlugin.Core {
public Size Win10BorderCrop { get; set; } public Size Win10BorderCrop { get; set; }
private Size _iconSize; private Size _iconSize;
[IniProperty("IconSize", Description = "Defines the size of the icons (e.g. for the buttons in the editor), default value 16,16 anything bigger will cause scaling", DefaultValue = "16,16")] [IniProperty("BaseIconSize", Description = "Defines the base size of the icons (e.g. for the buttons in the editor), default value 16,16 and it's scaled to the current DPI", DefaultValue = "16,16")]
public Size IconSize { public Size IconSize {
get { get {
return _iconSize; return _iconSize;
@ -295,6 +295,8 @@ namespace GreenshotPlugin.Core {
} }
} }
public Size ScaledIconSize => DpiHelper.ScaleWithCurrentDpi(_iconSize);
[IniProperty("WebRequestTimeout", Description = "The connect timeout value for webrequets, these are seconds", DefaultValue = "100")] [IniProperty("WebRequestTimeout", Description = "The connect timeout value for webrequets, these are seconds", DefaultValue = "100")]
public int WebRequestTimeout { get; set; } public int WebRequestTimeout { get; set; }
[IniProperty("WebRequestReadWriteTimeout", Description = "The read/write timeout value for webrequets, these are seconds", DefaultValue = "100")] [IniProperty("WebRequestReadWriteTimeout", Description = "The read/write timeout value for webrequets, these are seconds", DefaultValue = "100")]

View file

@ -0,0 +1,711 @@
using GreenshotPlugin.Core.Enums;
using GreenshotPlugin.UnmanagedHelpers;
using log4net;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace GreenshotPlugin.Core
{
/// <summary>
/// This handles DPI changes see
/// <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn469266.aspx">Writing DPI-Aware Desktop and Win32 Applications</a>
/// </summary>
public static class DpiHelper
{
private static readonly ILog Log = LogManager.GetLogger(typeof(DpiHelper));
/// <summary>
/// This is the default DPI for the screen
/// </summary>
public const uint DefaultScreenDpi = 96;
/// <summary>
/// Retrieve the current DPI for the UI element which is related to this DpiHandler
/// </summary>
public static uint Dpi { get; private set; } = GetDpiForSystem();
/// <summary>
/// Calculate a DPI scale factor
/// </summary>
/// <param name="dpi">uint</param>
/// <returns>double</returns>
public static float DpiScaleFactor(uint dpi)
{
return (float)dpi / DefaultScreenDpi;
}
/// <summary>
/// Scale the supplied number according to the supplied dpi
/// </summary>
/// <param name="someNumber">double with e.g. the width 16 for 16x16 images</param>
/// <param name="dpi">current dpi, normal is 96.</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>double with the scaled number</returns>
public static float ScaleWithDpi(float someNumber, uint dpi, Func<float, float> scaleModifier = null)
{
var dpiScaleFactor = DpiScaleFactor(dpi);
if (scaleModifier != null)
{
dpiScaleFactor = scaleModifier(dpiScaleFactor);
}
return dpiScaleFactor * someNumber;
}
/// <summary>
/// Scale the supplied number according to the supplied dpi
/// </summary>
/// <param name="number">int with e.g. 16 for 16x16 images</param>
/// <param name="dpi">current dpi, normal is 96.</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>Scaled width</returns>
public static int ScaleWithDpi(int number, uint dpi, Func<float, float> scaleModifier = null)
{
var dpiScaleFactor = DpiScaleFactor(dpi);
if (scaleModifier != null)
{
dpiScaleFactor = scaleModifier(dpiScaleFactor);
}
return (int)(dpiScaleFactor * number);
}
/// <summary>
/// Scale the supplied Size according to the supplied dpi
/// </summary>
/// <param name="size">Size to resize</param>
/// <param name="dpi">current dpi, normal is 96.</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>NativeSize scaled</returns>
public static Size ScaleWithDpi(Size size, uint dpi, Func<float, float> scaleModifier = null)
{
var dpiScaleFactor = DpiScaleFactor(dpi);
if (scaleModifier != null)
{
dpiScaleFactor = scaleModifier(dpiScaleFactor);
}
return new Size((int)(dpiScaleFactor * size.Width), (int)(dpiScaleFactor * size.Height));
}
/// <summary>
/// Scale the supplied NativePoint according to the supplied dpi
/// </summary>
/// <param name="size">NativePoint to resize</param>
/// <param name="dpi">current dpi, normal is 96.</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>NativePoint scaled</returns>
public static Point ScaleWithDpi(Point size, uint dpi, Func<float, float> scaleModifier = null)
{
var dpiScaleFactor = DpiScaleFactor(dpi);
if (scaleModifier != null)
{
dpiScaleFactor = scaleModifier(dpiScaleFactor);
}
return new Point((int)(dpiScaleFactor * size.X), (int)(dpiScaleFactor * size.Y));
}
/// <summary>
/// Scale the supplied NativeSizeFloat according to the supplied dpi
/// </summary>
/// <param name="point">PointF</param>
/// <param name="dpi">current dpi, normal is 96.</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>PointF</returns>
public static PointF ScaleWithDpi(PointF point, uint dpi, Func<float, float> scaleModifier = null)
{
var dpiScaleFactor = DpiScaleFactor(dpi);
if (scaleModifier != null)
{
dpiScaleFactor = scaleModifier(dpiScaleFactor);
}
return new PointF(dpiScaleFactor * point.X, dpiScaleFactor * point.Y);
}
/// <summary>
/// Scale the supplied NativeSizeFloat according to the supplied dpi
/// </summary>
/// <param name="size">NativeSizeFloat to resize</param>
/// <param name="dpi">current dpi, normal is 96.</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>NativeSize scaled</returns>
public static SizeF ScaleWithDpi(SizeF size, uint dpi, Func<float, float> scaleModifier = null)
{
var dpiScaleFactor = DpiScaleFactor(dpi);
if (scaleModifier != null)
{
dpiScaleFactor = scaleModifier(dpiScaleFactor);
}
return new SizeF(dpiScaleFactor * size.Width, dpiScaleFactor * size.Height);
}
/// <summary>
/// Scale the supplied number to the current dpi
/// </summary>
/// <param name="someNumber">double with e.g. a width like 16 for 16x16 images</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>double with scaled number</returns>
public static float ScaleWithCurrentDpi(float someNumber, Func<float, float> scaleModifier = null)
{
return ScaleWithDpi(someNumber, Dpi, scaleModifier);
}
/// <summary>
/// Scale the supplied number to the current dpi
/// </summary>
/// <param name="someNumber">int with e.g. a width like 16 for 16x16 images</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>int with scaled number</returns>
public static int ScaleWithCurrentDpi(int someNumber, Func<float, float> scaleModifier = null)
{
return ScaleWithDpi(someNumber, Dpi, scaleModifier);
}
/// <summary>
/// Scale the supplied NativeSize to the current dpi
/// </summary>
/// <param name="size">NativeSize to scale</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>NativeSize scaled</returns>
public static Size ScaleWithCurrentDpi(Size size, Func<float, float> scaleModifier = null)
{
return ScaleWithDpi(size, Dpi, scaleModifier);
}
/// <summary>
/// Scale the supplied NativeSizeFloat to the current dpi
/// </summary>
/// <param name="size">NativeSizeFloat to scale</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>NativeSizeFloat scaled</returns>
public static SizeF ScaleWithCurrentDpi(SizeF size, Func<float, float> scaleModifier = null)
{
return ScaleWithDpi(size, Dpi, scaleModifier);
}
/// <summary>
/// Scale the supplied NativePoint to the current dpi
/// </summary>
/// <param name="point">NativePoint to scale</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>NativePoint scaled</returns>
public static Point ScaleWithCurrentDpi(Point point, Func<float, float> scaleModifier = null)
{
return ScaleWithDpi(point, Dpi, scaleModifier);
}
/// <summary>
/// Scale the supplied PointF to the current dpi
/// </summary>
/// <param name="point">PointF to scale</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>PointF scaled</returns>
public static PointF ScaleWithCurrentDpi(PointF point, Func<float, float> scaleModifier = null)
{
return ScaleWithDpi(point, Dpi, scaleModifier);
}
/// <summary>
/// Calculate a DPI unscale factor
/// </summary>
/// <param name="dpi">uint</param>
/// <returns>float</returns>
public static float DpiUnscaleFactor(uint dpi)
{
return (float)DefaultScreenDpi / dpi;
}
/// <summary>
/// Unscale the supplied number according to the supplied dpi
/// </summary>
/// <param name="someNumber">double with e.g. the scaled width</param>
/// <param name="dpi">current dpi, normal is 96.</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>double with the unscaled number</returns>
public static float UnscaleWithDpi(float someNumber, uint dpi, Func<float, float> scaleModifier = null)
{
var dpiUnscaleFactor = DpiUnscaleFactor(dpi);
if (scaleModifier != null)
{
dpiUnscaleFactor = scaleModifier(dpiUnscaleFactor);
}
return dpiUnscaleFactor * someNumber;
}
/// <summary>
/// Unscale the supplied number according to the supplied dpi
/// </summary>
/// <param name="number">int with a scaled width</param>
/// <param name="dpi">current dpi, normal is 96.</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>Unscaled width</returns>
public static int UnscaleWithDpi(int number, uint dpi, Func<float, float> scaleModifier = null)
{
var dpiUnscaleFactor = DpiUnscaleFactor(dpi);
if (scaleModifier != null)
{
dpiUnscaleFactor = scaleModifier(dpiUnscaleFactor);
}
return (int)(dpiUnscaleFactor * number);
}
/// <summary>
/// Unscale the supplied NativeSize according to the supplied dpi
/// </summary>
/// <param name="size">NativeSize to unscale</param>
/// <param name="dpi">current dpi, normal is 96.</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>Size unscaled</returns>
public static Size UnscaleWithDpi(Size size, uint dpi, Func<float, float> scaleModifier = null)
{
var dpiUnscaleFactor = DpiUnscaleFactor(dpi);
if (scaleModifier != null)
{
dpiUnscaleFactor = scaleModifier(dpiUnscaleFactor);
}
return new Size((int)(dpiUnscaleFactor * size.Width), (int)(dpiUnscaleFactor * size.Height));
}
/// <summary>
/// Unscale the supplied Point according to the supplied dpi
/// </summary>
/// <param name="size">Point to unscale</param>
/// <param name="dpi">current dpi, normal is 96.</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>Point unscaled</returns>
public static Point UnscaleWithDpi(Point point, uint dpi, Func<float, float> scaleModifier = null)
{
var dpiUnscaleFactor = DpiUnscaleFactor(dpi);
if (scaleModifier != null)
{
dpiUnscaleFactor = scaleModifier(dpiUnscaleFactor);
}
return new Point((int)(dpiUnscaleFactor * point.X), (int)(dpiUnscaleFactor * point.Y));
}
/// <summary>
/// unscale the supplied NativeSizeFloat according to the supplied dpi
/// </summary>
/// <param name="size">NativeSizeFloat to resize</param>
/// <param name="dpi">current dpi, normal is 96.</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>SizeF unscaled</returns>
public static SizeF UnscaleWithDpi(SizeF size, uint dpi, Func<float, float> scaleModifier = null)
{
float dpiUnscaleFactor = DpiUnscaleFactor(dpi);
if (scaleModifier != null)
{
dpiUnscaleFactor = scaleModifier(dpiUnscaleFactor);
}
return new SizeF(dpiUnscaleFactor * size.Width, dpiUnscaleFactor * size.Height);
}
/// <summary>
/// Unscale the supplied number to the current dpi
/// </summary>
/// <param name="someNumber">double with e.g. a width like 16 for 16x16 images</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>double with unscaled number</returns>
public static float UnscaleWithCurrentDpi(float someNumber, Func<float, float> scaleModifier = null)
{
return UnscaleWithDpi(someNumber, Dpi, scaleModifier);
}
/// <summary>
/// Unscale the supplied number to the current dpi
/// </summary>
/// <param name="someNumber">int with e.g. a width like 16 for 16x16 images</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>int with unscaled number</returns>
public static int UnscaleWithCurrentDpi(int someNumber, Func<float, float> scaleModifier = null)
{
return UnscaleWithDpi(someNumber, Dpi, scaleModifier);
}
/// <summary>
/// Unscale the supplied NativeSize to the current dpi
/// </summary>
/// <param name="size">Size to unscale</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>Size unscaled</returns>
public static Size UnscaleWithCurrentDpi(Size size, Func<float, float> scaleModifier = null)
{
return UnscaleWithDpi(size, Dpi, scaleModifier);
}
/// <summary>
/// Unscale the supplied NativeSizeFloat to the current dpi
/// </summary>
/// <param name="size">NativeSizeFloat to unscale</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>NativeSizeFloat unscaled</returns>
public static SizeF UnscaleWithCurrentDpi(SizeF size, Func<float, float> scaleModifier = null)
{
return UnscaleWithDpi(size, Dpi, scaleModifier);
}
/// <summary>
/// Unscale the supplied NativePoint to the current dpi
/// </summary>
/// <param name="point">NativePoint to unscale</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>NativePoint unscaled</returns>
public static Point UnscaleWithCurrentDpi(Point point, Func<float, float> scaleModifier = null)
{
return UnscaleWithDpi(point, Dpi, scaleModifier);
}
/// <summary>
/// Unscale the supplied NativePointFloat to the current dpi
/// </summary>
/// <param name="point">NativePointFloat to unscale</param>
/// <param name="scaleModifier">A function which can modify the scale factor</param>
/// <returns>NativePointFloat unscaled</returns>
public static PointF UnscaleWithCurrentDpi(PointF point, Func<float, float> scaleModifier = null)
{
return ScaleWithDpi(point, Dpi, scaleModifier);
}
/// <summary>
/// public wrapper for EnableNonClientDpiScaling, this also checks if the function is available.
/// </summary>
/// <param name="hWnd">IntPtr</param>
/// <returns>true if it worked</returns>
public static bool TryEnableNonClientDpiScaling(IntPtr hWnd)
{
// EnableNonClientDpiScaling is only available on Windows 10 and later
if (!WindowsVersion.IsWindows10OrLater)
{
return false;
}
var result = EnableNonClientDpiScaling(hWnd);
if (result.Succeeded())
{
return true;
}
var error = Win32.GetLastErrorCode();
if (Log.IsDebugEnabled)
{
Log.DebugFormat("Error enabling non client dpi scaling : {0}", Win32.GetMessage(error));
}
return false;
}
/// <summary>
/// Make the current process DPI Aware, this should be done via the manifest but sometimes this is not possible.
/// </summary>
/// <returns>bool true if it was possible to change the DPI awareness</returns>
public static bool EnableDpiAware()
{
// We can only test this for Windows 8.1 or later
if (!WindowsVersion.IsWindows81OrLater)
{
Log.Debug("An application can only be DPI aware starting with Window 8.1 and later.");
return false;
}
if (WindowsVersion.IsWindows10BuildOrLater(15063))
{
if (IsValidDpiAwarenessContext(DpiAwarenessContext.PerMonitorAwareV2))
{
SetProcessDpiAwarenessContext(DpiAwarenessContext.PerMonitorAwareV2);
}
else
{
SetProcessDpiAwarenessContext(DpiAwarenessContext.PerMonitorAwareV2);
}
return true;
}
return SetProcessDpiAwareness(DpiAwareness.PerMonitorAware).Succeeded();
}
/// <summary>
/// Check if the process is DPI Aware, an DpiHandler doesn't make sense if not.
/// </summary>
public static bool IsDpiAware
{
get
{
// We can only test this for Windows 8.1 or later
if (!WindowsVersion.IsWindows81OrLater)
{
Log.Debug("An application can only be DPI aware starting with Window 8.1 and later.");
return false;
}
using var process = Process.GetCurrentProcess();
GetProcessDpiAwareness(process.Handle, out var dpiAwareness);
if (Log.IsDebugEnabled)
{
Log.DebugFormat("Process {0} has a Dpi awareness {1}", process.ProcessName, dpiAwareness);
}
return dpiAwareness != DpiAwareness.Unaware && dpiAwareness != DpiAwareness.Invalid;
}
}
/// <summary>
/// Retrieve the DPI value for the supplied window handle
/// </summary>
/// <param name="hWnd">IntPtr</param>
/// <returns>dpi value</returns>
public static uint GetDpi(IntPtr hWnd)
{
if (!User32.IsWindow(hWnd))
{
return DefaultScreenDpi;
}
// Use the easiest method, but this only works for Windows 10
if (WindowsVersion.IsWindows10OrLater)
{
return GetDpiForWindow(hWnd);
}
// Use the second easiest method, but this only works for Windows 8.1 or later
if (WindowsVersion.IsWindows81OrLater)
{
var hMonitor = User32.MonitorFromWindow(hWnd, MonitorFrom.DefaultToNearest);
// ReSharper disable once UnusedVariable
if (GetDpiForMonitor(hMonitor, MonitorDpiType.EffectiveDpi, out var dpiX, out var dpiY))
{
return dpiX;
}
}
// Fallback to the global DPI settings
using var hdc = SafeWindowDcHandle.FromWindow(hWnd);
if (hdc == null)
{
return DefaultScreenDpi;
}
return (uint)GDI32.GetDeviceCaps(hdc, DeviceCaps.LOGPIXELSX);
}
/// <summary>
/// See details <a hef="https://msdn.microsoft.com/en-us/library/windows/desktop/dn302113(v=vs.85).aspx">GetProcessDpiAwareness function</a>
/// Retrieves the dots per inch (dpi) awareness of the specified process.
/// </summary>
/// <param name="processHandle">IntPtr with handle of the process that is being queried. If this parameter is NULL, the current process is queried.</param>
/// <param name="value">out DpiAwareness - The DPI awareness of the specified process. Possible values are from the PROCESS_DPI_AWARENESS enumeration.</param>
/// <returns>HResult</returns>
[DllImport("shcore")]
private static extern HResult GetProcessDpiAwareness(IntPtr processHandle, out DpiAwareness value);
/// <summary>
/// Sets the current process to a specified dots per inch (dpi) awareness level. The DPI awareness levels are from the PROCESS_DPI_AWARENESS enumeration.
/// See <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn302122(v=vs.85).aspx">SetProcessDpiAwareness function</a>
/// </summary>
/// <param name="dpiAwareness">DpiAwareness</param>
/// <returns>HResult</returns>
[DllImport("shcore")]
private static extern HResult SetProcessDpiAwareness(DpiAwareness dpiAwareness);
/// <summary>
/// It is recommended that you set the process-default DPI awareness via application manifest. See Setting the default DPI awareness for a process for more information. Setting the process-default DPI awareness via API call can lead to unexpected application behavior.
///
/// Sets the current process to a specified dots per inch (dpi) awareness context. The DPI awareness contexts are from the DPI_AWARENESS_CONTEXT value.
/// Remarks:
/// This API is a more advanced version of the previously existing SetProcessDpiAwareness API, allowing for the process default to be set to the finer-grained DPI_AWARENESS_CONTEXT values. Most importantly, this allows you to programmatically set Per Monitor v2 as the process default value, which is not possible with the previous API.
///
/// This method sets the default DPI_AWARENESS_CONTEXT for all threads within an application. Individual threads can have their DPI awareness changed from the default with the SetThreadDpiAwarenessContext method.
/// See <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/mt807676(v=vs.85).aspx">SetProcessDpiAwarenessContext function</a>
/// </summary>
/// <param name="dpiAwarenessContext">DpiAwarenessContext</param>
/// <returns>bool</returns>
[DllImport("User32.dll", SetLastError = true)]
private static extern bool SetProcessDpiAwarenessContext(DpiAwarenessContext dpiAwarenessContext);
/// <summary>
/// See more at <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/mt748624(v=vs.85).aspx">GetDpiForWindow function</a>
/// Returns the dots per inch (dpi) value for the associated window.
/// </summary>
/// <param name="hWnd">IntPtr</param>
/// <returns>uint with dpi</returns>
[DllImport("User32.dll")]
private static extern uint GetDpiForWindow(IntPtr hWnd);
/// <summary>
/// See
/// <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn280510(v=vs.85).aspx">GetDpiForMonitor function</a>
/// Queries the dots per inch (dpi) of a display.
/// </summary>
/// <param name="hMonitor">IntPtr</param>
/// <param name="dpiType">MonitorDpiType</param>
/// <param name="dpiX">out int for the horizontal dpi</param>
/// <param name="dpiY">out int for the vertical dpi</param>
/// <returns>true if all okay</returns>
[DllImport("shcore")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GetDpiForMonitor(IntPtr hMonitor, MonitorDpiType dpiType, out uint dpiX, out uint dpiY);
/// <summary>
/// See <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/mt748621(v=vs.85).aspx">EnableNonClientDpiScaling function</a>
/// </summary>
/// <param name="hWnd">IntPtr</param>
/// <returns>bool</returns>
[DllImport("User32.dll", SetLastError = true)]
private static extern HResult EnableNonClientDpiScaling(IntPtr hWnd);
/// <summary>
/// See <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/mt748623(v=vs.85).aspx">GetDpiForSystem function</a>
/// Returns the system DPI.
/// </summary>
/// <returns>uint with the system DPI</returns>
[DllImport("User32.dll")]
private static extern uint GetDpiForSystem();
/// <summary>
/// Converts a point in a window from logical coordinates into physical coordinates, regardless of the dots per inch (dpi) awareness of the caller. For more information about DPI awareness levels, see PROCESS_DPI_AWARENESS.
/// See more at <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn384110(v=vs.85).aspx">LogicalToPhysicalPointForPerMonitorDPI function</a>
/// </summary>
/// <param name="hWnd">IntPtr A handle to the window whose transform is used for the conversion.</param>
/// <param name="point">A pointer to a POINT structure that specifies the logical coordinates to be converted. The new physical coordinates are copied into this structure if the function succeeds.</param>
/// <returns>bool</returns>
[DllImport("User32.dll")]
private static extern bool LogicalToPhysicalPointForPerMonitorDPI(IntPtr hWnd, ref POINT point);
/// <summary>
/// Converts a point in a window from logical coordinates into physical coordinates, regardless of the dots per inch (dpi) awareness of the caller. For more information about DPI awareness levels, see PROCESS_DPI_AWARENESS.
/// See more at <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn384112(v=vs.85).aspx">PhysicalToLogicalPointForPerMonitorDPI function</a>
/// </summary>
/// <param name="hWnd">IntPtr A handle to the window whose transform is used for the conversion.</param>
/// <param name="point">NativePoint A pointer to a POINT structure that specifies the physical/screen coordinates to be converted. The new logical coordinates are copied into this structure if the function succeeds.</param>
/// <returns>bool</returns>
[DllImport("User32.dll")]
private static extern bool PhysicalToLogicalPointForPerMonitorDPI(IntPtr hWnd, ref POINT point);
/// <summary>
/// See <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/ms724947(v=vs.85).aspx">SystemParametersInfo function</a>
/// Retrieves the value of one of the system-wide parameters, taking into account the provided DPI value.
/// </summary>
/// <param name="uiAction">
/// SystemParametersInfoActions The system-wide parameter to be retrieved.
/// This function is only intended for use with SPI_GETICONTITLELOGFONT, SPI_GETICONMETRICS, or SPI_GETNONCLIENTMETRICS. See SystemParametersInfo for more information on these values.
/// </param>
/// <param name="uiParam">
/// A parameter whose usage and format depends on the system parameter being queried or set. For more
/// information about system-wide parameters, see the uiAction parameter. If not otherwise indicated, you must specify
/// zero for this parameter.
/// </param>
/// <param name="pvParam">IntPtr</param>
/// <param name="fWinIni">SystemParametersInfoBehaviors</param>
/// <param name="dpi">uint with dpi value</param>
/// <returns>bool</returns>
[DllImport("User32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool SystemParametersInfoForDpi(SystemParametersInfoActions uiAction, uint uiParam, IntPtr pvParam, SystemParametersInfoBehaviors fWinIni, uint dpi);
/// <summary>
/// See <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/mt748626(v=vs.85).aspx">GetThreadDpiAwarenessContext function</a>
/// Gets the DPI_AWARENESS_CONTEXT for the current thread.
///
/// This method will return the latest DPI_AWARENESS_CONTEXT sent to SetThreadDpiAwarenessContext. If SetThreadDpiAwarenessContext was never called for this thread, then the return value will equal the default DPI_AWARENESS_CONTEXT for the process.
/// </summary>
/// <returns>DpiAwarenessContext</returns>
[DllImport("User32.dll")]
private static extern DpiAwarenessContext GetThreadDpiAwarenessContext();
/// <summary>
/// Set the DPI awareness for the current thread to the provided value.
/// </summary>
/// <param name="dpiAwarenessContext">DpiAwarenessContext the new value for the current thread</param>
/// <returns>DpiAwarenessContext previous value</returns>
[DllImport("User32.dll")]
private static extern DpiAwarenessContext SetThreadDpiAwarenessContext(DpiAwarenessContext dpiAwarenessContext);
/// <summary>
/// Retrieves the DpiAwareness value from a DpiAwarenessContext.
/// </summary>
/// <param name="dpiAwarenessContext">DpiAwarenessContext</param>
/// <returns>DpiAwareness</returns>
[DllImport("User32.dll")]
private static extern DpiAwareness GetAwarenessFromDpiAwarenessContext(DpiAwarenessContext dpiAwarenessContext);
/// <summary>
/// Retrieves the DPI from a given DPI_AWARENESS_CONTEXT handle. This enables you to determine the DPI of a thread without needed to examine a window created within that thread.
/// </summary>
/// <param name="dpiAwarenessContext">DpiAwarenessContext</param>
/// <returns>uint with dpi value</returns>
[DllImport("User32.dll")]
private static extern uint GetDpiFromDpiAwarenessContext(DpiAwarenessContext dpiAwarenessContext);
/// <summary>
/// Determines if a specified DPI_AWARENESS_CONTEXT is valid and supported by the current system.
/// </summary>
/// <param name="dpiAwarenessContext">DpiAwarenessContext The context that you want to determine if it is supported.</param>
/// <returns>bool true if supported otherwise false</returns>
[DllImport("User32.dll")]
private static extern bool IsValidDpiAwarenessContext(DpiAwarenessContext dpiAwarenessContext);
/// <summary>
/// Returns the DPI_HOSTING_BEHAVIOR of the specified window.
///
/// This API allows you to examine the hosting behavior of a window after it has been created. A window's hosting behavior is the hosting behavior of the thread in which the window was created, as set by a call to SetThreadDpiHostingBehavior. This is a permanent value and cannot be changed after the window is created, even if the thread's hosting behavior is changed.
/// </summary>
/// <returns>DpiHostingBehavior</returns>
[DllImport("User32.dll")]
private static extern DpiHostingBehavior GetWindowDpiHostingBehavior();
/// <summary>
/// See more at <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/mt845775.aspx">SetThreadDpiHostingBehavior function</a>
/// Sets the thread's DPI_HOSTING_BEHAVIOR. This behavior allows windows created in the thread to host child windows with a different DPI_AWARENESS_CONTEXT.
///
/// DPI_HOSTING_BEHAVIOR enables a mixed content hosting behavior, which allows parent windows created in the thread to host child windows with a different DPI_AWARENESS_CONTEXT value. This property only effects new windows created within this thread while the mixed hosting behavior is active. A parent window with this hosting behavior is able to host child windows with different DPI_AWARENESS_CONTEXT values, regardless of whether the child windows have mixed hosting behavior enabled.
///
/// This hosting behavior does not allow for windows with per-monitor DPI_AWARENESS_CONTEXT values to be hosted until windows with DPI_AWARENESS_CONTEXT values of system or unaware.
///
/// To avoid unexpected outcomes, a thread's DPI_HOSTING_BEHAVIOR should be changed to support mixed hosting behaviors only when creating a new window which needs to support those behaviors. Once that window is created, the hosting behavior should be switched back to its default value.
///
/// This API is used to change the thread's DPI_HOSTING_BEHAVIOR from its default value. This is only necessary if your app needs to host child windows from plugins and third-party components that do not support per-monitor-aware context. This is most likely to occur if you are updating complex applications to support per-monitor DPI_AWARENESS_CONTEXT behaviors.
///
/// Enabling mixed hosting behavior will not automatically adjust the thread's DPI_AWARENESS_CONTEXT to be compatible with legacy content. The thread's awareness context must still be manually changed before new windows are created to host such content.
/// </summary>
/// <param name="dpiHostingBehavior">DpiHostingBehavior</param>
/// <returns>previous DpiHostingBehavior</returns>
[DllImport("User32.dll")]
private static extern DpiHostingBehavior SetThreadDpiHostingBehavior(DpiHostingBehavior dpiHostingBehavior);
/// <summary>
///Retrieves the DPI_HOSTING_BEHAVIOR from the current thread.
/// </summary>
/// <returns>DpiHostingBehavior</returns>
[DllImport("User32.dll")]
private static extern DpiHostingBehavior GetThreadDpiHostingBehavior();
/// <summary>
/// Overrides the default per-monitor DPI scaling behavior of a child window in a dialog.
/// This function returns TRUE if the operation was successful, and FALSE otherwise. To get extended error information, call GetLastError.
///
/// Possible errors are ERROR_INVALID_HANDLE if passed an invalid HWND, and ERROR_ACCESS_DENIED if the windows belongs to another process.
///
/// The behaviors are specified as values from the DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS enum. This function follows the typical two-parameter approach to setting flags, where a mask specifies the subset of the flags to be changed.
///
/// It is valid to set these behaviors on any window. It does not matter if the window is currently a child of a dialog at the point in time that SetDialogControlDpiChangeBehavior is called. The behaviors are retained and will take effect only when the window is an immediate child of a dialog that has per-monitor DPI scaling enabled.
///
/// This API influences individual controls within dialogs. The dialog-wide per-monitor DPI scaling behavior is controlled by SetDialogDpiChangeBehavior.
/// </summary>
/// <param name="hWnd">IntPtr A handle for the window whose behavior will be modified.</param>
/// <param name="mask">DialogScalingBehaviors A mask specifying the subset of flags to be changed.</param>
/// <param name="values">DialogScalingBehaviors The desired value to be set for the specified subset of flags.</param>
/// <returns>bool</returns>
[DllImport("User32.dll")]
private static extern bool SetDialogControlDpiChangeBehavior(IntPtr hWnd, DialogScalingBehaviors mask, DialogScalingBehaviors values);
/// <summary>
/// Retrieves and per-monitor DPI scaling behavior overrides of a child window in a dialog.
/// The flags set on the given window. If passed an invalid handle, this function will return zero, and set its last error to ERROR_INVALID_HANDLE.
/// </summary>
/// <param name="hWnd">IntPtr A handle for the window whose behavior will be modified.</param>
/// <returns>DialogScalingBehaviors</returns>
[DllImport("User32.dll")]
private static extern DialogScalingBehaviors GetDialogControlDpiChangeBehavior(IntPtr hWnd);
}
}

View file

@ -0,0 +1,34 @@
// Copyright (c) Dapplo and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
namespace GreenshotPlugin.Core.Enums
{
/// <summary>
/// In Per Monitor v2 contexts, dialogs will automatically respond to DPI changes by resizing themselves and re-computing the positions of their child windows (here referred to as re-layouting). This enum works in conjunction with SetDialogDpiChangeBehavior in order to override the default DPI scaling behavior for dialogs.
/// This does not affect DPI scaling behavior for the child windows of dialogs(beyond re-layouting), which is controlled by DIALOG_CONTROL_DPI_CHANGE_BEHAVIORS.
/// </summary>
[Flags]
public enum DialogDpiChangeBehaviors
{
/// <summary>
/// The default behavior of the dialog manager. In response to a DPI change, the dialog manager will re-layout each control, update the font on each control, resize the dialog, and update the dialog's own font.
/// </summary>
Default = 0,
/// <summary>
/// Prevents the dialog manager from responding to WM_GETDPISCALEDSIZE and WM_DPICHANGED, disabling all default DPI scaling behavior.
/// </summary>
DisableAll = 1,
/// <summary>
/// Prevents the dialog manager from resizing the dialog in response to a DPI change.
/// </summary>
DisableResize = 2,
/// <summary>
/// Prevents the dialog manager from re-layouting all of the dialogue's immediate children HWNDs in response to a DPI change.
/// </summary>
DisableControlRelayout = 3
}
}

View file

@ -0,0 +1,32 @@
// Copyright (c) Dapplo and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
namespace GreenshotPlugin.Core.Enums
{
/// <summary>
/// Describes per-monitor DPI scaling behavior overrides for child windows within dialogs. The values in this enumeration are bitfields and can be combined.
///
/// This enum is used with SetDialogControlDpiChangeBehavior in order to override the default per-monitor DPI scaling behavior for a child window within a dialog.
///
/// These settings only apply to individual controls within dialogs. The dialog-wide per-monitor DPI scaling behavior of a dialog is controlled by DIALOG_DPI_CHANGE_BEHAVIORS.
/// </summary>
[Flags]
public enum DialogScalingBehaviors
{
/// <summary>
/// The default behavior of the dialog manager. The dialog managed will update the font, size, and position of the child window on DPI changes.
/// </summary>
Default = 0,
/// <summary>
/// Prevents the dialog manager from sending an updated font to the child window via WM_SETFONT in response to a DPI change.
/// </summary>
DisableFontUpdate = 1,
/// <summary>
/// Prevents the dialog manager from resizing and repositioning the child window in response to a DPI change.
/// </summary>
DisableRelayout = 2
}
}

View file

@ -0,0 +1,40 @@
// Copyright (c) Dapplo and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace GreenshotPlugin.Core.Enums
{
/// <summary>
/// Identifies the dots per inch (dpi) setting for a thread, process, or window.
/// Can be used everywhere ProcessDpiAwareness is passed.
/// </summary>
public enum DpiAwareness
{
/// <summary>
/// Invalid DPI awareness. This is an invalid DPI awareness value.
/// </summary>
Invalid = -1,
/// <summary>
/// DPI unaware.
/// This process does not scale for DPI changes and is always assumed to have a scale factor of 100% (96 DPI).
/// It will be automatically scaled by the system on any other DPI setting.
/// </summary>
Unaware = 0,
/// <summary>
/// System DPI aware.
/// This process does not scale for DPI changes.
/// It will query for the DPI once and use that value for the lifetime of the process.
/// If the DPI changes, the process will not adjust to the new DPI value.
/// It will be automatically scaled up or down by the system when the DPI changes from the system value.
/// </summary>
SystemAware = 1,
/// <summary>
/// Per monitor DPI aware.
/// This process checks for the DPI when it is created and adjusts the scale factor whenever the DPI changes.
/// These processes are not automatically scaled by the system.
/// </summary>
PerMonitorAware = 2
}
}

View file

@ -0,0 +1,46 @@
// Copyright (c) Dapplo and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace GreenshotPlugin.Core.Enums
{
/// <summary>
/// </summary>
public enum DpiAwarenessContext
{
/// <summary>
/// DPI unaware.
/// This window does not scale for DPI changes and is always assumed to have a scale factor of 100% (96 DPI).
/// It will be automatically scaled by the system on any other DPI setting.
/// </summary>
Unaware = -1,
/// <summary>
/// System DPI aware.
/// This window does not scale for DPI changes.
/// It will query for the DPI once and use that value for the lifetime of the process.
/// If the DPI changes, the process will not adjust to the new DPI value.
/// It will be automatically scaled up or down by the system when the DPI changes from the system value.
/// </summary>
SystemAware = -2,
/// <summary>
/// Per monitor DPI aware.
/// This window checks for the DPI when it is created and adjusts the scale factor whenever the DPI changes.
/// These processes are not automatically scaled by the system.
/// </summary>
PerMonitorAware = -3,
/// <summary>
/// Also known as Per Monitor v2. An advancement over the original per-monitor DPI awareness mode, which enables applications to access new DPI-related scaling behaviors on a per top-level window basis.
/// Per Monitor v2 was made available in the Creators Update of Windows 10, and is not available on earlier versions of the operating system.
/// The additional behaviors introduced are as follows:
/// * Child window DPI change notifications - In Per Monitor v2 contexts, the entire window tree is notified of any DPI changes that occur.
/// * Scaling of non-client area - All windows will automatically have their non-client area drawn in a DPI sensitive fashion. Calls to EnableNonClientDpiScaling are unnecessary.
/// * Scaling of Win32 menus - All NTUSER menus created in Per Monitor v2 contexts will be scaling in a per-monitor fashion.
/// * Dialog Scaling - Win32 dialogs created in Per Monitor v2 contexts will automatically respond to DPI changes.
/// * Improved scaling of comctl32 controls - Various comctl32 controls have improved DPI scaling behavior in Per Monitor v2 contexts.
/// * Improved theming behavior - UxTheme handles opened in the context of a Per Monitor v2 window will operate in terms of the DPI associated with that window.
/// </summary>
PerMonitorAwareV2 = -4
}
}

View file

@ -0,0 +1,28 @@
// Copyright (c) Dapplo and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace GreenshotPlugin.Core.Enums
{
/// <summary>
/// Identifies the DPI hosting behavior for a window.
/// This behavior allows windows created in the thread to host child windows with a different DPI_AWARENESS_CONTEXT
/// </summary>
public enum DpiHostingBehavior
{
/// <summary>
/// Invalid DPI hosting behavior. This usually occurs if the previous SetThreadDpiHostingBehavior call used an invalid parameter.
/// </summary>
Invalid = -1,
/// <summary>
/// Default DPI hosting behavior. The associated window behaves as normal, and cannot create or re-parent child windows with a different DPI_AWARENESS_CONTEXT.
/// </summary>
Default = 0,
/// <summary>
/// Mixed DPI hosting behavior. This enables the creation and re-parenting of child windows with different DPI_AWARENESS_CONTEXT. These child windows will be independently scaled by the OS.
/// </summary>
Mixed = 1
}
}

View file

@ -1,11 +1,25 @@
using System; // Greenshot - a free and open source screenshot tool
using System.Collections.Generic; // Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
using System.Diagnostics.CodeAnalysis; //
using System.Linq; // For more information see: http://getgreenshot.org/
using System.Text; // The Greenshot project is hosted on GitHub https://github.com/greenshot/greenshot
using System.Threading.Tasks; //
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 1 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
namespace GreenshotWin10Plugin.Native using System.Diagnostics.CodeAnalysis;
namespace GreenshotPlugin.Core.Enums
{ {
/// <summary> /// <summary>
/// The HRESULT represents Windows error codes /// The HRESULT represents Windows error codes

View file

@ -0,0 +1,40 @@
// Copyright (c) Dapplo and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
namespace GreenshotPlugin.Core.Enums
{
/// <summary>
/// See
/// <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dn280511(v=vs.85).aspx">
/// MONITOR_DPI_TYPE
/// enumeration
/// </a>
/// </summary>
[Flags]
public enum MonitorDpiType
{
/// <summary>
/// The effective DPI.
/// This value should be used when determining the correct scale factor for scaling UI elements.
/// This incorporates the scale factor set by the user for this specific display.
/// </summary>
EffectiveDpi = 0,
/// <summary>
/// The angular DPI.
/// This DPI ensures rendering at a compliant angular resolution on the screen.
/// This does not include the scale factor set by the user for this specific display
/// </summary>
AngularDpi = 1,
/// <summary>
/// The raw DPI.
/// This value is the linear DPI of the screen as measured on the screen itself.
/// Use this value when you want to read the pixel density and not the recommended scaling setting.
/// This does not include the scale factor set by the user for this specific display and is not guaranteed to be a
/// supported DPI value.
/// </summary>
RawDpi = 2
}
}

View file

@ -0,0 +1,30 @@
// Copyright (c) Dapplo and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
namespace GreenshotPlugin.Core.Enums
{
/// <summary>
/// Flags for the MonitorFromRect / MonitorFromWindow "flags" field
/// see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd145063(v=vs.85).aspx">MonitorFromRect function</a>
/// or see <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/dd145064(v=vs.85).aspx">MonitorFromWindow function</a>
/// </summary>
[Flags]
public enum MonitorFrom : uint
{
/// <summary>
/// Returns a handle to the display monitor that is nearest to the rectangle.
/// </summary>
DefaultToNearest = 0,
/// <summary>
/// Returns NULL. (why??)
/// </summary>
DefaultToNull = 1,
/// <summary>
/// Returns a handle to the primary display monitor.
/// </summary>
DefaultToPrimary = 2
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,28 @@
// Copyright (c) Dapplo and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
namespace GreenshotPlugin.Core.Enums
{
/// <summary>
/// If a system parameter is being set, specifies whether the user profile is to be updated, and if so, whether the
/// WM_SETTINGCHANGE message is to be broadcast to all top-level windows to notify them of the change.
/// This parameter can be zero if you do not want to update the user profile or broadcast the WM_SETTINGCHANGE message,
/// or it can be one or more of the following values.
/// </summary>
public enum SystemParametersInfoBehaviors : uint
{
/// <summary>
/// Do nothing
/// </summary>
None = 0x00,
/// <summary>Writes the new system-wide parameter setting to the user profile.</summary>
UpdateIniFile = 0x01,
/// <summary>Broadcasts the WM_SETTINGCHANGE message after updating the user profile.</summary>
SendChange = 0x02,
/// <summary>Same as SPIF_SENDCHANGE.</summary>
SendWinIniChange = SendChange
}
}

View file

@ -17,10 +17,11 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
using GreenshotPlugin.Core.Enums;
using System.Diagnostics.Contracts; using System.Diagnostics.Contracts;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
namespace GreenshotWin10Plugin.Native namespace GreenshotPlugin.Core
{ {
/// <summary> /// <summary>
/// Extensions to handle the HResult /// Extensions to handle the HResult
@ -55,7 +56,7 @@ namespace GreenshotWin10Plugin.Native
/// <param name="hResult">HResult</param> /// <param name="hResult">HResult</param>
public static void ThrowOnFailure(this HResult hResult) public static void ThrowOnFailure(this HResult hResult)
{ {
if (Failed(hResult)) if (hResult.Failed())
{ {
throw Marshal.GetExceptionForHR((int)hResult); throw Marshal.GetExceptionForHR((int)hResult);
} }

View file

@ -1,102 +0,0 @@
/*
* Greenshot - a free and open source screenshot tool
* Copyright (C) 2007-2020 Thomas Braun, Jens Klingen, Robin Krom
*
* For more information see: http://getgreenshot.org/
* The Greenshot project is hosted on GitHub: https://github.com/greenshot
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 1 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
using System;
namespace GreenshotPlugin.Core
{
/// <summary>
/// Extensions to help with querying the Operating System
/// </summary>
public static class OperatingSystemExtensions
{
/// <summary>
/// Test if the current OS is Windows 10
/// </summary>
/// <param name="operatingSystem">OperatingSystem from Environment.OSVersion</param>
/// <returns>true if we are running on Windows 10</returns>
public static bool IsWindows10(this OperatingSystem operatingSystem)
{
return operatingSystem.Version.Major == 10;
}
/// <summary>
/// Test if the current OS is Windows 10 or later
/// </summary>
/// <param name="operatingSystem">OperatingSystem from Environment.OSVersion</param>
/// <returns>true if we are running on Windows 10 or later</returns>
public static bool IsWindows10OrLater(this OperatingSystem operatingSystem)
{
return operatingSystem.Version.Major >= 10;
}
/// <summary>
/// Test if the current OS is Windows 8(.1)
/// </summary>
/// <param name="operatingSystem">OperatingSystem from Environment.OSVersion</param>
/// <returns>true if we are running on Windows 8(.1)</returns>
public static bool IsWindows8(this OperatingSystem operatingSystem)
{
return operatingSystem.Version.Major == 6 && operatingSystem.Version.Minor >= 2;
}
/// <summary>
/// Test if the current OS is Windows 8 or later
/// </summary>
/// <param name="operatingSystem">OperatingSystem from Environment.OSVersion</param>
/// <returns>true if we are running on Windows 8 or later</returns>
public static bool IsWindows8OrLater(this OperatingSystem operatingSystem)
{
return (operatingSystem.Version.Major == 6 && operatingSystem.Version.Minor >= 2) || operatingSystem.Version.Major > 6;
}
/// <summary>
/// Test if the current OS is Windows 7 or later
/// </summary>
/// <param name="operatingSystem">OperatingSystem from Environment.OSVersion</param>
/// <returns>true if we are running on Windows 7 or later</returns>
public static bool IsWindows7OrLater(this OperatingSystem operatingSystem)
{
return (operatingSystem.Version.Major == 6 && operatingSystem.Version.Minor >= 1) || operatingSystem.Version.Major > 6;
}
/// <summary>
/// Test if the current OS is Windows Vista or later
/// </summary>
/// <param name="operatingSystem">OperatingSystem from Environment.OSVersion</param>
/// <returns>true if we are running on Windows Vista or later</returns>
public static bool IsWindowsVistaOrLater(this OperatingSystem operatingSystem)
{
return operatingSystem.Version.Major >= 6;
}
/// <summary>
/// Test if the current OS is Windows XP or later
/// </summary>
/// <param name="operatingSystem">OperatingSystem from Environment.OSVersion</param>
/// <returns>true if we are running on Windows XP or later</returns>
public static bool IsWindowsXpOrLater(this OperatingSystem operatingSystem)
{
// Windows 2000 is Major 5 minor 0
return Environment.OSVersion.Version.Major > 5 || (Environment.OSVersion.Version.Major == 5 && Environment.OSVersion.Version.Minor >= 1);
}
}
}

View file

@ -757,7 +757,7 @@ namespace GreenshotPlugin.Core {
_lastWindowRectangleRetrieveTime = now; _lastWindowRectangleRetrieveTime = now;
} }
} }
if (gotFrameBounds && Environment.OSVersion.IsWindows10() && !Maximised) if (gotFrameBounds && WindowsVersion.IsWindows10OrLater && !Maximised)
{ {
// Somehow DWM doesn't calculate it corectly, there is a 1 pixel border around the capture // Somehow DWM doesn't calculate it corectly, there is a 1 pixel border around the capture
// Remove this border, currently it's fixed but TODO: Make it depend on the OS? // Remove this border, currently it's fixed but TODO: Make it depend on the OS?
@ -969,7 +969,7 @@ namespace GreenshotPlugin.Core {
} else { } else {
doesCaptureFit = true; doesCaptureFit = true;
} }
} else if (!Environment.OSVersion.IsWindows8OrLater()) { } else if (!WindowsVersion.IsWindows8OrLater) {
//GetClientRect(out windowRectangle); //GetClientRect(out windowRectangle);
GetBorderSize(out borderSize); GetBorderSize(out borderSize);
formLocation = new Point(windowRectangle.X - borderSize.Width, windowRectangle.Y - borderSize.Height); formLocation = new Point(windowRectangle.X - borderSize.Width, windowRectangle.Y - borderSize.Height);
@ -986,7 +986,7 @@ namespace GreenshotPlugin.Core {
captureRectangle.Inflate(borderSize.Width, borderSize.Height); captureRectangle.Inflate(borderSize.Width, borderSize.Height);
} else { } else {
// TODO: Also 8.x? // TODO: Also 8.x?
if (Environment.OSVersion.IsWindows10()) if (WindowsVersion.IsWindows10OrLater)
{ {
captureRectangle.Inflate(Conf.Win10BorderCrop); captureRectangle.Inflate(Conf.Win10BorderCrop);
} }
@ -1079,7 +1079,7 @@ namespace GreenshotPlugin.Core {
} }
if (capturedBitmap != null) { if (capturedBitmap != null) {
// Not needed for Windows 8 // Not needed for Windows 8
if (!Environment.OSVersion.IsWindows8OrLater()) { if (!WindowsVersion.IsWindows8OrLater) {
// Only if the Inivalue is set, not maximized and it's not a tool window. // Only if the Inivalue is set, not maximized and it's not a tool window.
if (Conf.WindowCaptureRemoveCorners && !Maximised && (ExtendedWindowStyle & ExtendedWindowStyleFlags.WS_EX_TOOLWINDOW) == 0) { if (Conf.WindowCaptureRemoveCorners && !Maximised && (ExtendedWindowStyle & ExtendedWindowStyleFlags.WS_EX_TOOLWINDOW) == 0) {
// Remove corners // Remove corners

View file

@ -0,0 +1,107 @@
// Copyright (c) Dapplo and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
namespace GreenshotPlugin.Core
{
/// <summary>
/// Extension methods to test the windows version
/// </summary>
public static class WindowsVersion
{
/// <summary>
/// Get the current windows version
/// </summary>
public static Version WinVersion { get; } = Environment.OSVersion.Version;
/// <summary>
/// Test if the current OS is Windows 10
/// </summary>
/// <returns>true if we are running on Windows 10</returns>
public static bool IsWindows10 { get; } = WinVersion.Major == 10;
/// <summary>
/// Test if the current OS is Windows 10 or later
/// </summary>
/// <returns>true if we are running on Windows 10 or later</returns>
public static bool IsWindows10OrLater { get; } = WinVersion.Major >= 10;
/// <summary>
/// Test if the current OS is Windows 7 or later
/// </summary>
/// <returns>true if we are running on Windows 7 or later</returns>
public static bool IsWindows7OrLater { get; } = WinVersion.Major == 6 && WinVersion.Minor >= 1 || WinVersion.Major > 6;
/// <summary>
/// Test if the current OS is Windows 8.0
/// </summary>
/// <returns>true if we are running on Windows 8.0</returns>
public static bool IsWindows8 { get; } = WinVersion.Major == 6 && WinVersion.Minor == 2;
/// <summary>
/// Test if the current OS is Windows 8(.1)
/// </summary>
/// <returns>true if we are running on Windows 8(.1)</returns>
public static bool IsWindows81 { get; } = WinVersion.Major == 6 && WinVersion.Minor == 3;
/// <summary>
/// Test if the current OS is Windows 8.0 or 8.1
/// </summary>
/// <returns>true if we are running on Windows 8.1 or 8.0</returns>
public static bool IsWindows8X { get; } = IsWindows8 || IsWindows81;
/// <summary>
/// Test if the current OS is Windows 8.1 or later
/// </summary>
/// <returns>true if we are running on Windows 8.1 or later</returns>
public static bool IsWindows81OrLater { get; } = WinVersion.Major == 6 && WinVersion.Minor >= 3 || WinVersion.Major > 6;
/// <summary>
/// Test if the current OS is Windows 8 or later
/// </summary>
/// <returns>true if we are running on Windows 8 or later</returns>
public static bool IsWindows8OrLater { get; } = WinVersion.Major == 6 && WinVersion.Minor >= 2 || WinVersion.Major > 6;
/// <summary>
/// Test if the current OS is Windows Vista
/// </summary>
/// <returns>true if we are running on Windows Vista or later</returns>
public static bool IsWindowsVista { get; } = WinVersion.Major >= 6 && WinVersion.Minor == 0;
/// <summary>
/// Test if the current OS is Windows Vista or later
/// </summary>
/// <returns>true if we are running on Windows Vista or later</returns>
public static bool IsWindowsVistaOrLater { get; } = WinVersion.Major >= 6;
/// <summary>
/// Test if the current OS is from before Windows Vista (e.g. Windows XP)
/// </summary>
/// <returns>true if we are running on Windows from before Vista</returns>
public static bool IsWindowsBeforeVista { get; } = WinVersion.Major < 6;
/// <summary>
/// Test if the current OS is Windows XP
/// </summary>
/// <returns>true if we are running on Windows XP or later</returns>
public static bool IsWindowsXp { get; } = WinVersion.Major == 5 && WinVersion.Minor >= 1;
/// <summary>
/// Test if the current OS is Windows XP or later
/// </summary>
/// <returns>true if we are running on Windows XP or later</returns>
public static bool IsWindowsXpOrLater { get; } = WinVersion.Major >= 5 || WinVersion.Major == 5 && WinVersion.Minor >= 1;
/// <summary>
/// Test if the current Windows version is 10 and the build number or later
/// See the build numbers <a href="https://en.wikipedia.org/wiki/Windows_10_version_history">here</a>
/// </summary>
/// <param name="minimalBuildNumber">int</param>
/// <returns>bool</returns>
public static bool IsWindows10BuildOrLater(int minimalBuildNumber)
{
return IsWindows10 && WinVersion.Build >= minimalBuildNumber;
}
}
}

View file

@ -25,12 +25,12 @@ using System.Text;
namespace Greenshot.IniFile { namespace Greenshot.IniFile {
/// <summary> /// <summary>
/// /// The IniReader does exactly what it says, it reads the .ini file
/// </summary> /// </summary>
public static class IniReader { public static class IniReader {
private const string SectionStart = "["; private const char SectionStartToken = '[';
private const string SectionEnd = "]"; private const char SectionEndToken = ']';
private const string Comment = ";"; private const char CommentToken = ';';
private static readonly char[] Assignment = { '=' }; private static readonly char[] Assignment = { '=' };
/// <summary> /// <summary>
@ -52,11 +52,16 @@ namespace Greenshot.IniFile {
continue; continue;
} }
string cleanLine = line.Trim(); string cleanLine = line.Trim();
if (cleanLine.Length == 0 || cleanLine.StartsWith(Comment)) { if (cleanLine.Length == 0 || cleanLine[0] == CommentToken) {
continue; continue;
} }
if (cleanLine.StartsWith(SectionStart)) { if (cleanLine[0] == SectionStartToken) {
string section = line.Replace(SectionStart, string.Empty).Replace(SectionEnd, string.Empty).Trim(); var sectionEndIndex = line.IndexOf(SectionEndToken,1);
if (sectionEndIndex <0)
{
continue;
}
string section = line.Substring(1,sectionEndIndex-1).Trim();
if (!ini.TryGetValue(section, out nameValues)) if (!ini.TryGetValue(section, out nameValues))
{ {
nameValues = new Dictionary<string, string>(); nameValues = new Dictionary<string, string>();

View file

@ -30,6 +30,7 @@ using Microsoft.Win32.SafeHandles;
using System.Security; using System.Security;
using System.Security.Permissions; using System.Security.Permissions;
using log4net; using log4net;
using GreenshotPlugin.Core.Enums;
namespace GreenshotPlugin.UnmanagedHelpers { namespace GreenshotPlugin.UnmanagedHelpers {
/// <summary> /// <summary>
@ -62,6 +63,19 @@ namespace GreenshotPlugin.UnmanagedHelpers {
[DllImport("user32", SetLastError = true)] [DllImport("user32", SetLastError = true)]
public static extern bool keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo); public static extern bool keybd_event(byte bVk, byte bScan, uint dwFlags, int dwExtraInfo);
/// <summary>
/// Determines whether the specified window handle identifies an existing window.
/// </summary>
/// <param name="hWnd">A handle to the window to be tested.</param>
/// <returns>
/// If the window handle identifies an existing window, the return value is true.
/// If the window handle does not identify an existing window, the return value is false.
/// </returns>
[DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWindow(IntPtr hWnd);
[DllImport("user32", SetLastError = true)] [DllImport("user32", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)] [return: MarshalAs(UnmanagedType.Bool)]
public static extern bool IsWindowVisible(IntPtr hWnd); public static extern bool IsWindowVisible(IntPtr hWnd);
@ -125,7 +139,7 @@ namespace GreenshotPlugin.UnmanagedHelpers {
[DllImport("user32", SetLastError = true, EntryPoint = "SetWindowLongPtr")] [DllImport("user32", SetLastError = true, EntryPoint = "SetWindowLongPtr")]
public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int index, IntPtr styleFlags); public static extern IntPtr SetWindowLongPtr(IntPtr hWnd, int index, IntPtr styleFlags);
[DllImport("user32", SetLastError = true)] [DllImport("user32", SetLastError = true)]
public static extern IntPtr MonitorFromWindow(IntPtr hwnd, uint dwFlags); public static extern IntPtr MonitorFromWindow(IntPtr hwnd, MonitorFrom dwFlags);
[DllImport("user32", SetLastError = true)] [DllImport("user32", SetLastError = true)]
public static extern IntPtr MonitorFromRect([In] ref RECT lprc, uint dwFlags); public static extern IntPtr MonitorFromRect([In] ref RECT lprc, uint dwFlags);
[DllImport("user32", SetLastError = true)] [DllImport("user32", SetLastError = true)]
@ -224,8 +238,18 @@ namespace GreenshotPlugin.UnmanagedHelpers {
[DllImport("user32", SetLastError = true)] [DllImport("user32", SetLastError = true)]
public static extern bool CloseDesktop(IntPtr hDesktop); public static extern bool CloseDesktop(IntPtr hDesktop);
/// <summary>
/// The GetWindowDC function retrieves the device context (DC) for the entire window, including title bar, menus, and scroll bars. A window device context permits painting anywhere in a window, because the origin of the device context is the upper-left corner of the window instead of the client area.
/// GetWindowDC assigns default attributes to the window device context each time it retrieves the device context.Previous attributes are lost.
/// See <a href="https://docs.microsoft.com/en-us/windows/desktop/api/winuser/nf-winuser-getwindowdc">GetWindowDC function</a>
/// </summary>
/// <param name="hWnd">A handle to the window whose DC is to be retrieved. If this value is NULL, GetWindowDC retrieves the DC for the entire screen.</param>
/// <returns>If the function succeeds, the return value is a handle to a device context for the specified window.</returns>
[DllImport("user32", SetLastError = true)]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
/// <summary>
/// <summary>
/// Retrieves the cursor location safely, accounting for DPI settings in Vista/Windows 7. /// Retrieves the cursor location safely, accounting for DPI settings in Vista/Windows 7.
/// </summary> /// </summary>
/// <returns>Point with cursor location, relative to the origin of the monitor setup /// <returns>Point with cursor location, relative to the origin of the monitor setup
@ -407,6 +431,21 @@ namespace GreenshotPlugin.UnmanagedHelpers {
return returnValue; return returnValue;
} }
/// <summary>
/// Creates a DC as SafeWindowDcHandle for the whole of the specified hWnd
/// </summary>
/// <param name="hWnd">IntPtr</param>
/// <returns>SafeWindowDcHandle</returns>
public static SafeWindowDcHandle FromWindow(IntPtr hWnd)
{
if (hWnd == IntPtr.Zero)
{
return null;
}
var hDcDesktop = GetWindowDC(hWnd);
return new SafeWindowDcHandle(hWnd, hDcDesktop);
}
public static SafeWindowDcHandle FromDesktop() { public static SafeWindowDcHandle FromDesktop() {
IntPtr hWndDesktop = User32.GetDesktopWindow(); IntPtr hWndDesktop = User32.GetDesktopWindow();
IntPtr hDCDesktop = GetWindowDC(hWndDesktop); IntPtr hDCDesktop = GetWindowDC(hWndDesktop);

View file

@ -19,6 +19,7 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
using GreenshotPlugin.Core;
using System; using System;
using System.Runtime.InteropServices.WindowsRuntime; using System.Runtime.InteropServices.WindowsRuntime;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;

View file

@ -17,18 +17,19 @@
// You should have received a copy of the GNU General Public License // You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
using GreenshotPlugin.Core.Enums;
using System; using System;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using Windows.ApplicationModel.DataTransfer; using Windows.ApplicationModel.DataTransfer;
namespace GreenshotWin10Plugin.Native namespace GreenshotWin10Plugin.Native
{ {
/// <summary> /// <summary>
/// The IDataTransferManagerInterOp is documented here: https://msdn.microsoft.com/en-us/library/windows/desktop/jj542488(v=vs.85).aspx. /// The IDataTransferManagerInterOp is documented here: https://msdn.microsoft.com/en-us/library/windows/desktop/jj542488(v=vs.85).aspx.
/// This interface allows an app to tie the share context to a specific /// This interface allows an app to tie the share context to a specific
/// window using a window handle. Useful for Win32 apps. /// window using a window handle. Useful for Win32 apps.
/// </summary> /// </summary>
[ComImport, Guid("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")] [ComImport, Guid("3A3DCD6C-3EAB-43DC-BCDE-45671CE800C8")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IDataTransferManagerInterOp public interface IDataTransferManagerInterOp
{ {

View file

@ -54,7 +54,7 @@ namespace GreenshotWin10Plugin
/// <returns>IEnumerable with the destinations</returns> /// <returns>IEnumerable with the destinations</returns>
public IEnumerable<IDestination> Destinations() public IEnumerable<IDestination> Destinations()
{ {
if (!Environment.OSVersion.IsWindows10OrLater()) if (!WindowsVersion.IsWindows10OrLater)
{ {
yield break; yield break;
} }