I'm using the Systems.Windows.Forms.Webbrowser
control and need to override the download manager. I've followed the instructions here to subclass the form and override 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;
}
}
In the DownloadWebBrowserSite
, I implement IServiceProvider
to provide an IDownloadManager
when requested.
/// <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;
}
The DownloadManager
is taken from the example above.
/// <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;
}
}
The problem is that the pmk.GetDisplayName
returns the initial URL, not the URL of the item to be downloaded. If the URI points to a dynamic page, such as http://www.example.com/download.php, I'm not getting the actual file to be downloaded. I need to get the URL from the header so that I get the actual file I'm supposed to be downloading.
Google indicates that I have to create a IBindStatusCallback
implementation that also implements IHttpNegotiate
and IServiceProvider
to respond to IID_IHttpNegotiate
, so that I can see the IHttpNegotiate.OnResponse
. I've managed to implement that, however, QueryService
only seems to ever ask for IID_IInternetProtocol
and never IID_IHttpNegotiate
.
Any advice would be great.
Your missing a call to: CreateBindCtx.
Add the following to your DownloadManager:
[DllImport("ole32.dll")]
static extern int CreateBindCtx(uint reserved, out IBindCtx ppbc);
Then make sure you call this before registering your callback. I have implemented this in your Download() method as follows:
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;
}
With this in place, your IHttpNegotiate implementation will be called and you'll have access to the response headers.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With