释放 Web 浏览器控件会导致在系统浏览器中打开新页面

本文关键字:浏览器 系统 新页面 Web 控件 释放 | 更新日期: 2023-09-27 17:55:24

问题是我上一个问题的延续。问题的简短解释:我尝试在WinForms应用程序中使用WebBrowser vk.com 使用类似OAuth2的方法授权用户,因此我需要在WebBrowser中打开特定于应用程序的URL,vk.com 将重定向到授权页面。授权后,vk.com 重定向到https://oauth.vk.com/blank.html#access_token={accessToken}&expires_in={expiresIn}&user_id={userId},我可以在其中获取令牌并将其与 vk.com API 一起使用。一切都很好,除了在授权后的不同时间在浏览器中打开的奇数页面(带有 url https://oauth.vk.com/blank.html#access_token={accessToken}&expires_in={expiresIn}&user_id={userId}%20-%20#access_token={accessToken}&expires_in={expiresIn}&user_id={userId})。我有点重新设计了示例,现在如果在处理 WebBrowwser(AuthenticationForm -> authenticationBrowser_DocumentCompleted)控件后出现GC.WaitForPendingFinalizers(); GC.Collect();,则页面无法在系统浏览器中打开,并在另一种情况下打开。

重要提示:仅当必须填写授权表单(即 Web 浏览器不包含有效的会话数据)时,该错误才会显现出来。 丢弃会话可以转到设置 ->页面的安全性 ->查看活动历史记录->关闭所有会话

主窗体代码:

public partial class Form1 : Form
{
    private string _token;
    private readonly Uri _redirectUri = new Uri("https://oauth.vk.com/blank.html");
    private readonly Uri _request = new Uri("https://api.vk.com/method/users.get.xml?user_ids=1&fields=online");
    private readonly Uri _authorizationUri =
        new Uri("https://oauth.vk.com/authorize?client_id=3836576&scope=8&redirect_uri=https://oauth.vk.com/blank.html&display=page&response_type=token");
    public Form1()
    {
        InitializeComponent();
    }
    private async void loginButton_Click(object sender, EventArgs e)
    {
        var authenticationForm = new AuthenticationForm();
        authenticationForm.Show();
        _token = await Authenticate(authenticationForm.GetToken);
        authenticationForm.Close();
    }
    private async Task<string> Authenticate(Func<Uri, Uri, Task<string>> aunthenticationResultGetter)
    {
        var token = await aunthenticationResultGetter(_authorizationUri, _redirectUri);
        //...
        return token;
    }
    private async void doRequestButton_Click(object sender, EventArgs e)
    {
        //assume, we using token here
        var httpClient = new HttpClient();
        var response = await httpClient.GetAsync(_request);
        var responseString = await response.Content.ReadAsStringAsync();
        outputTextBox.AppendText(responseString);
    }
}

认证表单代码:

public partial class AuthenticationForm : Form
{
    private readonly TaskCompletionSource<string> _tokenCompletitionSource = new TaskCompletionSource<string>();
    private Uri _redirectUri;
    private WebBrowser _authenticationBrowser;
    public AuthenticationForm()
    {
        InitializeComponent();
    }
    public async Task<string> GetToken(Uri authUri, Uri redirectUri)
    {
        _redirectUri = redirectUri;
        _authenticationBrowser = new WebBrowser
        {
            Dock = DockStyle.Fill,
            Location = new System.Drawing.Point(0, 0),
            MinimumSize = new System.Drawing.Size(20, 20),
            Name = "authenticationBrowser",
            ScriptErrorsSuppressed = true,
            Size = new System.Drawing.Size(641, 353),
            TabIndex = 0
        };
        _authenticationBrowser.DocumentCompleted += authenticationBrowser_DocumentCompleted;
            _authenticationBrowser.Navigate(authUri);
        Controls.Add(_authenticationBrowser);
        var token = await _tokenCompletitionSource.Task;
        return token;
    }
    private void authenticationBrowser_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        if (!(_redirectUri.IsBaseOf(e.Url) && _redirectUri.AbsolutePath.Equals(e.Url.AbsolutePath))) return;
            _authenticationBrowser.Dispose();
        //GC.WaitForPendingFinalizers();
        //GC.Collect();
        var token = e.Url.ToString().Split('=')[1].Split('&')[0];
        _tokenCompletitionSource.SetResult(token);
    }
}

这是完整的项目代码

释放 Web 浏览器控件会导致在系统浏览器中打开新页面

我在将 box.com api OAuth 集成到我的 WPF 应用程序中时也遇到了类似的问题。但是 box.com API 提供的 WPF 示例可以正常工作。

即使我更改了官方示例的逻辑和流程以与我的 WPF 应用程序匹配,我也无法重现该问题。

最后,我

通过在导航事件中添加"about:blank"导航来解决此问题,如下所示,我正在共享代码,以便它可以帮助某人。

private void browserAuthentication_Navigating(object sender, NavigatingCancelEventArgs e)
{
    if(e.Uri.Host.Equals( _callbackUri.Host))
    {
        e.Cancel = true;
        //This code is required. Otherwise the box api navigation will popup an external browser window.
        browserAuthentication.Navigate("about:blank");
        this._authenticationResponseValues = GetQueryOptions(e.Uri);
    }
}
private IDictionary<string, string> GetQueryStringParamAndValues(Uri resultUri)
{
    string[] queryParams = null;
    var queryValues = new Dictionary<string, string>();
    int fragmentIndex = resultUri.AbsoluteUri.IndexOf("#", StringComparison.Ordinal);
    if (fragmentIndex > 0 && fragmentIndex < resultUri.AbsoluteUri.Length + 1)
    {
        queryParams = resultUri.AbsoluteUri.Substring(fragmentIndex + 1).Split('&');
    }
    else if (fragmentIndex < 0)
    {
        if (!string.IsNullOrEmpty(resultUri.Query))
        {
            queryParams = resultUri.Query.TrimStart('?').Split('&');
        }
    }
    if (queryParams != null)
    {
        foreach (var param in queryParams)
        {
            if (!string.IsNullOrEmpty(param))
            {
                string[] kvp = param.Split('=');
                queryValues.Add(kvp[0], System.Net.WebUtility.UrlDecode(kvp[1]));
            }
        }
    }
    return queryValues;
}