Skip to content
Original file line number Diff line number Diff line change
Expand Up @@ -63,76 +63,5 @@ public static IntPtr LoadLibraryFromSystemPathIfAvailable(string libraryName)
}

#endregion

#region PInvoke DpiRelated
// This section could go to Nativemethods.cs or Safenativemethods.cs but we have separate copies of them in each library (System.winforms, System.Design and System.Drawing).
// Keeping them here will reduce duplicating code but may have to take care of security warnings (if any).
// These APIs are available starting Windows 10, version 1607 only.
[DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
internal static extern DpiAwarenessContext GetThreadDpiAwarenessContext();

[DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
internal static extern DpiAwarenessContext SetThreadDpiAwarenessContext(DpiAwarenessContext dpiContext);

[DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AreDpiAwarenessContextsEqual(DpiAwarenessContext dpiContextA, DpiAwarenessContext dpiContextB);

/// <summary>
/// Tries to compare two DPIawareness context values. Return true if they were equal.
/// Return false when they are not equal or underlying OS does not support this API.
/// </summary>
/// <returns>true/false</returns>
public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext dpiContextA, DpiAwarenessContext dpiContextB)
{
if (ApiHelper.IsApiAvailable(ExternDll.User32, "AreDpiAwarenessContextsEqual"))
{
return AreDpiAwarenessContextsEqual(dpiContextA, dpiContextB);
}

return false;
}

/// <summary>
/// Tries to get thread dpi awareness context
/// </summary>
/// <returns> returns thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero.</returns>
public static DpiAwarenessContext TryGetThreadDpiAwarenessContext()
{
if (ApiHelper.IsApiAvailable(ExternDll.User32, "GetThreadDpiAwarenessContext"))
{
return GetThreadDpiAwarenessContext();
}
else
{
// legacy OS that does not have this API available.
return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED;
}
}

/// <summary>
/// Tries to set thread dpi awareness context
/// </summary>
/// <returns> returns old thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero.</returns>
public static DpiAwarenessContext TrySetThreadDpiAwarenessContext(DpiAwarenessContext dpiCOntext)
{
if (ApiHelper.IsApiAvailable(ExternDll.User32, "SetThreadDpiAwarenessContext"))
{
return SetThreadDpiAwarenessContext(dpiCOntext);
}
else
{
// legacy OS that does not have this API available.
return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED;
}
}
/*
// Dpi awareness context values. Matching windows values.
public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_UNAWARE = (-1);
public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_SYSTEM_AWARE = (-2);
public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE = (-3);
public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = (-4);
public static readonly DPI_AWARENESS_CONTEXT DPI_AWARENESS_CONTEXT_UNSPECIFIED = (0);*/
#endregion
}
}
4 changes: 2 additions & 2 deletions src/System.Windows.Forms.Design.Editors/src/Misc/DpiHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -290,8 +290,8 @@ internal static bool EnableDpiChangedMessageHandling
{
// We can't cache this value because different top level windows can have different DPI awareness context
// for mixed mode applications.
DpiAwarenessContext dpiAwareness = CommonUnsafeNativeMethods.GetThreadDpiAwarenessContext();
return CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
DpiAwarenessContext dpiAwareness = DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext();
return DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(dpiAwareness, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In line 585ff in this file, should we add DpiUnawareGdiScaled to have parity with the DpiHelper from the runtime?
Can we actually consolidate DpiHelper, maybe with #ifs and File References, so we have parity here?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, DpiHelper and other common files should be shared between the projects. Either linked into each project or into a m=new assembly that has internals visible to all other assemblies in this repo(as needed). Probably keeping files in common folder is a good stating point.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@JuditRose is working on creating this infrastructure for PR #439

Copy link
Copy Markdown
Contributor

@JuditRose JuditRose Feb 15, 2019

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a Common folder already in winforms/src folder. Files in the Common folder can be referenced from the csproj files. E.g. https://github.com/dotnet/winforms/blob/master/src/System.Windows.Forms/src/System.Windows.Forms.csproj#L24

}
else
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

namespace System.Windows.Forms
{
using Runtime.InteropServices;
using Runtime.Versioning;
using System;

internal class DpiUnsafeNativeMethods
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as before: Would it make sense to consolidate this with the Runtime via a File Reference?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes very probably

{

// This section could go to Nativemethods.cs or Safenativemethods.cs but we have separate copies of them in each library (System.winforms, System.Design and System.Drawing).
// Keeping them here will reduce duplicating code but may have to take care of security warnings (if any).
// These APIs are available starting Windows 10, version 1607 only.

[DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
internal static extern DpiAwarenessContext GetThreadDpiAwarenessContext();

[DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
internal static extern DpiAwarenessContext SetThreadDpiAwarenessContext(DpiAwarenessContext dpiContext);

[DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static extern bool AreDpiAwarenessContextsEqual(DpiAwarenessContext dpiContextA, DpiAwarenessContext dpiContextB);

[DllImport(ExternDll.User32, SetLastError = true, ExactSpelling = true, CharSet = CharSet.Auto)]
[ResourceExposure(ResourceScope.None)]
internal static extern DpiAwarenessContext GetWindowDpiAwarenessContext(IntPtr hwnd);

/// <summary>
/// Tries to compare two DPIawareness context values. Return true if they were equal.
/// Return false when they are not equal or underlying OS does not support this API.
/// </summary>
/// <returns>true/false</returns>
public static bool TryFindDpiAwarenessContextsEqual(DpiAwarenessContext dpiContextA, DpiAwarenessContext dpiContextB)
{
if(dpiContextA == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED && dpiContextB == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)
{
return true;
}
if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.AreDpiAwarenessContextsEqual)))
{
return AreDpiAwarenessContextsEqual(dpiContextA, dpiContextB);
}

return false;
}

/// <summary>
/// Tries to get thread dpi awareness context
/// </summary>
/// <returns> returns thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero.</returns>
public static DpiAwarenessContext TryGetThreadDpiAwarenessContext()
{
if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.GetThreadDpiAwarenessContext)))
{
return GetThreadDpiAwarenessContext();
}
else
{
// legacy OS that does not have this API available.
return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED;
}
}

