带有 IDownloadManager 的 Windows 窗体 Webbrowswer 控件

Posted

技术标签:

【中文标题】带有 IDownloadManager 的 Windows 窗体 Webbrowswer 控件【英文标题】:Windows Forms Webbrowswer control with IDownloadManager 【发布时间】:2012-11-02 00:15:10 【问题描述】:

我正在使用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,我就没有得到要下载的实际文件。我需要从标头中获取 URL,以便获取我应该下载的实际文件。

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

任何建议都会很棒。

【问题讨论】:

嗨,您提到实现 IBindingStatusCallback - 据我所知,该接口称为 IBindStatusCallback - 这是一个错字吗?您还没有展示 IBindStatusCallback 的实现。 MSDN 提到 Urlmon.dll 将使用 IBindStatusCallback 的 QueryInterface 实现来获取指向您的 IHttpNegotiate 接口的指针。看看msdn.microsoft.com/en-us/library/ms775054(v=vs.85).aspx 是否可以将可编译的示例放在某个地方以便我们进行测试? @SimonMourier 我会在接下来的一两天内研究创建示例。 @Anthill 是的,应该是IBindStatusCallback。我已经编辑了这个问题。我已经实现了 msdn.microsoft.com/en-us/library/ms775054(v=vs.85).aspx 的建议。问题是IBindStatusCallbackQueryService 永远不会用IID_IHttpNegotiate 调用(这样我就可以返回IHttpNegotiate 实现)。它总是用IID_IInternetProtocol 调用。当QueryService 要求IID_IInternetProtocol 时,我不知道该返回什么。 @SimonMourier 我为演示示例应用程序添加了一个保管箱链接,解决方案中包含 README.txt。 【参考方案1】:

您错过了对 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 实现将被调用,您将可以访问响应标头。

【讨论】:

很高兴能帮上忙!它会做你希望它做的一切吗?它是否满足赏金的所有条件? 是的,确实如此。我以为赏金是自动授予的。对不起。我现在意识到我也必须以类似的方式获取 HTTP 响应内容,但我认为这是一个单独的问题 :) 我下载了一个文档,但它不适合我。事件没有发生。我从dl.dropbox.com/u/58016866/Working_WebBrowserIBindDemo.zip 运行了这个例子。我的盒子上有 IE 10,我的盒子是 64 位的。 嗨,Sam,下载链接已损坏,我会删除它以防止未来的 NAA,也许您想添加一个新的。 哦,梦想这个例子还在正常工作中。

以上是关于带有 IDownloadManager 的 Windows 窗体 Webbrowswer 控件的主要内容,如果未能解决你的问题,请参考以下文章

带有win32con的Python滚轮鼠标

带有子控件的 Win32 自定义控件

带有多个菜单栏的 Win32 中的 Windows 应用程序?

硬链接到带有 Win32 API 的符号链接?

在Win7系统下, 使用VS2015 打开带有日文注释程序出现乱码的解决方案

Cx_freeze 不适用于带有 Python 3.6.2 的 Win 10