c# - 带 IDownloadManager 的 Windows 窗体 Webbrowswer 控件

标签 c# winforms webbrowser-control download-manager

我正在使用 Systems.Windows.Forms.Webbrowser 控件并且需要覆盖下载管理器。我已按照说明进行操作 here子类化表单并覆盖 CreateWebBrowserSiteBase()

/// <summary>
/// Browser with download manager
/// </summary>
public class MyBrowser : WebBrowser  
{
    /// <summary>
    /// Returns a reference to the unmanaged WebBrowser ActiveX control site,
    /// which you can extend to customize the managed <see ref="T:System.Windows.Forms.WebBrowser"/> control.
    /// </summary>
    /// <returns>
    /// A <see cref="T:System.Windows.Forms.WebBrowser.WebBrowserSite"/> that represents the WebBrowser ActiveX control site.
    /// </returns>
    protected override WebBrowserSiteBase CreateWebBrowserSiteBase()
    {
        var manager = new DownloadWebBrowserSite(this);
        manager.FileDownloading += (sender, args) =>
            {
                if (FileDownloading != null)
                {
                    FileDownloading(this, args);
                }
            };
        return manager;
    }
}

DownloadWebBrowserSite 中,我实现了 IServiceProvider 以在请求时提供 IDownloadManager

        /// <summary>
        /// Queries for a service
        /// </summary>
        /// <param name="guidService">the service GUID</param>
        /// <param name="riid"></param>
        /// <param name="ppvObject"></param>
        /// <returns></returns>
        public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject)
        {
            if ( (guidService == Constants.IID_IDownloadManager && riid == Constants.IID_IDownloadManager ))
            {
                ppvObject = Marshal.GetComInterfaceForObject(_manager, typeof(IDownloadManager));
                return Constants.S_OK;
            }
            ppvObject = IntPtr.Zero;
            return Constants.E_NOINTERFACE;
        }

DownloadManager 取自上面的示例。

/// <summary>
/// Intercepts downloads of files, to add as PDFs or suppliments
/// </summary>
[ComVisible(true)]
[Guid("bdb9c34c-d0ca-448e-b497-8de62e709744")]
[CLSCompliant(false)]
public class DownloadManager : IDownloadManager
{
    /// <summary>
    /// event called when the browser is about to download a file
    /// </summary>
    public event EventHandler<FileDownloadEventArgs> FileDownloading;

    /// <summary>
    /// Return S_OK (0) so that IE will stop to download the file itself. 
    /// Else the default download user interface is used.
    /// </summary>
    public int Download(IMoniker pmk, IBindCtx pbc, uint dwBindVerb, int grfBINDF, IntPtr pBindInfo,
                        string pszHeaders, string pszRedir, uint uiCP)
    {
        // Get the display name of the pointer to an IMoniker interface that specifies the object to be downloaded.
        string name;
        pmk.GetDisplayName(pbc, null, out name);
        if (!string.IsNullOrEmpty(name))
        {
            Uri url;
            if (Uri.TryCreate(name, UriKind.Absolute, out url))
            {
                if ( FileDownloading != null )
                {
                     FileDownloading(this, new FileDownloadEventArgs(url));
                }
                return Constants.S_OK;
            }
        }
        return 1;
    }
}

问题是 pmk.GetDisplayName 返回初始 URL,而不是要下载的项目的 URL。如果 URI 指向动态页面,例如 http://www.example.com/download.php ,我没有得到要下载的实际文件。我需要从 header 中获取 URL,以便获取我应该下载的实际文件。

Google 指示我必须创建一个 IBindStatusCallback 实现,该实现还实现了 IHttpNegotiateIServiceProvider 以响应 IID_IHttpNegotiate,这样我就可以看到 IHttpNegotiate.OnResponse。我已经设法实现了这一点,但是,QueryService 似乎只要求 IID_IInternetProtocol 而从不要求 IID_IHttpNegotiate

任何建议都会很棒。

最佳答案

您错过了对:CreateBindCtx 的调用。

将以下内容添加到您的下载管理器:

   [DllImport("ole32.dll")]
        static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);

然后确保在注册回调之前调用它。我已经在您的 Download() 方法中实现了它,如下所示:

public int Download(IMoniker pmk, IBindCtx pbc, uint dwBindVerb, int grfBINDF, IntPtr pBindInfo,
                            string pszHeaders, string pszRedir, uint uiCP)
        {
            // Get the display name of the pointer to an IMoniker interface that specifies the object to be downloaded.
            string name;
            pmk.GetDisplayName(pbc, null, out name);
            if (!string.IsNullOrEmpty(name))
            {
                Uri url;
                if (Uri.TryCreate(name, UriKind.Absolute, out url))
                {
                    Debug.WriteLine("DownloadManager: initial URL is: " + url);
                    CreateBindCtx(0, out pbc);
                    RegisterCallback(pbc, url);
                    BindMonikerToStream(pmk, pbc);

                    return MyBrowser.Constants.S_OK;
                }
            }
            return 1;
        }

有了这个,您的 IHttpNegotiate 实现将被调用,您将可以访问响应 header 。

关于c# - 带 IDownloadManager 的 Windows 窗体 Webbrowswer 控件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13362922/

相关文章:

c# - 在单个线程上启动第二个消息循环不是有效操作。改为使用 Form.ShowDialog

mysql - 机器人筛选网站的每个页面

javascript - 使用 dom 访问调用 javascript 文件

c# - 当 C# 没有任何 ID 和名称时,如何单击 Web 表单上的元素(超链接)

c# - 在用户输入时加快记录过滤

c# - 密码框不会屏蔽我的密码 WPF

c# - 有 Form.Invoke() 方法吗?

c# - 运行时服务不再注入(inject) DNX 控制台应用程序 (RC1)

c# - Duplicate Key 错误,但在使用调试点运行时不会重新创建

c# - 应该如何将数组从 C# 传递给 Javascript 函数?