Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add MetroWindowAutomationPeer to prevent Win32Exception #3893

Merged
merged 1 commit into from
Jul 24, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions src/MahApps.Metro/Automation/Peers/MetroWindowAutomationPeer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
// 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.

using System.Windows;
using System.Windows.Automation.Peers;
using JetBrains.Annotations;
using MahApps.Metro.Controls;

namespace MahApps.Metro.Automation.Peers
{
public class MetroWindowAutomationPeer : FrameworkElementAutomationPeer
{
public MetroWindowAutomationPeer([NotNull] Window owner)
: base(owner)
{
}

protected override string GetClassNameCore()
{
return "MetroWindow";
}

protected override string GetNameCore()
{
string name = base.GetNameCore();

if (name == string.Empty)
{
MetroWindow window = (MetroWindow)this.Owner;

name = window.GetWindowText();
if (name == null)
{
name = string.Empty;
}
}

return name;
}

protected override AutomationControlType GetAutomationControlTypeCore()
{
return AutomationControlType.Window;
}

protected override Rect GetBoundingRectangleCore()
{
MetroWindow window = (MetroWindow)this.Owner;

Rect bounds = window.GetWindowBoundingRectangle();

return bounds;
}
}
}
17 changes: 13 additions & 4 deletions src/MahApps.Metro/Controls/MetroWindow.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
using System.Linq;
using System.Reflection;
using System.Windows;
using System.Windows.Automation.Peers;
using System.Windows.Controls;
using System.Windows.Input;
using System.Windows.Media;
Expand All @@ -21,6 +22,7 @@
using ControlzEx.Standard;
using ControlzEx.Theming;
using JetBrains.Annotations;
using MahApps.Metro.Automation.Peers;
using MahApps.Metro.Behaviors;
using MahApps.Metro.Controls.Dialogs;
using MahApps.Metro.ValueBoxes;
Expand Down Expand Up @@ -1246,13 +1248,20 @@ public override void OnApplyTemplate()
}
}

protected IntPtr CriticalHandle
/// <summary>
/// Creates AutomationPeer (<see cref="UIElement.OnCreateAutomationPeer"/>)
/// </summary>
protected override AutomationPeer OnCreateAutomationPeer()
{
return new MetroWindowAutomationPeer(this);
}

protected internal IntPtr CriticalHandle
{
get
{
var value = typeof(Window)
.GetProperty("CriticalHandle", BindingFlags.NonPublic | BindingFlags.Instance)
.GetValue(this, new object[0]);
this.VerifyAccess();
var value = typeof(Window).GetProperty("CriticalHandle", BindingFlags.NonPublic | BindingFlags.Instance)?.GetValue(this, new object[0]) ?? IntPtr.Zero;
return (IntPtr)value;
}
}
Expand Down
79 changes: 79 additions & 0 deletions src/MahApps.Metro/Controls/WinApiHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
// See the LICENSE file in the project root for more information.

using System;
using System.ComponentModel;
using System.Text;
using System.Windows;
using System.Windows.Interop;
using System.Windows.Media;
Expand Down Expand Up @@ -36,5 +38,82 @@ public static Size GetMonitorWorkSize(this Visual visual)
return default;
}
#pragma warning restore 618

/// <summary> Gets the text associated with the given window handle. </summary>
/// <param name="window"> The window to act on. </param>
/// <returns> The window text. </returns>
#pragma warning disable 618
internal static string GetWindowText(this Window window)
{
if (window != null
&& PresentationSource.FromVisual(window) is HwndSource source
&& source.IsDisposed == false
&& source.RootVisual is null == false
&& source.Handle != IntPtr.Zero)
{
int size = NativeMethods.GetWindowTextLength(source.Handle);
if (size == 0)
{
var lastError = Win32Error.GetLastError();
if (lastError != Win32Error.ERROR_SUCCESS)
{
throw new Win32Exception(lastError.Error);
}

return string.Empty;
}

var builder = new StringBuilder(size + 1);
var finalLength = NativeMethods.GetWindowText(source.Handle, builder, builder.Capacity);
if (finalLength == 0)
{
var lastError = Win32Error.GetLastError();
if (lastError != Win32Error.ERROR_SUCCESS)
{
throw new Win32Exception(lastError.Error);
}

return string.Empty;
}

return builder.ToString();
}

return default;
}

/// <summary> Gets the text associated with the given window handle. </summary>
/// <param name="window"> The window to act on. </param>
/// <returns> The window text. </returns>
internal static Rect GetWindowBoundingRectangle(this Window window)
{
Rect bounds = new Rect(0, 0, 0, 0);

if (window != null
&& PresentationSource.FromVisual(window) is HwndSource source
&& source.IsDisposed == false
&& source.RootVisual is null == false
&& source.Handle != IntPtr.Zero)
{
RECT rc = new RECT(0, 0, 0, 0);

try
{
rc = NativeMethods.GetWindowRect(source.Handle);
}
// Allow empty catch statements.
#pragma warning disable 56502
catch (Win32Exception)
{
}
// Disallow empty catch statements.
#pragma warning restore 56502

bounds = new Rect(rc.Left, rc.Top, rc.Width, rc.Height);
}

return bounds;
}
#pragma warning restore 618
}
}