Microsoft KB Archive/325204

= BUG: FileDownload event handler in .NET WebBrowser host is never called =

Article ID: 325204

Article Last Modified on 7/24/2007

-

APPLIES TO


 * Microsoft Internet Explorer (Programming)
 * Microsoft Visual C# .NET 2003 Standard Edition
 * Microsoft Visual C# .NET 2002 Standard Edition
 * Microsoft .NET Framework 1.1
 * Microsoft .NET Framework 1.0

-



This article was previously published under Q325204





SYMPTOMS
If you sink the FileDownload event of the DWebBrowserEvents2 source interface for the WebBrowser control that is hosted on a managed application in Visual C# .NET, the event handler is not called.



CAUSE
According to the documentation, the FileDownload event fires with a single parameter: the VARIANT_BOOL argument Cancel. However, this event fires with another BOOL argument in addition to the Cancel VARIANT_BOOL argument. This BOOL argument indicates whether an Active Document server is loading in memory. However, because the type library has only one parameter listed, any development tool that generates event handlers that are based on the type library cannot properly sink the event.



WORKAROUND
To work around the problem:
 * 1) Subclass the AxHost class in System.Windows.Forms instead of importing the WebBrowser control.
 * 2) Write all the supporting code to host the WebBrowser control and sink its events.
 * 3) Add an event handler for FileDownload that uses the actual signature instead of the signature that is defined in intermediate definition language (IDL) and the documentation. The actual signature for the FileDownload event must have two arguments.

Step-by-step workaround
  Create a new class named MyWebBrowser that hosts the WebBrowser control and raises the FileDownload event. The MyWebBrowser class derives from AxHost and implements IMyWebBrowserEvents: public class MyWebBrowser : AxHost, IMyWebBrowserEvents {   ... }   Define the constructor of MyWebBrowser: public MyWebBrowser : base(&quot;8856f961-340a-11d0-a96b-00c04fd705a2&quot;) { } Notice that the constructor for MyWebBrowser calls the base class constructor. The base class is specified by its GUID. The specified GUID is of the WebBrowser co-class.   Define the IMyWebBrowser interface with the same methods as the IWebBrowser interface: [Guid(&quot;eab22ac1-30c1-11cf-a7eb-0000c05bae0b&quot;)] interface IMyWebBrowser {    void GoBack; void GoForward; void GoHome; void GoSearch; void Navigate(string url, ref object flags, ref object targetFrame, ref        object postData, ref object headers); void Refresh; void Refresh2; void Stop; void GetApplication; void GetParent; void GetContainer; } Notice that the GUID as specified by the Guid attribute is the interface identifier (IID) of the IWebBrowser interface. The complete method signature of only the method that is used in the MyWebBrowser class is specified. For example, the Navigate method is specified completely.   Define the IMyWebBrowserEvents interface: [Guid(&quot;34A715A0-6587-11D0-924A-0020AFC7AC4D&quot;), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] public interface IMyWebBrowserEvents {    [DispId(270)] void RaiseFileDownload(bool Load,ref bool Cancel); } Notice that the GUID as specified by the Guid attribute is the IID of DWebBrowserEvents2 dispinterface. Additionally, the DispId attribute that is applied to the RaiseFileDownload method is the same as the dispatch identifier (DISPID) of the FileDownload event in the DWebBrowserEvent2 source interface. The signature of the RaiseFileDownload event has two arguments instead of the one argument that is visible for the FileDownload event.   Provide an event and a delegate for clients of the MyWebBrowser class to receive the FileDownload event from the WebBrowser ActiveX control: public class MyWebBrowser : AxHost, IMyWebBrowserEvents {        ...         public event FileDownloadEventHandler FileDownload; ...   }    ...    public delegate void FileDownloadEventHandler(object sender, FileDownloadEventArgs e); public class FileDownloadEventArgs {       public FileDownloadEventArgs(bool load, bool cancel) {           this.cancel = cancel; this.load = load; }

protected bool cancel; protected bool load;

public bool Load {           get{return load;} }

public bool Cancel {           get{return cancel;} set{cancel = value;} }   }   Implement the RaiseFileDownload method to raise the event that is defined in step 5: public class MyWebBrowser : AxHost, IMyWebBrowserEvents {       ...        public void RaiseFileDownload(bool load,ref bool cancel) {           FileDownloadEventArgs e = new FileDownloadEventArgs(load,cancel); if(FileDownload != null) FileDownload(this, e); cancel = e.Cancel; }       ... } </li>  Override the AttachInterfaces method to get the reference of the WebBrowser ActiveX control being hosted: public class MyWebBrowser : AxHost, IMyWebBrowserEvents {

private IMyWebBrowser control; ...       protected override void AttachInterfaces {           try { control = (IMyWebBrowser) GetOcx; } catch { } }       ... } </li>  Override CreateSink and DetachSink to attach a sink and detach a sink from the connectable object (WebBrowser instance), respectively: public class MyWebBrowser : AxHost, IMyWebBrowserEvents {       ...        private ConnectionPointCookie cookie; ...       protected override void CreateSink {           try {               cookie = new ConnectionPointCookie(control, this, typeof(IMyWebBrowserEvents)); }            catch { } }

protected override void DetachSink {           try {               cookie.Disconnect; }            catch { } }       ... } </li>  Implement a simple property that invokes the Navigate method of the WebBrowser ActiveX component that is being hosted: public class MyWebBrowser : AxHost, IMyWebBrowserEvents {      ...        protected string url; ...       public string Url {           get { return url; } set {               url = value; object o = null; control.Navigate(url, ref o, ref o, ref o, ref o); }       }        ... } </li></ol>

