【问题标题】:disposing WebBrowser control causes opening new page in system browser处理 WebBrowser 控件会导致在系统浏览器中打开新页面
【发布时间】:2016-01-31 13:50:20
【问题描述】:

问题是我的previous question 的延续。问题的简短解释:我尝试在 vk.com 上使用类似 OAuth2 的方法在 WinForms 应用程序中使用 WebBrowser 授权用户,因此我需要在 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);
    }
}

here is full project code

【问题讨论】:

  • 您确定可以在不同的 HTTP 会话上使用相同的 {accessToken} 令牌吗?服务器对此进行控制,而不是客户端(除非您设法以 1:1 复制会话身份,这不仅仅是复制 cookie)。您需要使用 Fiddler 研究会话流量。
  • @Noseratio,在 vk.com api 文档中的某处告诉令牌未绑定到 http 会话,它只绑定到 ip。
  • 我不认为这只是 IP 地址。考虑一个在单个真实 IP 后面进行 NAT 的典型网络。无论如何,不要在自己的事件处理程序中处理 _authenticationBrowser。使用form.BeginInvoke 推迟处理。
  • @Noseratio ALong with access_token, the lifetime of the key will be specified expires_in expressed in seconds. Should the key expire it will be necessary to repeat the steps above, but the user won't have to confirm access rights again. Please note that the given acces_token is linked to the three digits of the IP address of the client, therefore you have to get a new key if the address is changed. vk API docs.. 谢谢,我稍后再试,但我认为,它不会解决主要问题。
  • 如果您不使用DisposeWebBrowser,而是将其隐藏在您的主窗体中,会发生什么情况?它会给你想要的行为吗?如果不是,期望的行为是什么?

标签: c# wpf winforms wpf-controls webbrowser-control


【解决方案1】:

我也遇到了将 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;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-08-04
    • 2014-09-04
    相关资源
    最近更新 更多