Windows窗体Web浏览器控件与IDownloadManager

11

我正在使用 Systems.Windows.Forms.Webbrowser 控件,需要覆盖下载管理器。我已经按照 这里 的说明子类化了表单并覆盖了 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,以便获取我应该下载的实际文件。
谷歌表明我必须创建一个实现IBindStatusCallback,同时还实现IHttpNegotiateIServiceProvider以响应IID_IHttpNegotiate,以便我可以看到IHttpNegotiate.OnResponse。我已经成功实现了这一点,但是QueryService似乎只会请求IID_IInternetProtocol而永远不会请求IID_IHttpNegotiate
任何建议都将非常有用。

能否将一个可编译的示例放在某个地方,以便我们进行测试? - Simon Mourier
@SimonMourier 我会在接下来的一两天内着手创建示例。 - jimbojones
@Anthill 是的,应该是 IBindStatusCallback。我已经编辑了问题。我已经实现了 msdn.microsoft.com/en-us/library/ms775054(v=vs.85).aspx 建议的内容。问题在于 IBindStatusCallbackQueryService 从未调用 IID_IHttpNegotiate(以便我可以返回 IHttpNegotiate 实现)。它总是调用 IID_IInternetProtocol。当 QueryService 要求 IID_IInternetProtocol 时,我不知道该返回什么。 - jimbojones
@SimonMourier,我已经添加了一个Dropbox链接,其中包含一个演示样例应用程序和解决方案中的README.txt。 - jimbojones
你需要URL做什么?(即,你的最终目标是什么?)http://www.sciencedirect.com/science 是从该页面下载文件的正确URL。服务器会对您POST到该页面的值做出反应。您需要下载文件吗? - JoshVarty
显示剩余6条评论
1个回答

5
您漏掉了调用CreateBindCtx的部分。
请在DownloadManager中添加以下内容:
   [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实现将被调用,你将能够访问响应头。


很高兴能够帮忙!它是否完成了你所希望的一切?并且是否满足悬赏的所有条件? - Sam Shiles
是的,没错。我以为悬赏会自动授予。非常抱歉。我现在意识到我还必须以类似的方式获取HTTP响应内容,但我认为这是一个单独的问题 :) - jimbojones
我下载了一个文档,但它对我不起作用。事件没有响应。我运行了http://dl.dropbox.com/u/58016866/Working_WebBrowserIBindDemo.zip上的示例。我的电脑上安装了IE 10,并且是64位的。 - kyleb
嗨,Sam,下载链接已经失效了,我将其移除以防止未来的 NAA(Not An Answer),也许你想添加一个新的链接。 - bummi
哦,如果这个例子仍然能够正常工作,那该多好啊。 - TEK

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接