With the MyWebBrowser class, you receive the FileDownload event.

Complete code listing
For your reference, the following is the complete code listing for the steps. To receive the FileDownload event, replace the existing code in Form1.cs for a Visual C# .NET Windows Application project with this code: using System; using System.Windows.Forms; using System.Runtime.InteropServices; using System.Data; using System.IO;

namespace HostingActiveX {

public class Form1 : System.Windows.Forms.Form {

private System.ComponentModel.Container components = null; private MyWebBrowser webBrowser;

public Form1 {

InitializeComponent;

webBrowser = new MyWebBrowser;

webBrowser.Dock = DockStyle.Fill;

webBrowser.FileDownload += new FileDownloadEventHandler(OnFileDownload);

Controls.Add(webBrowser); }

protected void OnFileDownload(object sender, FileDownloadEventArgs e)       { MessageBox.Show(&quot;User is trying to download file&quot;); }

protected override void Dispose( bool disposing ) {           if( disposing ) {               if (components != null) {                   components.Dispose; }           }            base.Dispose( disposing ); }

private void InitializeComponent {

this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(292, 266); this.Name = &quot;Form1&quot;; this.Text = &quot;Form1&quot;; this.Load += new System.EventHandler(this.Form1_Load);

}

[STAThread] static void Main {           Application.Run(new Form1); }

private void Form1_Load(object sender, System.EventArgs e)       { webBrowser.Url = &quot;http://www.microsoft.com&quot;; }   }

public class MyWebBrowser : AxHost, IMyWebBrowserEvents {

private IMyWebBrowser control; private ConnectionPointCookie cookie;

protected string url; public event FileDownloadEventHandler FileDownload;

public MyWebBrowser : base(&quot;8856f961-340a-11d0-a96b-00c04fd705a2&quot;) {       }

public void RaiseFileDownload(bool load, ref bool cancel) {           FileDownloadEventArgs e = new FileDownloadEventArgs(load, cancel); if(FileDownload != null) FileDownload(this, e); cancel = e.Cancel; }

protected override void CreateSink {           try {               cookie = new ConnectionPointCookie(control, this,                    typeof(IMyWebBrowserEvents)); } catch { } }

protected override void DetachSink {           try {               cookie.Disconnect; }            catch { } }

protected override void AttachInterfaces {           try { control = (IMyWebBrowser) GetOcx; } catch { } }

public string Url {           get { return url; } set {               url = value; object o = null; control.Navigate(url, ref o, ref o, ref o, ref o); }       }    }

[Guid(&quot;34A715A0-6587-11D0-924A-0020AFC7AC4D&quot;), InterfaceType(ComInterfaceType.InterfaceIsIDispatch)] public interface IMyWebBrowserEvents {       [DispId(270)] void RaiseFileDownload(bool Load,ref bool Cancel); }

public delegate void FileDownloadEventHandler(object sender, FileDownloadEventArgs e); public class FileDownloadEventArgs {       public FileDownloadEventArgs(bool load,bool cancel) {           this.cancel = cancel; this.load = load; }

protected bool cancel; protected bool load;

public bool Load {           get{return load;} }

public bool Cancel {           get{return cancel;} set{cancel = value;} }   }

[Guid(&quot;eab22ac1-30c1-11cf-a7eb-0000c05bae0b&quot;)] interface IMyWebBrowser {       void GoBack; void GoForward; void GoHome; void GoSearch; void Navigate(string url, ref object flags, ref object targetFrame, ref           object postData, ref object headers); void Refresh; void Refresh2; void Stop; void GetApplication; void GetParent; void GetContainer; } }

<div class="status_section">

STATUS
Microsoft has confirmed that this is a bug in the Microsoft products that are listed in the &quot;Applies to&quot; section.

<div class="moreinformation_section">

Steps to reproduce the behavior
<ol> In Visual Studio .NET, create a new Visual C# .NET Windows Application project.</li> Right-click the Toolbox window, and then clickAdd/Remove Items.</li> On the COM Components tab, click to select the Microsoft Web Browser check box.</li> Drag the Explorer control from the toolbox to the Visual C# .NET form.</li>  Double-click the form to open the code window for the Form1_Load method. Add the following code for the Form1_Load method (by default, the name of the WebBrowser control is axWebBrowser1): private void Form1_Load(object sender, System.EventArgs e) { object url = &quot;http://www.microsoft.com&quot;; object empty = &quot;&quot;; axWebBrowser1.Navigate2(ref url, ref empty, ref empty, ref empty, ref empty); } </li> In the Properties window for axWebBrowser1, click Events. Click the FileDownload event to create an event handler.</li> In the box to the right of the event name, type TestFileDownload as the name of the handler, and then press ENTER. The event handler method is generated and displayed in the code editor.</li>  Add the following code to the TestFileDownload event handler method: private void TestFileDownload(object sender, AxSHDocVw.DWebBrowserEvents2_FileDownloadEvent e)       { MessageBox.Show (&quot;FileDownload event fired!&quot;); } </li> On the Debug menu, click Start to run the code.</li></ol>

Notice that the WebBrowser control opens the Microsoft Web site, but the FileDownload event is not fired.

<div class="references_section">