使用IDownloadManager控制Windows窗体Webbrowswer
我正在使用Systems.Windows.Forms.Webbrowser
控件,需要覆盖下载管理器。 我按照这里的说明CreateWebBrowserSiteBase()
表单并覆盖CreateWebBrowserSiteBase()
/// /// Browser with download manager /// public class MyBrowser : WebBrowser { /// /// Returns a reference to the unmanaged WebBrowser ActiveX control site, /// which you can extend to customize the managed control. /// /// /// A that represents the WebBrowser ActiveX control site. /// protected override WebBrowserSiteBase CreateWebBrowserSiteBase() { var manager = new DownloadWebBrowserSite(this); manager.FileDownloading += (sender, args) => { if (FileDownloading != null) { FileDownloading(this, args); } }; return manager; } }
在DownloadWebBrowserSite
,我实现IServiceProvider
以在请求时提供IDownloadManager
。
/// /// Queries for a service /// /// the service GUID /// /// /// 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
取自上面的示例。
/// /// Intercepts downloads of files, to add as PDFs or suppliments /// [ComVisible(true)] [Guid("bdb9c34c-d0ca-448e-b497-8de62e709744")] [CLSCompliant(false)] public class DownloadManager : IDownloadManager { /// /// event called when the browser is about to download a file /// public event EventHandler FileDownloading; /// /// Return S_OK (0) so that IE will stop to download the file itself. /// Else the default download user interface is used. /// 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
实现,它也实现IHttpNegotiate
和IServiceProvider
来响应IID_IHttpNegotiate
,这样我就可以看到IHttpNegotiate.OnResponse
。 我已经成功实现了这一点,但是, QueryService
似乎只是要求IID_IInternetProtocol
而不是IID_IHttpNegotiate
。
任何建议都会很棒。
您错过了对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实现将被调用,您将可以访问响应头。