/// <summary>
/// Tries to set thread dpi awareness context
/// </summary>
/// <returns> returns old thread dpi awareness context if API is available in this version of OS. otherwise, return IntPtr.Zero.</returns>
public static DpiAwarenessContext TrySetThreadDpiAwarenessContext(DpiAwarenessContext dpiContext)
{
if (ApiHelper.IsApiAvailable(ExternDll.User32, nameof(DpiUnsafeNativeMethods.SetThreadDpiAwarenessContext)))
{
if (dpiContext == DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)
{
throw new ArgumentException(nameof(dpiContext), dpiContext.ToString());
}
return SetThreadDpiAwarenessContext(dpiContext);
}
else
{
// legacy OS that does not have this API available.
return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED;
}
}

/// <summary>
/// Tries to get window dpi awareness context
/// </summary>
/// <returns> returns window dpi awareness context if API is available in this version of OS. otherwise, return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED.</returns>
public static DpiAwarenessContext TryGetWindowDpiAwarenessContext(IntPtr hwnd)
{
if (ApiHelper.IsApiAvailable(ExternDll.User32, "GetWindowDpiAwarenessContext"))
{
return GetWindowDpiAwarenessContext(hwnd);
}
else
{
// legacy OS that does not have this API available.
return DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED;
}
}
}
}
15 changes: 7 additions & 8 deletions src/System.Windows.Forms/src/System/Windows/Forms/Application.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1242,7 +1242,7 @@ private static void RaiseThreadExit() {
/// "Parks" the given HWND to a temporary HWND. This allows WS_CHILD windows to
/// be parked.
/// </devdoc>
internal static void ParkHandle(HandleRef handle, DpiAwarenessContext dpiAwarenessContext = DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) {
internal static void ParkHandle(HandleRef handle, DpiAwarenessContext? dpiAwarenessContext = null) {
Debug.Assert(UnsafeNativeMethods.IsWindow(handle), "Handle being parked is not a valid window handle");
Debug.Assert(((int)UnsafeNativeMethods.GetWindowLong(handle, NativeMethods.GWL_STYLE) & NativeMethods.WS_CHILD) != 0, "Only WS_CHILD windows should be parked.");

Expand All @@ -1257,7 +1257,7 @@ internal static void ParkHandle(HandleRef handle, DpiAwarenessContext dpiAwarene
/// </summary>
/// <param name="cp"> create params for control handle</param>
/// <param name="dpiContext"> dpi awareness</param>
internal static void ParkHandle(CreateParams cp, DpiAwarenessContext dpiAwarenessContext = DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED) {
internal static void ParkHandle(CreateParams cp, DpiAwarenessContext? dpiAwarenessContext = null) {

ThreadContext cxt = ThreadContext.FromCurrent();
if (cxt != null) {
Expand Down Expand Up @@ -1289,7 +1289,7 @@ public static void OnThreadException(Exception t) {
/// "Unparks" the given HWND to a temporary HWND. This allows WS_CHILD windows to
/// be parked.
/// </devdoc>
internal static void UnparkHandle(HandleRef handle, DpiAwarenessContext context) {
internal static void UnparkHandle(HandleRef handle, DpiAwarenessContext? context) {
ThreadContext cxt = GetContextForHandle(handle);
if (cxt != null) {
cxt.GetParkingWindow(context).UnparkHandle(handle);
Expand Down Expand Up @@ -2493,7 +2493,7 @@ internal bool CustomThreadExceptionHandlerAttached {
/// if it needs to.
/// </devdoc>
/// <internalonly/>
internal ParkingWindow GetParkingWindow(DpiAwarenessContext context) {
internal ParkingWindow GetParkingWindow(DpiAwarenessContext? context) {

// Locking 'this' here is ok since this is an internal class.
lock(this) {
Expand All @@ -2507,7 +2507,6 @@ internal ParkingWindow GetParkingWindow(DpiAwarenessContext context) {
Debug.WriteLine(CoreSwitches.PerfTrack.Enabled, Environment.StackTrace);
}
#endif

using (DpiHelper.EnterDpiAwarenessScope(context)) {
parkingWindow = new ParkingWindow();
}
Expand All @@ -2522,7 +2521,7 @@ internal ParkingWindow GetParkingWindow(DpiAwarenessContext context) {
/// Returns parking window that matches dpi awareness context. return null if not found.
/// </summary>
/// <returns>return matching parking window from list. returns null if not found</returns>
internal ParkingWindow GetParkingWindowForContext(DpiAwarenessContext context) {
internal ParkingWindow GetParkingWindowForContext(DpiAwarenessContext? context) {

if (parkingWindows.Count == 0) {
return null;
Expand All @@ -2531,15 +2530,15 @@ internal ParkingWindow GetParkingWindowForContext(DpiAwarenessContext context) {
// Legacy OS/target framework scenario where ControlDpiContext is set to DPI_AWARENESS_CONTEXT.DPI_AWARENESS_CONTEXT_UNSPECIFIED
// because of 'ThreadContextDpiAwareness' API unavailability or this feature is not enabled.

if (!DpiHelper.IsScalingRequirementMet || CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(context, DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED)) {
if (!DpiHelper.IsScalingRequirementMet || DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(context, null)) {

Debug.Assert(parkingWindows.Count == 1, "parkingWindows count can not be > 1 for legacy OS/target framework versions");
return parkingWindows[0];
}

// Supported OS scenario.
foreach (var p in parkingWindows) {
if (CommonUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(p.DpiAwarenessContext, context)) {
if (DpiUnsafeNativeMethods.TryFindDpiAwarenessContextsEqual(p.DpiAwarenessContext, context)) {
return p;
}
}
Expand Down
13 changes: 9 additions & 4 deletions src/System.Windows.Forms/src/System/Windows/Forms/Control.cs
Original file line number Diff line number Diff line change
Expand Up @@ -577,7 +577,7 @@ public Control( Control parent, string text, int left, int top, int width, int h
/// <summary>
/// gets or sets control Dpi awareness context value.
/// </summary>
internal DpiAwarenessContext DpiAwarenessContext {
internal DpiAwarenessContext? DpiAwarenessContext {
get {
return window.DpiAwarenessContext;
}
Expand Down Expand Up @@ -5617,7 +5617,7 @@ protected virtual void CreateHandle() {
//
if (cp.Parent == IntPtr.Zero && (cp.Style & NativeMethods.WS_CHILD) != 0) {
Debug.Assert((cp.ExStyle & NativeMethods.WS_EX_MDICHILD) == 0, "Can't put MDI child forms on the parking form");
Application.ParkHandle(cp);
Application.ParkHandle(cp, window.DpiAwarenessContext);
}

window.CreateHandle(cp);
Expand All @@ -5638,6 +5638,11 @@ protected virtual void CreateHandle() {
}
}

protected void ApplicationParkHandleWithWindowContext(HandleRef handle)
{
Application.ParkHandle(handle, window.DpiAwarenessContext);
}

/// <include file='doc\Control.uex' path='docs/doc[@for="Control.CreateControl"]/*' />
/// <devdoc>
/// Forces the creation of the control. This includes the creation of the handle,
Expand Down Expand Up @@ -8096,7 +8101,7 @@ internal virtual void OnParentHandleRecreating() {

// use SetParent directly so as to not raise ParentChanged events
if (IsHandleCreated) {
Application.ParkHandle(new HandleRef(this, this.Handle));
Application.ParkHandle(new HandleRef(this, this.Handle), window.DpiAwarenessContext);
}
}

Expand Down Expand Up @@ -11420,7 +11425,7 @@ private void SetParentHandle(IntPtr value) {
}
if (!GetTopLevel()) {
if (value == IntPtr.Zero) {
Application.ParkHandle(new HandleRef(window, Handle));
Application.ParkHandle(new HandleRef(window, Handle), window.DpiAwarenessContext);
UpdateRoot();
}
else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -459,6 +459,7 @@ public const int
public const int E_NOTIMPL = unchecked((int)0x80004001),
E_OUTOFMEMORY = unchecked((int)0x8007000E),
E_INVALIDARG = unchecked((int)0x80070057),
E_ACCESSDENIED = unchecked((int)0x80070005),
E_NOINTERFACE = unchecked((int)0x80004002),
E_POINTER = unchecked((int)0x80004003),
E_FAIL = unchecked((int)0x80004005),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,9 @@ public class NativeWindow : MarshalByRefObject, IWin32Window {
NativeWindow previousWindow; // doubly linked list of subclasses.
NativeWindow nextWindow;
WeakReference weakThisPtr;
private DpiAwarenessContext windowDpiAwarenessContext = DpiHelper.IsScalingRequirementMet ?
CommonUnsafeNativeMethods.TryGetThreadDpiAwarenessContext() :
DpiAwarenessContext.DPI_AWARENESS_CONTEXT_UNSPECIFIED;
private DpiAwarenessContext? windowDpiAwarenessContext = DpiHelper.IsScalingRequirementMet ?
(DpiAwarenessContext?)DpiUnsafeNativeMethods.TryGetThreadDpiAwarenessContext() :
null;

static NativeWindow() {
EventHandler shutdownHandler = new EventHandler(OnShutdown);
Expand Down Expand Up @@ -127,7 +127,7 @@ public NativeWindow() {
/// <summary>
/// Cache window DpiContext awareness information that helps to create handle with right context at the later time.
/// </summary>
internal DpiAwarenessContext DpiAwarenessContext {
internal DpiAwarenessContext? DpiAwarenessContext {
get {
return windowDpiAwarenessContext;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,7 @@ protected override void WndProc(ref Message m) {
if (this.ActiveXState >= WebBrowserHelper.AXState.InPlaceActive) {
IntPtr hwndInPlaceObject;
if (NativeMethods.Succeeded(this.AXInPlaceObject.GetWindow(out hwndInPlaceObject))) {
Application.ParkHandle(new HandleRef(this.AXInPlaceObject, hwndInPlaceObject));
this.ApplicationParkHandleWithWindowContext(new HandleRef(this.AXInPlaceObject, hwndInPlaceObject));
}
}

Expand Down
Loading