0

I'm using wpf window to display a modeless dialog inside credential provider. I am setting the parent for my window using WindowInteropHelper. In most cases everything works fine, but in rare cases the window stops responding to the keyboard. KeyDown events arrive, but no character messages appear.

Probably the reason is that I use Show instead of ShowDialog like described here https://stackoverflow.com/a/52089207/22586113.

Has anyone tried using a modeless window from a provider? What are the correct steps to do this?

Show window in most cases works fine, but in very rare cases the window stops responding to the keyboard.

2
  • I’m suggest you use separate thread for your window. Commented Sep 22, 2023 at 8:53
  • @Alexander thank you very much for the advice. Does what I wrote in the answer below seem true? Commented Sep 24, 2023 at 11:02

1 Answer 1

0

If I correctly understood the advice to make a separate thread for my window, then below is an example of code that does this:

using NLog;
using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Interop;
using System.Windows.Threading;

namespace WpfTest
{
    public class LoginUIApplicationInstance: IDisposable
    {
        [DllImport("user32.dll")]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr ProcessId);


        static protected readonly Logger logger = LogManager.GetLogger("WpfTest");

        TestWindow? mainWindow;
        Thread uiThread;
        Dispatcher? uiDispatcher;
        TaskCompletionSource windowCreateCompletion = new TaskCompletionSource();

        public LoginUIApplicationInstance(IntPtr ownerHwnd)
        {
            // credential provider thread ID
            var credentialProviderThreadId = Environment.CurrentManagedThreadId;
            // ID of the parent window thread obtained using OnCreatingWindow
            var parentWindowThreadId = GetWindowThreadProcessId(ownerHwnd, IntPtr.Zero);

            uiThread = new Thread(() =>
            {
                SynchronizationContext.SetSynchronizationContext(new DispatcherSynchronizationContext());
                uiDispatcher = Dispatcher.CurrentDispatcher;

                mainWindow = new TestWindow();
                var interopHelper = new WindowInteropHelper(mainWindow);
                interopHelper.Owner = ownerHwnd;

                mainWindow.Closed += (sender, e) => mainWindow.Dispatcher.InvokeShutdown();
                windowCreateCompletion.SetResult();
                Dispatcher.Run();
            });

            uiThread.SetApartmentState(ApartmentState.STA);
            uiThread.Start();

            logger.Info("Credential Provider threadId: {id1}, Parent window threadId: {id2}, Current window threadId: {id3}",
                credentialProviderThreadId, parentWindowThreadId, uiThread.ManagedThreadId);
        }

        public void Dispose()
        {
            windowCreateCompletion.Task.ContinueWith((t) => {
                if (mainWindow != null)
                    uiDispatcher?.BeginInvoke(mainWindow.Close);
            });
            uiThread.Join();
        }

        public void Hide()
        {
            windowCreateCompletion.Task.ContinueWith((t) => {
                if (mainWindow != null)
                    uiDispatcher?.BeginInvoke(mainWindow.Hide);
            });
        }

        public void Show()
        {
            windowCreateCompletion.Task.ContinueWith((t) => {
                if (mainWindow != null)
                    uiDispatcher?.BeginInvoke(mainWindow.Show);
            });
        }
    }
}

The credential provider receives the handle of the parent window (via OnCreatingWindow) and creates a window in a new UI thread where it sets the parent via interopHelper.Owner = ownerHwnd. Then, when the provider needs to show or hide the window, it calls the appropriate show/hide methods.

The interesting thing is that the parent window is created on a thread different from the one in which the provider methods are executed: credentialProviderThreadId != parentWindowThreadId.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.