Skip to content
Draft
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
100 changes: 98 additions & 2 deletions src/AuthProviders/UIContext.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Windows.Forms;

namespace KeePassWinHello
Expand All @@ -10,13 +11,107 @@ internal sealed class UIContext : IWin32Window
public string Message { get; private set; }
public HWND ParentWindowHandle { get; private set; }

private readonly Control _uiThreadControl;
private Form _promptParentWindow;

IntPtr IWin32Window.Handle { get { return ParentWindowHandle.Value; } }

public UIContext(string message, HWND windowHandle)
{
Message = message;
ParentWindowHandle = windowHandle;
}

public UIContext(string message, IWin32Window parentWindow)
: this(message, new HWND(parentWindow.Handle))
{
_uiThreadControl = parentWindow as Control;
}

public HWND PromptParentWindowHandle
{
get
{
if (Win32Window.IsVisibleAndNotMinimized(ParentWindowHandle))
return ParentWindowHandle;

return EnsurePromptParentWindowHandle();
}
}

public void DisposePromptParentWindow()
{
if (ShouldInvokeOnUIThread())
{
_uiThreadControl.Invoke(new MethodInvoker(DisposePromptParentWindowOnCurrentThread));
return;
}

DisposePromptParentWindowOnCurrentThread();
}

private void DisposePromptParentWindowOnCurrentThread()
{
if (_promptParentWindow != null)
{
_promptParentWindow.Dispose();
_promptParentWindow = null;
}
}

private HWND EnsurePromptParentWindowHandle()
{
if (ShouldInvokeOnUIThread())
{
return (HWND)_uiThreadControl.Invoke(new Func<HWND>(EnsurePromptParentWindowHandleOnCurrentThread));
}

return EnsurePromptParentWindowHandleOnCurrentThread();
}

private HWND EnsurePromptParentWindowHandleOnCurrentThread()
{
if (_promptParentWindow == null || _promptParentWindow.IsDisposed)
_promptParentWindow = PromptParentForm.CreateCentered();

return new HWND(_promptParentWindow.Handle);
}

private bool ShouldInvokeOnUIThread()
{
return _uiThreadControl != null
&& !_uiThreadControl.IsDisposed
&& _uiThreadControl.InvokeRequired;
}

private sealed class PromptParentForm : Form
{
private PromptParentForm()
{
FormBorderStyle = FormBorderStyle.None;
Opacity = 0.01;
ShowInTaskbar = false;
Size = new Size(1, 1);
StartPosition = FormStartPosition.Manual;
Text = Settings.ProductName;
}

protected override bool ShowWithoutActivation
{
get { return true; }
}

public static PromptParentForm CreateCentered()
{
PromptParentForm form = new PromptParentForm();
Rectangle workingArea = Screen.FromPoint(Cursor.Position).WorkingArea;
form.Location = new Point(
workingArea.Left + (workingArea.Width - form.Width) / 2,
workingArea.Top + (workingArea.Height - form.Height) / 2);
form.Show();
return form;
}
}
}

internal sealed class UIContextManager
Expand Down Expand Up @@ -46,7 +141,7 @@ public UIContext CurrentContext

public IDisposable PushContext(string message, IWin32Window parentWindow)
{
var context = new UIContext(message, new HWND(parentWindow.Handle));
var context = new UIContext(message, parentWindow);
_contexts.AddFirst(context);
return new Disposer(this, context);
}
Expand All @@ -66,7 +161,8 @@ public void Dispose()
{
bool removed = _contextManager._contexts.Remove(_context);
Debug.Assert(removed);
_context.DisposePromptParentWindow();
}
}
}
}
}
2 changes: 1 addition & 1 deletion src/AuthProviders/WinHelloProvider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -482,7 +482,7 @@ private void ApplyUIContext(SafeNCryptKeyHandle ngcKeyHandle, bool retryMessage
var uiContext = _uiContextManager.CurrentContext;
if (uiContext != null)
{
IntPtr parentWindowHandle = uiContext.ParentWindowHandle.Value;
IntPtr parentWindowHandle = uiContext.PromptParentWindowHandle.Value;
if (parentWindowHandle != IntPtr.Zero)
{
byte[] handle = BitConverter.GetBytes(IntPtr.Size == 8 ? parentWindowHandle.ToInt64() : parentWindowHandle.ToInt32());
Expand Down
13 changes: 13 additions & 0 deletions src/Utilities/Win32Window.cs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ public static Win32Window GetOrNull(HWND hwnd)
return null;
}

public static bool IsVisibleAndNotMinimized(HWND hwnd)
{
return hwnd.IsValid
&& WinAPI.IsWindowVisible(hwnd.Value).Result
&& !WinAPI.IsIconic(hwnd.Value).Result;
}

#endregion Creation


Expand Down Expand Up @@ -233,6 +240,12 @@ private static class WinAPI

[DllImport(User32, SetLastError = true, CharSet = CharSet.Unicode)]
public static extern HWND FindWindowEx(IntPtr parentHandle, IntPtr childAfter, string lpClassName, string lpWindowName);

[DllImport(User32, SetLastError = true)]
public static extern BOOL IsWindowVisible(IntPtr hWnd);

[DllImport(User32, SetLastError = true)]
public static extern BOOL IsIconic(IntPtr hWnd);
}
}
}