我正在尝试打开包含 WebView2 控件的 WPF 窗口,以便我可以将 OpenID Connect 身份验证添加到现有的 C++ CLI 应用程序。
我们使用 https://github.com/IdentityModel/IdentityModel.OidcClient.Samples/tree/main/WpfWebView2/WpfWebView2 作为我们代码的基础。
如果您不熟悉这个示例,它背后的想法是有一个代表“浏览器”的“自定义”类。此类负责实例化 WPF 窗口、添加 WebView2 控件作为其内容、导航到 OAuth 站点并将身份验证结果返回给调用者。所有这一切都在调用其
InvokeAsync()
方法时发生。
调用此异步方法的是
OidcClient
库中的 IdentityModel.OidcClient
类。 OidcClient
类采用一个设置类,其中之一是实现其 IBrowser
接口的类。您可以通过从应用程序中调用 OidcClient.LoginAsync
来启动此身份验证逻辑。就我而言,这是 C++ 代码。
当我从 C++ 代码中调用
LoginAsync()
时,应用程序会阻塞并且窗口不会打开。我很确定这是一个 UI 线程问题,但我一生都无法弄清楚。
我尝试了多种方法,包括尝试将调用包装在
Application.Current.Dispatcher.Invoke()
和 Application.Current.Dispatcher.BeginInvoke()
中。我尝试检测我是否在 UI 线程上,以便我可以创建一个 DispatcherSynchronizationContext
。
我怀疑问题是来自 C++ 的
async/await
调用从非 UI 工作开始,并且 在某些时候需要执行 UI 工作。在 C++ 中,我可以轻松创建 WPF 窗口的实例(通过 gcnew
),然后调用 Show()
或 ShowDialog()
,但这种多层异步/等待设计会导致问题。
我在 C++ 端使用
LoginAsync().Wait()
,因此这很可能只是没有正确从 C++ 调用异步方法的问题。我什至不知道还能去哪里寻找或者需要哪些额外的知识来调试这个特定的设置。
对
.Wait()
的调用肯定会导致死锁。
如果您不需要立即从 LoginAsync 获得结果,请改为使用
.ContinueWith(...)
注册延续。
如果您需要同步 LoginAsync 的结果,请在 UI 线程中启动一个新的 DispatcherFrame。 C# 代码如下所示:
// Assuming that the 't' is a Task<...>.
var t = LoginAsync();
var frame = new DispatcherFrame();
t.ContinueWith((_) => { frame.Continue = false; }, TaskContinuationOptions.ExecuteSynchronously);
// Block current thread until LoginAsync is completed, while performing all UI stuff.
// (Make sure you call this in UI thread)
Dispatcher.PushFrame(frame);
// Since 't' is already completed, this call won't block the thread.
var loginResult = t.Result;