At first, I enjoyed the new webbrowser control but then I noticed that the args provided by the events NewWindow3 and BeforeNavigate of the axWebBrowser had not been implemented in the new control. Arg.
Good news, the guys from Microsoft gave us the a workaround recently in C# and said they will publish a sample soon on MSDN.
In the mean time, here is some VB.net code inspired from Microsoft's C# that will give you 2 new events and the args people like me were crying for.
NavigatingExtended gives you the following args: - Url as string - Frame as string - Headers as string - Postdata as string - PostdataByte() as byte - Cancel as boolean (to cancel navigation)
NewWindowExtended gives you the following args: - Url as string - UrlContext as string - Flags as NWMF (see NewWindow3 on MSDN for definition) - Cancel as boolean (to cancel navigation to new window)
Ok let's go. 1. Create a new Windows form project. 2. Add a new Module and Copy/Paste the following:
| |
Imports System Imports System.Collections.Generic Imports System.Text Imports System.ComponentModel Imports System.Runtime.InteropServices
Module Module1
Public Enum NWMF NWMF_UNLOADING = &H1& NWMF_USERINITED = &H2& NWMF_FIRST_USERINITED = &H4& NWMF_OVERRIDEKEY = &H8& NWMF_SHOWHELP = &H10& NWMF_HTMLDIALOG = &H20& NWMF_FROMPROXY = &H40& End Enum
'First define a new EventArgs class to contain the newly exposed data Public Class WebBrowserNavigatingExtendedEventArgs Inherits CancelEventArgs
Private m_Url As String Private m_Frame As String Private m_Postdata() As Byte Private m_Headers As String
Public ReadOnly Property Url() As String Get Return m_Url End Get End Property
Public ReadOnly Property Frame() As String Get Return m_Frame End Get End Property
Public ReadOnly Property Headers() As String Get Return m_Headers End Get End Property
Public ReadOnly Property Postdata() As String Get Return PostdataToString(m_Postdata) End Get End Property
Public ReadOnly Property PostdataByte() As Byte() Get Return m_Postdata End Get End Property
Public Sub New(ByVal url As String, ByVal frame As String, ByVal postdata As Byte(), ByVal headers As String) m_Url = url m_Frame = frame m_Postdata = postdata m_Headers = headers End Sub
Private Function PostdataToString(ByVal p() As Byte) As String 'not sexy but it works... Dim tabpd() As Byte, bstop As Boolean = False, stmp As String = "", i As Integer = 0 tabpd = p If tabpd Is Nothing OrElse tabpd.Length = 0 Then Return "" Else For i = 0 To tabpd.Length - 1 stmp += ChrW(tabpd(i)) Next stmp = Replace(stmp, ChrW(13), "") stmp = Replace(stmp, ChrW(10), "") stmp = Replace(stmp, ChrW(0), "") End If If stmp = Nothing Then Return "" Else Return stmp End If End Function
End Class
Public Class WebBrowserNewWindowExtendedEventArgs Inherits CancelEventArgs
Private m_Url As String Private m_UrlContext As String Private m_Flags As NWMF
Public ReadOnly Property Url() As String Get Return m_Url End Get End Property
Public ReadOnly Property UrlContext() As String Get Return m_UrlContext End Get End Property
Public ReadOnly Property Flags() As NWMF Get Return m_Flags End Get End Property
Public Sub New(ByVal url As String, ByVal urlcontext As String, ByVal flags As NWMF) m_Url = url m_UrlContext = urlcontext m_Flags = flags End Sub
End Class
Public Class ExtendedWebBrowser Inherits WebBrowser
Private cookie As AxHost.ConnectionPointCookie Private wevents As WebBrowserExtendedEvents
'This method will be called to give you a chance to create your own event sink Protected Overrides Sub CreateSink() 'MAKE SURE TO CALL THE BASE or the normal events won't fire MyBase.CreateSink() wevents = New WebBrowserExtendedEvents(Me) cookie = New AxHost.ConnectionPointCookie(Me.ActiveXInstance, wevents, GetType(DWebBrowserEvents2)) End Sub
Protected Overrides Sub DetachSink() If Not cookie Is Nothing Then cookie.Disconnect() cookie = Nothing End If MyBase.DetachSink() End Sub
'This new event will fire when the page is navigating Public Delegate Sub WebBrowserNavigatingExtendedEventHandler(ByVal sender As Object, ByVal e As WebBrowserNavigatingExtendedEventArgs) Public Event NavigatingExtended As WebBrowserNavigatingExtendedEventHandler
'This event will fire when a new window is about to be opened Public Delegate Sub WebBrowserNewWindowExtendedEventHandler(ByVal sender As Object, ByVal e As WebBrowserNewWindowExtendedEventArgs) Public Event NewWindowExtended As WebBrowserNewWindowExtendedEventHandler
Protected Friend Sub OnNavigatingExtended(ByVal Url As String, ByVal Frame As String, ByVal Postdata As Byte(), ByVal Headers As String, ByRef Cancel As Boolean) Dim e As WebBrowserNavigatingExtendedEventArgs = New WebBrowserNavigatingExtendedEventArgs(Url, Frame, Postdata, Headers) RaiseEvent NavigatingExtended(Me, e) Cancel = e.Cancel End Sub
Protected Friend Sub OnNewWindowExtended(ByVal Url As String, ByRef Cancel As Boolean, ByVal Flags As NWMF, ByVal UrlContext As String) Dim e As WebBrowserNewWindowExtendedEventArgs = New WebBrowserNewWindowExtendedEventArgs(Url, UrlContext, Flags) RaiseEvent NewWindowExtended(Me, e) Cancel = e.Cancel End Sub
End Class
'This class will capture events from the WebBrowser Class WebBrowserExtendedEvents Inherits System.Runtime.InteropServices.StandardOleMarshalObject Implements DWebBrowserEvents2
Private m_Browser As ExtendedWebBrowser
Public Sub New(ByVal browser As ExtendedWebBrowser) m_Browser = browser End Sub
'Implement whichever events you wish Public Sub BeforeNavigate2(ByVal pDisp As Object, ByRef URL As String, ByRef flags As Object, ByRef targetFrameName As String, ByRef postData As Object, ByRef headers As String, ByRef cancel As Boolean) Implements DWebBrowserEvents2.BeforeNavigate2
m_Browser.OnNavigatingExtended(URL, targetFrameName, CType(postData, Byte()), headers, cancel)
End Sub
Public Sub NewWindow3(ByVal pDisp As Object, ByRef Cancel As Boolean, ByRef Flags As Object, ByRef UrlContext As String, ByRef Url As String) Implements DWebBrowserEvents2.NewWindow3
m_Browser.OnNewWindowExtended(Url, Cancel, CType(Flags, NWMF), UrlContext)
End Sub
End Class
<ComImport(), _ Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"), _ InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIDispatch), _ TypeLibType(TypeLibTypeFlags.FHidden)> _ Public Interface DWebBrowserEvents2
<DispId(250)> _ Sub BeforeNavigate2(<[In](), MarshalAs(UnmanagedType.IDispatch)> ByVal pDisp As Object, _ <InAttribute(), MarshalAs(UnmanagedType.BStr)> ByRef URL As String, _ <InAttribute()> ByRef flags As Object, _ <InAttribute(), MarshalAs(UnmanagedType.BStr)> ByRef targetFrameName As String, _ <InAttribute()> ByRef postdata As Object, _ <InAttribute(), MarshalAs(UnmanagedType.BStr)> ByRef headers As String, _ <InAttribute(), OutAttribute()> ByRef cancel As Boolean)
'Note: Postdata is a SafeArray but for some reason, if I do a proper declaration, the event will not be raised: '<[In](), MarshalAs(UnmanagedType.SafeArray, safearraysubtype:=VarEnum.VT_UI1)> ByRef postdata() As Byte, _
<DispId(273)> _ Sub NewWindow3(<InAttribute(), MarshalAs(UnmanagedType.IDispatch)> ByVal pDisp As Object, _ <InAttribute(), OutAttribute()> ByRef cancel As Boolean, _ <InAttribute()> ByRef Flags As Object, _ <InAttribute(), MarshalAs(UnmanagedType.BStr)> ByRef UrlContext As String, _ <InAttribute(), MarshalAs(UnmanagedType.BStr)> ByRef Url As String)
End Interface
End Module
| 3. Now, Open your form1 in Code view 4. Copy/Paste the following to test your control
| |
Public Class Form1 'Source from wilfridB
Private wb As New ExtendedWebBrowser
Private Sub Form1_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
AddHandler wb.NavigatingExtended, AddressOf wb_NavigatingExtended AddHandler wb.DocumentCompleted, AddressOf wb_DocumentCompleted AddHandler wb.NewWindowExtended, AddressOf wb_NewWindowExtended Me.Controls.Add(wb) wb.Dock = DockStyle.Fill wb.Navigate(New Uri("http://www.microsoft.com"))
End Sub
Private Sub wb_NavigatingExtended(ByVal sender As Object, ByVal e As module1.WebBrowserNavigatingExtendedEventArgs) 'This is a new event
Dim postdata As String = e.Postdata Dim msg As String = "Navigating to : " & e.Url & ControlChars.CrLf msg &= "Postdata : " & postdata & ControlChars.CrLf msg &= "Headers : " & e.Headers & ControlChars.CrLf msg &= "Frame : " & e.Frame & ControlChars.CrLf msg &= "Continue ?" Dim res As DialogResult = MessageBox.Show(msg, "NavigatingExtended", MessageBoxButtons.YesNo, MessageBoxIcon.Question) If res = Windows.Forms.DialogResult.No Then e.Cancel = True
End Sub
Private Sub wb_DocumentCompleted(ByVal sender As Object, ByVal e As System.Windows.Forms.WebBrowserDocumentCompletedEventArgs) 'This is a standard event
MessageBox.Show("Document complete: " & e.Url.ToString, "DocumentCompleted", MessageBoxButtons.OK, MessageBoxIcon.Information)
End Sub
Private Sub wb_NewWindowExtended(ByVal sender As Object, ByVal e As Module1.WebBrowserNewWindowExtendedEventArgs) 'This is a new event
Dim msg As String = "Navigation vers : " & e.Url & ControlChars.CrLf msg &= "UrlContext : " & e.UrlContext & ControlChars.CrLf msg &= "Flags : " & e.Flags.ToString & ControlChars.CrLf msg &= "Continue ?" Dim res As DialogResult = MessageBox.Show(msg, "NewWindowExtended", MessageBoxButtons.YesNo, MessageBoxIcon.Question) If res = Windows.Forms.DialogResult.No Then e.Cancel = True
End Sub
End Class
|
That's it and it should work... Any comment or additional event implementation is welcome... And if somebody could give me the solution for properly retrieving the postdata using something like: <[In](), MarshalAs(UnmanagedType.SafeArray, safearraysubtype:=VarEnum.VT_UI1)> ByRef postdata() As Byte, _
And converting an array of byte in a more decent way, that would be great. wilfridB | | WilfridB Friday, May 06, 2005 2:34 AM | As for the daclaration question: no idea so far But you could replace 'PostdataToString' with Public ReadOnly Property Postdata() As String Get 'Return PostdataToString(m_Postdata) Dim encode As System.Text.Encoding = System.Text.Encoding.UTF8 Return encode.GetString(m_Postdata) End Get End Property | | orbit3032263 Saturday, May 14, 2005 11:20 AM | Thanks for the reply, Orbit. Actually, I wonder if the best solution is not to leave it as an array of byte and in the calling code to add to the sub mybrowser_NavigatingExtended something like | | 'do not forget to check if objects exist Dim CurrentDoc as IHTMLDocument2 = Ctype(mybrowser.Document, IHTMLDocument2) Dim PageEncoding as Encoding = Encoding.GetEncoding(CurrentDoc.defaultCharset) Dim PostDataString as String = System.Web.HttpUtility.UrlDecode(CType(e.postData, Byte()), PageEncoding)
|
then, you get the right encoding. ------------------ By the way, does anybody have an idea how I could add the "Application" property to the WebbrowserExtended Class (similar to the one from axWebBrowser) so I can set ppDisp in NewWindow3 and redirect the new page to a custom browser ?wilfridB | | WilfridB Monday, May 16, 2005 9:13 PM | "Good news, the guys from Microsoft gave us the a workaround recently in C# and said they will publish a sample soon on MSDN."
Is this work-around available to share ?
thanks, Bill | | dotScience Thursday, June 09, 2005 7:40 PM | | | WilfridB Thursday, June 09, 2005 7:46 PM | | WilfridB wrote: |
By the way, does anybody have an idea how I could add the "Application" property to the WebbrowserExtended Class (similar to the one from axWebBrowser) so I can set ppDisp in NewWindow3 and redirect the new page to a custom browser ?
wilfridB |
|
Sorry, but I do not think this is possible. The reason for this is that the object "AxWebbrowser" (used internally by the webbrowser control) is not exposed, even not for inheritors. (It's declared "Private")Therefore the property is not available, since it's never called by any member of the class. I also would like to use this functionality, but it seems that this will only be possible when you write your own (complete) control  | | jlandheer Friday, November 04, 2005 9:46 PM | | WilfridB wrote: | Thanks for the reply, Orbit. Actually, I wonder if the best solution is not to leave it as an array of byte and in the calling code to add to the sub mybrowser_NavigatingExtended something like | |
'do not forget to check if objects exist Dim CurrentDoc as IHTMLDocument2 = Ctype(mybrowser.Document, IHTMLDocument2) Dim PageEncoding as Encoding = Encoding.GetEncoding(CurrentDoc.defaultCharset) Dim PostDataString as String = System.Web.HttpUtility.UrlDecode(CType(e.postData, Byte()), PageEncoding)
| then, you get the right encoding. ------------------
By the way, does anybody have an idea how I could add the "Application" property to the WebbrowserExtended Class (similar to the one from axWebBrowser) so I can set ppDisp in NewWindow3 and redirect the new page to a custom browser ?
wilfridB
|
|
Oooops, it seems that I missed one. Here goes: 1: Declare an interface "IWebBrowser2" 2: Override "AttachInterfaces" and set a private field to the value of the parameter "nativeActiveXObject" 3: Override "DetachInterfaces" to set this private field back to "null" 3: Use this private field's "Application" property That's all there's to it. An example in c#: | |
internal static class UnsafeNativeMethods { [System.Runtime.InteropServices.ComImport(), System.Runtime.InteropServices.Guid("34A715A0-6587-11D0-924A-0020AFC7AC4D"), System.Runtime.InteropServices.InterfaceTypeAttribute(System.Runtime.InteropServices.ComInterfaceType.InterfaceIsIDispatch), System.Runtime.InteropServices.TypeLibType(System.Runtime.InteropServices.TypeLibTypeFlags.FHidden)] public interface DWebBrowserEvents2 { [System.Runtime.InteropServices.DispId(250)] void BeforeNavigate2( [System.Runtime.InteropServices.In, System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.IDispatch)] object pDisp, [System.Runtime.InteropServices.In] ref object URL, [System.Runtime.InteropServices.In] ref object flags, [System.Runtime.InteropServices.In] ref object targetFrameName, [System.Runtime.InteropServices.In] ref object postData, [System.Runtime.InteropServices.In] ref object headers, [System.Runtime.InteropServices.In, System.Runtime.InteropServices.Out] ref bool cancel); [System.Runtime.InteropServices.DispId(273)] void NewWindow3( [System.Runtime.InteropServices.In, System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.IDispatch)] object pDisp, [System.Runtime.InteropServices.In, System.Runtime.InteropServices.Out] ref bool cancel, [System.Runtime.InteropServices.In] ref object flags, [System.Runtime.InteropServices.In] ref object URLContext, [System.Runtime.InteropServices.In] ref object URL); } [ComImport, SuppressUnmanagedCodeSecurity, TypeLibType(TypeLibTypeFlags.FOleAutomation | (TypeLibTypeFlags.FDual | TypeLibTypeFlags.FHidden)), Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E")] public interface IWebBrowser2 { [DispId(100)] void GoBack(); [DispId(0x65)] void GoForward(); [DispId(0x66)] void GoHome(); [DispId(0x67)] void GoSearch(); [DispId(0x68)] void Navigate([In] string Url, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers); [DispId(-550)] void Refresh(); [DispId(0x69)] void Refresh2([In] ref object level); [DispId(0x6a)] void Stop(); [DispId(200)] object Application { [return: MarshalAs(UnmanagedType.IDispatch)] get; } [DispId(0xc9)] object Parent { [return: MarshalAs(UnmanagedType.IDispatch)] get; } [DispId(0xca)] object Container { [return: MarshalAs(UnmanagedType.IDispatch)] get; } [DispId(0xcb)] object Document { [return: MarshalAs(UnmanagedType.IDispatch)] get; } [DispId(0xcc)] bool TopLevelContainer { get; } [DispId(0xcd)] string Type { get; } [DispId(0xce)] int Left { get; set; } [DispId(0xcf)] int Top { get; set; } [DispId(0xd0)] int Width { get; set; } [DispId(0xd1)] int Height { get; set; } [DispId(210)] string LocationName { get; } [DispId(0xd3)] string LocationURL { get; } [DispId(0xd4)] bool Busy { get; } [DispId(300)] void Quit(); [DispId(0x12d)] void ClientToWindow(out int pcx, out int pcy); [DispId(0x12e)] void PutProperty([In] string property, [In] object vtValue); [DispId(0x12f)] object GetProperty([In] string property); [DispId(0)] string Name { get; } [DispId(-515)] int HWND { get; } [DispId(400)] string FullName { get; } [DispId(0x191)] string Path { get; } [DispId(0x192)] bool Visible { get; set; } [DispId(0x193)] bool StatusBar { get; set; } [DispId(0x194)] string StatusText { get; set; } [DispId(0x195)] int ToolBar { get; set; } [DispId(0x196)] bool MenuBar { get; set; } [DispId(0x197)] bool FullScreen { get; set; } [DispId(500)] void Navigate2([In] ref object URL, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers); [DispId(0x1f5)] NativeMethods.OLECMDF QueryStatusWB([In] NativeMethods.OLECMDID cmdID); [DispId(0x1f6)] void ExecWB([In] NativeMethods.OLECMDID cmdID, [In] NativeMethods.OLECMDEXECOPT cmdexecopt, ref object pvaIn, IntPtr pvaOut); [DispId(0x1f7)] void ShowBrowserBar([In] ref object pvaClsid, [In] ref object pvarShow, [In] ref object pvarSize); [DispId(-525)] WebBrowserReadyState ReadyState { get; } [DispId(550)] bool Offline { get; set; } [DispId(0x227)] bool Silent { get; set; } [DispId(0x228)] bool RegisterAsBrowser { get; set; } [DispId(0x229)] bool RegisterAsDropTarget { get; set; } [DispId(0x22a)] bool TheaterMode { get; set; } [DispId(0x22b)] bool AddressBar { get; set; } [DispId(0x22c)] bool Resizable { get; set; } } }
and in your class:
private UnsafeNativeMethods.IWebBrowser2 axIWebBrowser2; protected override void AttachInterfaces(object nativeActiveXObject) { this.axIWebBrowser2 = (UnsafeNativeMethods.IWebBrowser2)nativeActiveXObject; base.AttachInterfaces(nativeActiveXObject); } protected override void DetachInterfaces() { this.axIWebBrowser2 = null; base.DetachInterfaces(); }
|
| | jlandheer Friday, November 04, 2005 10:20 PM | Hi jlandheer,
Could you please post the full ExtendedWebBrowser (with the worknig ppDisp / Application part) class or send it to me by email ( gjunge AT gmail DOT com), since I cannot get it to work. This would help me enormously.
Thank you in advance, Gidon | | Gidon J Thursday, November 10, 2005 11:37 AM | I'll send it over  I'm using it allready in one of my programs, there was one thing I needed to change on the interface declaration to get things going. Here is the modified part: | |
public interface DWebBrowserEvents2 { [System.Runtime.InteropServices.DispId(250)] void BeforeNavigate2( [System.Runtime.InteropServices.In, System.Runtime.InteropServices.MarshalAs(System.Runtime.InteropServices.UnmanagedType.IDispatch)] object pDisp, [System.Runtime.InteropServices.In] ref object URL, [System.Runtime.InteropServices.In] ref object flags, [System.Runtime.InteropServices.In] ref object targetFrameName, [System.Runtime.InteropServices.In] ref object postData, [System.Runtime.InteropServices.In] ref object headers, [System.Runtime.InteropServices.In, System.Runtime.InteropServices.Out] ref bool cancel); [System.Runtime.InteropServices.DispId(273)] void NewWindow3( [System.Runtime.InteropServices.In, System.Runtime.InteropServices.Out] ref object pDisp, [System.Runtime.InteropServices.In, System.Runtime.InteropServices.Out] ref bool cancel, [System.Runtime.InteropServices.In] ref object flags, [System.Runtime.InteropServices.In] ref object URLContext, [System.Runtime.InteropServices.In] ref object URL); }
|
| | jlandheer Sunday, November 13, 2005 5:48 PM | Can someone please send me the entire class/module/whatever Which includes the full ppDisp implementation?
Also is there a reason you chose to omit the NewWindow2 event? I need to use that in lieu of NewWindow3.
Any chance anyone has that? I'd prefer to use this over the AxWebBrowser (if I can)...
jshapcott AT MSN dot com | | FundamentalDiscord Saturday, November 26, 2005 5:27 AM | Never mind... I got it sorted out. | | FundamentalDiscord Saturday, November 26, 2005 8:34 AM | Nice that you figured this out. I've posted the complete source code on my weblog, you can download the file here: http://weblogs.servehttp.com/jeroen/files/extendedwebbrowser.zip
| | jlandheer Saturday, November 26, 2005 6:08 PM | OUTSTANDING! I'm not so good with translating C# (especially interop stuff) to VB, so this is great to have you spelling it out.
The plumbing was the hard part... now I have the entire interface built, just need to wire up some code.
So... dumb question: why hasn't anyone just wired up ALL of these events into one control? Why wouldn't MS have passed these events in the first place? So many of us spend our time wiring this stuff up...
Oh well, I feel like the doors are opening!
THANK YOU!!!
JR | | JR_runnfool Monday, January 30, 2006 11:05 PM | Do you have an example in VB to declare this interface. I am interested in accessing the Application property.
Thanks | | UseCode Thursday, February 09, 2006 9:36 PM | In VS 2003, I was able to use pDisp to maintain session in a new window. I assume that there is some way of doing this with the module above, but I haven't been able to figure it out. (I'm working with Visual Basic .Net 2005)
Thanks for the code, and for any assistance. | | CEisen Tuesday, February 21, 2006 4:04 PM | Hello CEisen.
Yes, there is. The way to do this is to override AttachInterfaces() and store the parameters in one of your own local class variables. (And use DetachInterfaces() to clear the memory used)
An example:
private UnsafeNativeMethods.IWebBrowser2 axIWebBrowser2;
/// <summary> /// This method supports the .NET Framework infrastructure and is not intended to be used directly from your code. /// Called by the control when the underlying ActiveX control is created. /// </summary> /// <param name="nativeActiveXObject"></param> [PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")] protected override void AttachInterfaces(object nativeActiveXObject) { this.axIWebBrowser2 = (UnsafeNativeMethods.IWebBrowser2)nativeActiveXObject; base.AttachInterfaces(nativeActiveXObject); }
/// <summary> /// This method supports the .NET Framework infrastructure and is not intended to be used directly from your code. /// Called by the control when the underlying ActiveX control is discarded. /// </summary> [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] protected override void DetachInterfaces() { this.axIWebBrowser2 = null; base.DetachInterfaces(); }
/// <summary> /// Returns the automation object for the web browser /// </summary> public object Application { get { return axIWebBrowser2.Application; } }
The IWebBrowser2 interface:
[ComImport, SuppressUnmanagedCodeSecurity, TypeLibType(TypeLibTypeFlags.FOleAutomation | (TypeLibTypeFlags.FDual | TypeLibTypeFlags.FHidden)), Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E")] public interface IWebBrowser2 { [DispId(100)] void GoBack(); [DispId(0x65)] void GoForward(); [DispId(0x66)] void GoHome(); [DispId(0x67)] void GoSearch(); [DispId(0x68)] void Navigate([In] string Url, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers); [DispId(-550)] void Refresh(); [DispId(0x69)] void Refresh2([In] ref object level); [DispId(0x6a)] void Stop(); [DispId(200)] object Application { [return: MarshalAs(UnmanagedType.IDispatch)] get; } [DispId(0xc9)] object Parent { [return: MarshalAs(UnmanagedType.IDispatch)] get; } [DispId(0xca)] object Container { [return: MarshalAs(UnmanagedType.IDispatch)] get; } [DispId(0xcb)] object Document { [return: MarshalAs(UnmanagedType.IDispatch)] get; } [DispId(0xcc)] bool TopLevelContainer { get; } [DispId(0xcd)] string Type { get; } [DispId(0xce)] int Left { get; set; } [DispId(0xcf)] int Top { get; set; } [DispId(0xd0)] int Width { get; set; } [DispId(0xd1)] int Height { get; set; } [DispId(210)] string LocationName { get; } [DispId(0xd3)] string LocationURL { get; } [DispId(0xd4)] bool Busy { get; } [DispId(300)] void Quit(); [DispId(0x12d)] void ClientToWindow(out int pcx, out int pcy); [DispId(0x12e)] void PutProperty([In] string property, [In] object vtValue); [DispId(0x12f)] object GetProperty([In] string property); [DispId(0)] string Name { get; } [DispId(-515)] int HWND { get; } [DispId(400)] string FullName { get; } [DispId(0x191)] string Path { get; } [DispId(0x192)] bool Visible { get; set; } [DispId(0x193)] bool StatusBar { get; set; } [DispId(0x194)] string StatusText { get; set; } [DispId(0x195)] int ToolBar { get; set; } [DispId(0x196)] bool MenuBar { get; set; } [DispId(0x197)] bool FullScreen { get; set; } [DispId(500)] void Navigate2([In] ref object URL, [In] ref object flags, [In] ref object targetFrameName, [In] ref object postData, [In] ref object headers); [DispId(0x1f5)] NativeMethods.OLECMDF QueryStatusWB([In] NativeMethods.OLECMDID cmdID); [DispId(0x1f6)] void ExecWB([In] NativeMethods.OLECMDID cmdID, [In] NativeMethods.OLECMDEXECOPT cmdexecopt, ref object pvaIn, IntPtr pvaOut); [DispId(0x1f7)] void ShowBrowserBar([In] ref object pvaClsid, [In] ref object pvarShow, [In] ref object pvarSize); [DispId(-525)] WebBrowserReadyState ReadyState { get; } [DispId(550)] bool Offline { get; set; } [DispId(0x227)] bool Silent { get; set; } [DispId(0x228)] bool RegisterAsBrowser { get; set; } [DispId(0x229)] bool RegisterAsDropTarget { get; set; } [DispId(0x22a)] bool TheaterMode { get; set; } [DispId(0x22b)] bool AddressBar { get; set; } [DispId(0x22c)] bool Resizable { get; set; } }
As you can see, the documentation of Microsoft isn't very clear on this... But it is still possible.
Best regards,
Jeroen Landheer. | | jlandheer Tuesday, February 21, 2006 6:24 PM | Hello,
Thanks for your help. I believe I have that all translated into Visual Basic and added into my project in the correct places. However, I am unsure as to what I should have in the NewWindow(Exteneded?) event handler so that the session is carried over, and so that a new instance if IE is not opened.
Thanks again. | | CEisen Tuesday, February 21, 2006 8:40 PM | Hi CEisen
What you have to do in the event handler from the NewWindow2/3 event, is the following:
- Create a new instance of the extended web browser, or any (user)control that consumes it.
- Pass to the object of the event, (called "pDisp"i believe) the "Application" property of the new web browser instance.
In your original post, you also mentioned if you should call AttachInterfaces() and DetachInterfaces(). The answer to this is "No". These methods are being called by the underlying WebBrowser control, which this piece of code is inherited from.
If you have any trouble, please let me know. I can provide you with a code sample if needed. (Maybe I'll put a nice article on the code project )
Best regards,
Jeroen Landheer.
| | jlandheer Wednesday, February 22, 2006 1:06 AM | Hi,
Thanks for the help, but I seem to be having some problems getting this to work. I manage to pass "{System.__ComObject}" from Application:
Dim newbrowser As New frmWebBrowser
pDisp = newbrowser.Browser.Application
newbrowser.Visible = True
"Browser" is a readonly Property referencing the instance of the ExtendedWebBrowser class object in my frmWebBrowser form.
I'm using GMail as my example site. When I start the browser I load GMail and login, then go to view one of my messages. Then, when I click the "Open in New Window" button I get a new instance of Internet Exporer with the GMail login page, and a new instance of my webbrowser form, but the browser is blank and hasn't loaded anything.
Thanks for all your help. Code samples would be great; please send to CEisensmith AT GMail DOT com. | | CEisen Thursday, February 23, 2006 7:06 PM | I am not totally sure how this works.
Is there anyway this extension can be extended further to include RegisterAsBrowser? I am trying to create a tabbed browser -- I really need it for what I am doing. But if I put a webbrowser on subsequent tabs, only the one on the first tab will work. (I am doing the tabbing right and accessing each tab and browser instance right).
By working I mean only the first webbrowser will bring up a context menu with a right click, only the first will respond to a print, print preview, etc., even though I change the webbrowser reference (I add each to an arraylist).
As far as I have been able to figure out, it's because the subsequent webbrowsers need RegisterAsBrowser called when the user will change to that tab.
PLEASE HELP!
This is driving me nuts. I am seriously debating returning to 2003, even though I'd have to rewrite everything. At least tabbed browsing worked in 2003.
Doe
| | Doe Monday, March 06, 2006 3:12 PM | The RegisterAsBrowser property is part of the IWebBrowser2 interface. This property can be accessed the same way as the Application property of the web browser.
Example: private UnsafeNativeMethods.IWebBrowser2 axIWebBrowser2;
/// <SUMMARY>
/// This method supports the .NET Framework infrastructure and is not intended to be used directly from your code.
/// Called by the control when the underlying ActiveX control is created.
/// </SUMMARY>
/// <PARAM name="nativeActiveXObject">
[PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")]
protected override void AttachInterfaces(object nativeActiveXObject)
{
this.axIWebBrowser2 = (UnsafeNativeMethods.IWebBrowser2)nativeActiveXObject;
base.AttachInterfaces(nativeActiveXObject);
}
/// <SUMMARY>
/// Sets or retrieves a value that indicates whether the object is registered as a top-level browser for target name resolution
/// </SUMMARY>
public bool RegisterAsBrowser
{
get { return axIWebBrowser2.RegisterAsBrowser; }
set { axIWebBrowser2.RegisterAsBrowser = value; }
}
/// <SUMMARY>
/// This method supports the .NET Framework infrastructure and is not intended to be used directly from your code.
/// Called by the control when the underlying ActiveX control is discarded.
/// </SUMMARY>
[PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")]
protected override void DetachInterfaces()
{
this.axIWebBrowser2 = null;
base.DetachInterfaces();
}
/// <SUMMARY>
/// Returns the automation object for the web browser
/// </SUMMARY>
public object Application
{
get { return axIWebBrowser2.Application; }
}
Hope this helps!
Best regards,
Jeroen Landheer.
| | jlandheer Monday, March 06, 2006 7:11 PM | [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] protected override void DetachInterfaces() { this.axIWebBrowser2 = null; base.DetachInterfaces(); } /// <SUMMARY> /// Returns the automation object for the web browser /// </SUMMARY> public object Application { get { return axIWebBrowser2.Application; } }
Hope this helps!
Best regards,
Jeroen Landheer.
========
Yes it does. :-)
I am using VB.Net rather than c, but I can readc well enough and will figure it out.
Thanks a lot!!!
Doe
| | Doe Monday, March 06, 2006 7:29 PM | Alright, I think I have sessioning working (Thanks jlandheer).
I'm just having one additional difficulty. Whenever I'm using the WebBrowser and I click a button or link which is tied to a "Window.close()" command, the WebBrowser control hangs. The form the control is on can still be manipulated (and if it is moved under another form, will "Streak" with colored lines from the other window).
I've tried handling this as a Disposed event and as a Visibility changed event and neither seem to work. I've also tried to implement the WindowClosing event for the axIWebBrowser, like the code above has for NewWindow3 and OnNavigate2, but the event never seems to get raised.
Any assistance would be appreciated. Thanks. | | CEisen Tuesday, March 07, 2006 10:04 PM | You can use the WebBrowser.Document.Window.Unload event. This event equals the DHTML onunload event, but be carefull, this event is raised in the following situations:
- Close the current browser window.
- Navigate to another location by entering a new address or selecting a Favorite.
- Click the Back, Forward, Refresh, or Home button.
- Click on an anchor that refers the browser to another Web page.
- Invoke the anchorclick method.
- Invoke the documentwrite method.
- Invoke the documentopen method.
- Invoke the documentclose method.
- Invoke the windowclose method.
- Invoke the windowopen method, providing the possible value _self for the window name.
- Invoke the windownavigate or NavigateAndFind method.
- Invoke the locationreplace method.
- Invoke the locationreload method.
- Specify a new value for the locationhref property.
- Submit a form to the address specified in the ACTION attribute via the INPUT type=submit control, or invoke the submit method.
http://msdn.microsoft.com/library/default.asp?url=/workshop/browser/mshtml/reference/ifaces/document3/attachevent.asp
So I check if the Webbrowser.Document propererty is null in order to see whether it is an Window.Close event.
| | Gidon J Wednesday, March 08, 2006 8:42 PM | Okay, I give up. I've tried.
I am unable to translate the c in public interface IWebBrowser2 into VB.
Is there some way that theVB code that WilfridB shared could be extended to include RegisterAsBrowser? Without RegisterAsBrowser, tabbed browsing seems impossible to do in VB.net 2005. Each browser on each tab really needs to be registered as it is called.
Help from any VBers appreciated!!!
Guess being able to read c, doesn't necessarily mean being able to translate it.
Doe :-(
| | Doe Friday, March 10, 2006 2:21 AM | Hi Doe
Here's the translation of the IWebBrowser2 interface to VB.Net... <ComImport, TypeLibType((TypeLibTypeFlags.FOleAutomation Or (TypeLibTypeFlags.FDual Or TypeLibTypeFlags.FHidden))), Guid("D30C1661-CDAF-11d0-8A3E-00C04FC9E26E"), SuppressUnmanagedCodeSecurity> _
Public Interface IWebBrowser2
' Methods
<DispId(301)> _
Sub ClientToWindow(<Out> ByRef pcx As Integer, <Out> ByRef pcy As Integer)
<DispId(502)> _
Sub ExecWB(<In> ByVal cmdID As OLECMDID, <In> ByVal cmdexecopt As OLECMDEXECOPT, ByRef pvaIn As Object, ByVal pvaOut As IntPtr)
<DispId(303)> _
Function GetProperty(<In> ByVal [property] As String) As Object
<DispId(100)> _
Sub GoBack()
<DispId(101)> _
Sub GoForward()
<DispId(102)> _
Sub GoHome()
<DispId(103)> _
Sub GoSearch()
<DispId(104)> _
Sub Navigate(<In> ByVal Url As String, <In> ByRef flags As Object, <In> ByRef targetFrameName As Object, <In> ByRef postData As Object, <In> ByRef headers As Object)
<DispId(500)> _
Sub Navigate2(<In> ByRef URL As Object, <In> ByRef flags As Object, <In> ByRef targetFrameName As Object, <In> ByRef postData As Object, <In> ByRef headers As Object)
<DispId(302)> _
Sub PutProperty(<In> ByVal [property] As String, <In> ByVal vtValue As Object)
<DispId(501)> _
Function QueryStatusWB(<In> ByVal cmdID As OLECMDID) As OLECMDF
<DispId(300)> _
Sub Quit()
<DispId(-550)> _
Sub Refresh()
<DispId(105)> _
Sub Refresh2(<In> ByRef level As Object)
<DispId(503)> _
Sub ShowBrowserBar(<In> ByRef pvaClsid As Object, <In> ByRef pvarShow As Object, <In> ByRef pvarSize As Object)
<DispId(106)> _
Sub [Stop]()
' Properties
<DispId(555)> _
Property AddressBar As Boolean
<DispId(200)> _
ReadOnly Property Application As <MarshalAs(UnmanagedType.IDispatch)> Object
<DispId(212)> _
ReadOnly Property Busy As Boolean
<DispId(202)> _
ReadOnly Property Container As <MarshalAs(UnmanagedType.IDispatch)> Object
<DispId(203)> _
ReadOnly Property Document As <MarshalAs(UnmanagedType.IDispatch)> Object
<DispId(400)> _
ReadOnly Property FullName As String
<DispId(407)> _
Property FullScreen As Boolean
<DispId(209)> _
Property Height As Integer
<DispId(-515)> _
ReadOnly Property HWND As Integer
<DispId(206)> _
Property Left As Integer
<DispId(210)> _
ReadOnly Property LocationName As String
<DispId(211)> _
ReadOnly Property LocationURL As String
<DispId(406)> _
Property MenuBar As Boolean
<DispId(0)> _
ReadOnly Property Name As String
<DispId(550)> _
Property Offline As Boolean
<DispId(201)> _
ReadOnly Property Parent As <MarshalAs(UnmanagedType.IDispatch)> Object
<DispId(401)> _
ReadOnly Property Path As String
<DispId(-525)> _
ReadOnly Property ReadyState As WebBrowserReadyState
<DispId(552)> _
Property RegisterAsBrowser As Boolean
<DispId(553)> _
Property RegisterAsDropTarget As Boolean
<DispId(556)> _
Property Resizable As Boolean
<DispId(551)> _
Property Silent As Boolean
<DispId(403)> _
Property StatusBar As Boolean
<DispId(404)> _
Property StatusText As String
<DispId(554)> _
Property TheaterMode As Boolean
<DispId(405)> _
Property ToolBar As Integer
<DispId(207)> _
Property Top As Integer
<DispId(204)> _
ReadOnly Property TopLevelContainer As Boolean
<DispId(205)> _
ReadOnly Property Type As String
<DispId(402)> _
Property Visible As Boolean
<DispId(208)> _
Property Width As Integer
End Interface
Hope this helps!
Best regards,
Jeroen Landheer
| | jlandheer Friday, March 10, 2006 11:36 AM | Hi GidonJ.
Yes, this might work for the most common situations, but it still isn't applicable for every situation. A better method in this case might be listening to the WM_PARENTNOTIFY/WM_DESTROY messages by overriding voidWndProc(ref Message)
It's not the most simple solution, but I assure you: It works flawlessly.
An example:
- Create a event called "Quit" or any other name you would like
- Launch the event from the following code:
[PermissionSet(SecurityAction.LinkDemand, Name="FullTrust")]
protected override void WndProc(ref Message m)
{
if (m.Msg == (int)WindowsMessages.WM_PARENTNOTIFY)
{
//int lp = m.LParam.ToInt32();
int wp = m.WParam.ToInt32();
int X = wp & 0xFFFF;
//int Y = (wp >> 16) & 0xFFFF;
if (X == (int)WindowsMessages.WM_DESTROY)
this.OnQuit();
}
base.WndProc(ref m);
}
The WindowMessages enumeration looks like this: enum WindowsMessages
{
WM_ACTIVATE = 0x6,
WM_ACTIVATEAPP = 0x1C,
WM_AFXFIRST = 0x360,
WM_AFXLAST = 0x37F,
WM_APP = 0x8000,
WM_ASKCBFORMATNAME = 0x30C,
WM_CANCELJOURNAL = 0x4B,
WM_CANCELMODE = 0x1F,
WM_CAPTURECHANGED = 0x215,
WM_CHANGECBCHAIN = 0x30D,
WM_CHAR = 0x102,
WM_CHARTOITEM = 0x2F,
WM_CHILDACTIVATE = 0x22,
WM_CLEAR = 0x303,
WM_CLOSE = 0x10,
WM_COMMAND = 0x111,
WM_COMPACTING = 0x41,
WM_COMPAREITEM = 0x39,
WM_CONTEXTMENU = 0x7B,
WM_COPY = 0x301,
WM_COPYDATA = 0x4A,
WM_CREATE = 0x1,
WM_CTLCOLORBTN = 0x135,
WM_CTLCOLORDLG = 0x136,
WM_CTLCOLOREDIT = 0x133,
WM_CTLCOLORLISTBOX = 0x134,
WM_CTLCOLORMSGBOX = 0x132,
WM_CTLCOLORSCROLLBAR = 0x137,
WM_CTLCOLORSTATIC = 0x138,
WM_CUT = 0x300,
WM_DEADCHAR = 0x103,
WM_DELETEITEM = 0x2D,
WM_DESTROY = 0x2,
WM_DESTROYCLIPBOARD = 0x307,
WM_DEVICECHANGE = 0x219,
WM_DEVMODECHANGE = 0x1B,
WM_DISPLAYCHANGE = 0x7E,
WM_DRAWCLIPBOARD = 0x308,
WM_DRAWITEM = 0x2B,
WM_DROPFILES = 0x233,
WM_ENABLE = 0xA,
WM_ENDSESSION = 0x16,
WM_ENTERIDLE = 0x121,
WM_ENTERMENULOOP = 0x211,
WM_ENTERSIZEMOVE = 0x231,
WM_ERASEBKGND = 0x14,
WM_EXITMENULOOP = 0x212,
WM_EXITSIZEMOVE = 0x232,
WM_FONTCHANGE = 0x1D,
WM_GETDLGCODE = 0x87,
WM_GETFONT = 0x31,
WM_GETHOTKEY = 0x33,
WM_GETICON = 0x7F,
WM_GETMINMAXINFO = 0x24,
WM_GETOBJECT = 0x3D,
WM_GETTEXT = 0xD,
WM_GETTEXTLENGTH = 0xE,
WM_HANDHELDFIRST = 0x358,
WM_HANDHELDLAST = 0x35F,
WM_HELP = 0x53,
WM_HOTKEY = 0x312,
WM_HSCROLL = 0x114,
WM_HSCROLLCLIPBOARD = 0x30E,
WM_ICONERASEBKGND = 0x27,
WM_IME_CHAR = 0x286,
WM_IME_COMPOSITION = 0x10F,
WM_IME_COMPOSITIONFULL = 0x284,
WM_IME_CONTROL = 0x283,
WM_IME_ENDCOMPOSITION = 0x10E,
WM_IME_KEYDOWN = 0x290,
WM_IME_KEYLAST = 0x10F,
WM_IME_KEYUP = 0x291,
WM_IME_NOTIFY = 0x282,
WM_IME_REQUEST = 0x288,
WM_IME_SELECT = 0x285,
WM_IME_SETCONTEXT = 0x281,
WM_IME_STARTCOMPOSITION = 0x10D,
WM_INITDIALOG = 0x110,
WM_INITMENU = 0x116,
WM_INITMENUPOPUP = 0x117,
WM_INPUTLANGCHANGE = 0x51,
WM_INPUTLANGCHANGEREQUEST = 0x50,
WM_KEYDOWN = 0x100,
WM_KEYFIRST = 0x100,
WM_KEYLAST = 0x108,
WM_KEYUP = 0x101,
WM_KILLFOCUS = 0x8,
WM_LBUTTONDBLCLK = 0x203,
WM_LBUTTONDOWN = 0x201,
WM_LBUTTONUP = 0x202,
WM_MBUTTONDBLCLK = 0x209,
WM_MBUTTONDOWN = 0x207,
WM_MBUTTONUP = 0x208,
WM_MDIACTIVATE = 0x222,
WM_MDICASCADE = 0x227,
WM_MDICREATE = 0x220,
WM_MDIDESTROY = 0x221,
WM_MDIGETACTIVE = 0x229,
WM_MDIICONARRANGE = 0x228,
WM_MDIMAXIMIZE = 0x225,
WM_MDINEXT = 0x224,
WM_MDIREFRESHMENU = 0x234,
WM_MDIRESTORE = 0x223,
WM_MDISETMENU = 0x230,
WM_MDITILE = 0x226,
WM_MEASUREITEM = 0x2C,
WM_MENUCHAR = 0x120,
WM_MENUCOMMAND = 0x126,
WM_MENUDRAG = 0x123,
WM_MENUGETOBJECT = 0x124,
WM_MENURBUTTONUP = 0x122,
WM_MENUSELECT = 0x11F,
WM_MOUSEACTIVATE = 0x21,
WM_MOUSEFIRST = 0x200,
WM_MOUSEHOVER = 0x2A1,
WM_MOUSELAST = 0x20A,
WM_MOUSELEAVE = 0x2A3,
WM_MOUSEMOVE = 0x200,
WM_MOUSEWHEEL = 0x20A,
WM_MOVE = 0x3,
WM_MOVING = 0x216,
WM_NCACTIVATE = 0x86,
WM_NCCALCSIZE = 0x83,
WM_NCCREATE = 0x81,
WM_NCDESTROY = 0x82,
WM_NCHITTEST = 0x84,
WM_NCLBUTTONDBLCLK = 0xA3,
WM_NCLBUTTONDOWN = 0xA1,
WM_NCLBUTTONUP = 0xA2,
WM_NCMBUTTONDBLCLK = 0xA9,
WM_NCMBUTTONDOWN = 0xA7,
WM_NCMBUTTONUP = 0xA8,
WM_NCMOUSEHOVER = 0x2A0,
WM_NCMOUSELEAVE = 0x2A2,
WM_NCMOUSEMOVE = 0xA0,
WM_NCPAINT = 0x85,
WM_NCRBUTTONDBLCLK = 0xA6,
WM_NCRBUTTONDOWN = 0xA4,
WM_NCRBUTTONUP = 0xA5,
WM_NEXTDLGCTL = 0x28,
WM_NEXTMENU = 0x213,
WM_NOTIFY = 0x4E,
WM_NOTIFYFORMAT = 0x55,
WM_NULL = 0x0,
WM_PAINT = 0xF,
WM_PAINTCLIPBOARD = 0x309,
WM_PAINTICON = 0x26,
WM_PALETTECHANGED = 0x311,
WM_PALETTEISCHANGING = 0x310,
WM_PARENTNOTIFY = 0x210,
WM_PASTE = 0x302,
WM_PENWINFIRST = 0x380,
WM_PENWINLAST = 0x38F,
WM_POWER = 0x48,
WM_PRINT = 0x317,
WM_PRINTCLIENT = 0x318,
WM_QUERYDRAGICON = 0x37,
WM_QUERYENDSESSION = 0x11,
WM_QUERYNEWPALETTE = 0x30F,
WM_QUERYOPEN = 0x13,
WM_QUEUESYNC = 0x23,
WM_QUIT = 0x12,
WM_RBUTTONDBLCLK = 0x206,
WM_RBUTTONDOWN = 0x204,
WM_RBUTTONUP = 0x205,
WM_RENDERALLFORMATS = 0x306,
WM_RENDERFORMAT = 0x305,
WM_SETCURSOR = 0x20,
WM_SETFOCUS = 0x7,
WM_SETFONT = 0x30,
WM_SETHOTKEY = 0x32,
WM_SETICON = 0x80,
WM_SETREDRAW = 0xB,
WM_SETTEXT = 0xC,
WM_SETTINGCHANGE = 0x1A,
WM_SHOWWINDOW = 0x18,
WM_SIZE = 0x5,
WM_SIZECLIPBOARD = 0x30B,
WM_SIZING = 0x214,
WM_SPOOLERSTATUS = 0x2A,
WM_STYLECHANGED = 0x7D,
WM_STYLECHANGING = 0x7C,
WM_SYNCPAINT = 0x88,
WM_SYSCHAR = 0x106,
WM_SYSCOLORCHANGE = 0x15,
WM_SYSCOMMAND = 0x112,
WM_SYSDEADCHAR = 0x107,
WM_SYSKEYDOWN = 0x104,
WM_SYSKEYUP = 0x105,
WM_TCARD = 0x52,
WM_TIMECHANGE = 0x1E,
WM_TIMER = 0x113,
WM_UNDO = 0x304,
WM_UNINITMENUPOPUP = 0x125,
WM_USER = 0x400,
WM_USERCHANGED = 0x54,
WM_VKEYTOITEM = 0x2E,
WM_VSCROLL = 0x115,
WM_VSCROLLCLIPBOARD = 0x30A,
WM_WINDOWPOSCHANGED = 0x47,
WM_WINDOWPOSCHANGING = 0x46,
WM_WININICHANGE = 0x1A
}
I got this knowledge from someone else on one of the other threads of this forum, who's name I've forgotten ,but this information is based on a Microsoft Knowledge Base article.
Best regards,
Jeroen Landheer. | | jlandheer Friday, March 10, 2006 12:21 PM | Egad, yes!!!
I was thinking you were just a c guy, so didn't want to ask you.
Thanks ever so much.
Whew.
Doe
| | Doe Friday, March 10, 2006 2:16 PM | Thanks to both GidonJ and jlandheer for their help. I've tried both the solutions, but I haven't been able to get either to work completely.
I am closer with jlandheer's; I'm recieving the system messages, and the function responds to me clicking in the browser window; however, I'm not understanding the last bit that is supposed to trigger the event. The WParam always seems to be "0;" even when I've clicked something I know to be a Window.Close() call.
Please advise. Thanks.
<PermissionSet(SecurityAction.Demand, Name:="FullTrust")> _
Protected Overrides Sub WndProc(ByRef m As Message)
If m.Msg = WindowsMessages.WM_PARENTNOTIFY Then
'int lp = m.LParam.ToInt32();
Dim wp As Integer = m.WParam.ToInt32
Dim X As Integer = wp + &HFFFF
'int Y = (wp >> 16) & 0xFFFF;
If X = WindowsMessages.WM_DESTROY Then
RaiseEvent Quit()
End If
End If
MyBase.WndProc(m)
End Sub | | CEisen Tuesday, March 21, 2006 2:49 PM | In case anyone elseis interested, the code for the IWebBrowser2 interfacecan be found at pinvoke.net in both C and VB. They also seem to have code for all the interop interfaces.
http://www.pinvoke.net/default.aspx/Interfaces.IWebBrowser2
Doe :-)
| | Doe Monday, March 27, 2006 9:01 PM | For everyone that's interested. I've posted an article on the code project as promised.
The URL is: http://www.codeproject.com/csharp/ExtendedWebBrowser.asp
Best regards,
Jeroen Landheer. | | jlandheer Wednesday, March 29, 2006 1:50 PM | I'm not getting anything (Nothing) from e.Headers.
What I've done:
1) copied the module definition into my project
2) created the following class:
Public Class IETools
Private wb As New ExtendedWebBrowser
Private webBrowserHeader As String
'constructor
Public Sub New()
MyBase.new()
AddHandler wb.NavigatingExtended, AddressOf wb_NavigatingExtended
End Sub
'destructor
Protected Overrides Sub Finalize()
RemoveHandler wb.NavigatingExtended, AddressOf wb_NavigatingExtended
MyBase.Finalize()
End Sub
'Properties
Public ReadOnly Property GetIEheader()
'make the header available
Get
Return webBrowserHeader
End Get
End Property
'make the webbrowser navigate. Then capture the Navigate event
Public Sub LoadIEheaders(ByVal strUnusedURL As String)
wb.Navigate(strUnusedURL, False)
End Sub
'capture the Navigating event
Private Sub wb_NavigatingExtended(ByVal sender As Object, ByVal e As ExtendedWebBrowserModule.WebBrowserNavigatingExtendedEventArgs)
'grab the header
webBrowserHeader = e.Headers
e.Cancel = True
End Sub
End Class
3) Then I try to get the Header like this when my project starts up:
'if headers did not load from settings file, then grab default IE headers
If strHeaders = "" Then
Dim classBrowser As New IETools
classBrowser.LoadIEheaders( "www.microsoft.com")
strHeaders = classBrowser.GetIEheader
End If
4) strHeaders is always Nothing. :-( | | Alex19063 Thursday, April 20, 2006 2:04 AM | Hi,
I've just started to use VB.NET in VS 2005 and I had a specific requirement to capture the URL from a NewWindow event as part of a browser application and with this URL, effectively open a popup window. Having hit the limits of what the standard WebBrowser control will do in VS 2005, i turned to the net and found this thread. I copied WilfridB's code into my application and it worked first time. Many, many thanks to you all, for without people like yourselves, people like me would never be able to achieve what we aim for. You are appreciated!  | | lavenm50 Sunday, June 18, 2006 3:08 AM | This is such a very good thread 
BTW. Can anybody show me how to change the browser USER-AGENT from the original Mozilla MSIE6 to my own custom one? (ie: USER-AGENT: MyApp 1.1.1) | | KissedNunueng Thursday, July 20, 2006 9:39 PM | I'm using the vb.net 2005code example thatWilfrid posted. I'm redirecting a popup window to a new windows form instead of the default IE one. My question is, how do I get BeforeNavigate2 working in Wilfrid's code? I want to watch for the user clicking a button on a form so that I can close the popup window automatically. I can't seem to figure out how to implement BeforeNavigate2 so I can get the URL and if it contains javascript:window.close then close my windows form.
Any assistance would be greatly appreciated.
Kindest regards,
Josh | | JoshuaVB Thursday, September 07, 2006 4:08 PM | Did you ever get an answer?
I have a Windows browser and I need to change the User-Agent too.
Thanks, Filippo | | SanDiegoKiss Friday, October 06, 2006 11:35 PM | I believe this is one of the best threads to have ever have occurred on this board. I've read it so many times because I've been working on a large project ( www.freewebs.com/reneecc) for about the last year and it's web browser based.
A couple of weeks ago, i noticed that the webbrowser portion was not navigating flash well at all and I came to understand the full significance of the navigating3 events. I included Jeroen Landheer's code from the code project code into my solution and it certainly did fix the flash problems.
However, since then, I've noticed a couple of small inexplicable behaviors.
On these fora if I enter 'test' into a textbox and depress <enter> my browser navigates right back to the MSDN page that it was on. If I depress the Search button, navigation is quite proper. I wrote to the codeproject auther and have received no response. What's worse if that Test<enter> works with a program with nothing but an extended events view. I find extented code difficult to debug because there's very little code there. Most of it is a series of event driven black boxes.
These little bugs are the last known bugs and I really would like to get them cleaned up. Any help would be very appreciated.
It should also be a major message to MS that this thread has 38,000 + reads. I have never seen any thread receive the attention this thread, which means there are notable product issues with this control.
| | ReneeC Saturday, February 17, 2007 11:16 AM | I am afraid this is not helping me.
I am working in VB 6.0 so please tell me in the same language.
Thanks in advance.
| | Prashant Sahay Monday, February 19, 2007 12:46 PM | These fora are not for vb6. | | ReneeC Thursday, February 22, 2007 12:27 AM | Hello,
this code is really nice!
But after implementing it, I have problems with the standard events like "WebBrowser1.DocumentCompleted or WebBrowser1.CanGoBackChanged
How can I use New Events and the old Events together?
InVB05-Code please!
Thanks for your help | | dennis_dieckmann Sunday, April 01, 2007 9:59 AM | O.k just wanted to say a huge thanks to all the contributors to this thread. Its been a massively helpful post and I really do appreciate the time, trouble and effort you all have put into providing a solution to the requirements gap found between the VS webcontrol and what I would have thoughtto be it's fairly typical use.
The less I have to poke around Win32 and COM the better. Thanks a bunch.
| | SpeakerBob Saturday, August 11, 2007 2:53 AM | I've never seen a thread with replies over such a long period of time!
Anyway, since this thread is what gave me the leg up to extend the standard WebBrowser component with the DWebBrowser2 interface to get the 'useful' events like NewWindow3 (plus code to allow the html script itself to close the popup window) and add in the IWebBrowser2 interface to get the RegisterAsTopLevelBrowser property, have a look here or here at the full definition of the extended webbrowser Iended up with based on the contributions to this thread.
Please let me know if anybody has any comments or if this was useful.Bear in mind it was my first foray into .NET so i'm sure it's untidy and will make professional developers cringe ;-) As no doubt will my web pages but I'm learning all about them as well!
Many thanks to all.
Martyn.
| | lavenm50 Sunday, September 02, 2007 1:28 AM | Hello everyone.
It's been some time since I last posted anything here, but I've done some research in the mean time.Here's a solutionto capture script errors in the browser control, without using the "ScriptErrorsSuppressed" property. The latter causes also otherdialogboxes to disapear, including certificate logon, and so on. This solution is different from one I posted on the code project earlier andworks more solid. I'm still working on a solution to let pages run in a predetermined security zone and I'll expect to resolve this within the next few months.
For anyone who's interested and is short on time, the script error solution can be achieved by extendingthe classWebBrowser+WebBrowserSite and implementing the IOleClientSide interface. It was a though one to tackle, but one who deserves to be mentioned.
In the extended webbrowser class, create a class "ExtendedWebbrowserSite" that extends WebBrowserSite
-
Override the "CreateWebBrowserSiteBase" method and return your own "ExtendedWebBrowserSite" class instance
-
Implement the IOleCommandTarget interface in your ExtendedWebbrowserSite class
When this is done, you can capture a lot of events and other things, which allowsmore control over the web browser.
Here's a piece of code that I use for the ExtendedWebBrowserSite class:
/// <summary> /// Represenst an extended version of the <see cref="WebBrowser.WebBrowserSite"/> class /// </summary> /// <remarks> /// This class implements <b>IOleCommandTarget</b> for handling script errors /// </remarks> protected class ExtendedWebBrowserSite : WebBrowserSite, NativeMethods.IOleCommandTarget { /// <summary> /// Creates a new instance of the <see cref="ExtendedWebBrowserSite"/> class /// </summary> /// <param name="host">The <see cref="ExtendedWebBrowser"/> hosting the browser</param> public ExtendedWebBrowserSite(ExtendedWebBrowser host) : base(host) { _host = host; }
private ExtendedWebBrowser _host; private ExtendedWebBrowser Host { get { return _host; } }
#region IOleCommandTarget Members
int NativeMethods.IOleCommandTarget.QueryStatus(ref Guid pguidCmdGroup, int cCmds, NativeMethods.OLECMD prgCmds, IntPtr pCmdText) { return NativeMethods.S_FALSE; }
int NativeMethods.IOleCommandTarget.Exec(ref Guid pguidCmdGroup, int nCmdID, int nCmdexecopt, object[] pvaIn, ref int pvaOut) { int hResult = NativeMethods.S_OK; //if (pguidCmdGroup == null) // return h Result; // Check for invalid pointers (or get a NullReferenceException on a value type???) if (pguidCmdGroup == NativeMethods.CGID_DocHostCommandHandler) { switch (nCmdID) { case (int)NativeMethods.OLECMDID.OLECMDID_SHOWSCRIPTERROR: // Dit werkt  // mshtml.IHTMLDocument document mshtml.IHTMLDocument2 doc = (mshtml.IHTMLDocument2)pvaIn[0]; mshtml.IHTMLWindow2 window = doc.parentWindow; mshtml.IHTMLEventObj2 wndevt = (mshtml.IHTMLEventObj2)window.@event; // IntPtr wndPtr = Marshal.GetIDispatchForObject(wndevt);
string errorMessage = (string)wndevt.getAttribute("errorMessage", 0); int errorLine = (int)wndevt.getAttribute("errorLine", 0); int errorCode = (int)wndevt.getAttribute("errorCode", 0); int errorCharacter = (int)wndevt.getAttribute("errorCharacter", 0); string errorUrl = (string)wndevt.getAttribute("errorUrl", 0);
// Notify the host _host.OnScriptError(new ScriptErrorEventArgs(new BrowserScriptError(new Uri(errorUrl), errorLine, errorCharacter, errorMessage)));
// Hide the dialog pvaOut = NativeMethods.VARIANT_TRUE;
break; default: hResult = NativeMethods.OLECMDERR_E_NOTSUPPORTED; break; } } return hResult; }
#endregion }
Next, the host implements this class by overriding the CreateWebBrowserSiteBase method:
/// <summary> /// Overridden. Creates a new <see cref="ExtendedWebBrowserSite"/> instance with this instance as the host /// </summary> /// <returns>A new instance of the <see cref="ExtendedWebBrowserSite"/> class</returns> protected override WebBrowserSiteBase CreateWebBrowserSiteBase() { return new ExtendedWebBrowserSite(this); }
And ofcourse, OnScriptError (with it's appropiate EventArgs item) looks like this:
private static readonly object EventScriptError = new object(); /// <summary> /// Fired when a script error occured /// </summary> public event EventHandler<ScriptErrorEventArgs> ScriptError { add { Events.AddHandler(EventScriptError, value); } remove { Events.RemoveHandler(EventScriptError, value); } }
/// <summary> /// Raises the <see cref="ScriptError"/> event /// </summary> /// <param name="e">The <see cref="ScriptErrorEventArgs"/> event arguments</param> /// <remarks>The script error is automatically recorded in the <see cref="ScriptErrorControl"/></remarks> protected virtual void OnScriptError(ScriptErrorEventArgs e) { if (e == null) throw new ArgumentNullException("e"); // ScriptErrorControl.Instance.BrowserScriptErrorCollection.Add(e.Error); BrowserApplication.Instance.AddScriptError(e.Error);
EventHandler<ScriptErrorEventArgs> handler = (EventHandler<ScriptErrorEventArgs>)Events[EventScriptError]; if (handler != null) handler(this, e); }
The reason that this is using "Events.AddHandler" and "Events.RemoveHandler" is that the events will not fire when the control is disposed. (The EventHandlerList will be disposed, causing all it's events to stop working)
Finally theScriptErrorEventArgs class is a simple standardEventArgs class with some extra parameters.
Before I forget, the interface IOleCommandTarget and related items:
public enum OLECMDF { // Fields OLECMDF_DEFHIDEONCTXTMENU = 0x20, OLECMDF_ENABLED = 2, OLECMDF_INVISIBLE = 0x10, OLECMDF_LATCHED = 4, OLECMDF_NINCHED = 8, OLECMDF_SUPPORTED = 1 }
public enum OLECMDID { // Fields OLECMDID_PAGESETUP = 8, OLECMDID_PRINT = 6, OLECMDID_PRINTPREVIEW = 7, OLECMDID_PROPERTIES = 10, OLECMDID_SAVEAS = 4, OLECMDID_SHOWSCRIPTERROR = 40 } public enum OLECMDEXECOPT { // Fields OLECMDEXECOPT_DODEFAULT = 0, OLECMDEXECOPT_DONTPROMPTUSER = 2, OLECMDEXECOPT_PROMPTUSER = 1, OLECMDEXECOPT_SHOWHELP = 3 }
[ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("B722BCCB-4E68-101B-A2BC-00AA00404770"), ComVisible(true)] public interface IOleCommandTarget { [return: MarshalAs(UnmanagedType.I4)] [PreserveSig] int QueryStatus(ref Guid pguidCmdGroup, int cCmds, [In, Out] NativeMethods.OLECMD prgCmds, [In, Out] IntPtr pCmdText); [return: MarshalAs(UnmanagedType.I4)] [PreserveSig] int Exec(ref Guid pguidCmdGroup, int nCmdID, int nCmdexecopt, [In, MarshalAs(UnmanagedType.LPArray)] object[] pvaIn, ref int pvaOut); }
[StructLayout(LayoutKind.Sequential)] public class OLECMD { [MarshalAs(UnmanagedType.U4)] public int cmdID; [MarshalAs(UnmanagedType.U4)] public int cmdf; public OLECMD() { } }
Hope this helps and keep the suggestions coming!
Best regards,
Jeroen Landheer
| | Jeroen Landheer Tuesday, September 04, 2007 2:30 AM | Hello ReneeC
Sorry if you haven't got a response from me, I try to answer all my mail but as you can imagine, I'm getting a LOT of mail... It seems that this control is populair, and the amount of feedback is simply too much for a single person to handle. So if I disappointed you, my apologies.
For the other part, I've also had some weird issues, especially with the directional keys and have changed the procedures in the mean time to combat this problem. Here's what I'm using at the moment, let me know if this works for you:
/// <summary> /// Overridden. Handles window messages for the browser control /// </summary> /// <param name="m">The <see cref="Message"/> send to this procedure</param> [PermissionSet(SecurityAction.LinkDemand, Name = "FullTrust")] protected override void WndProc(ref Message m) { switch ((NativeMethods.WindowsMessages)m.Msg) { // These are for getting flash to work case NativeMethods.WindowsMessages.WM_LBUTTONDOWN: case NativeMethods.WindowsMessages.WM_RBUTTONDOWN: case NativeMethods.WindowsMessages.WM_MBUTTONDOWN: case NativeMethods.WindowsMessages.WM_MOUSEACTIVATE: //case (int)WindowsMessages.WM_KEYDOWN: //case (int)WindowsMessages.WM_KEYUP: //case (int)WindowsMessages.WM_CHAR: //case (int)WindowsMessages.WM_SYSKEYDOWN: //case (int)WindowsMessages.WM_SYSKEYUP: base.DefWndProc(ref m); // Don't call base break; // When the browser is destroyed, close the window case NativeMethods.WindowsMessages.WM_PARENTNOTIFY: int wp = m.WParam.ToInt32(); int X = wp & 0xFFFF; if (X == (int)NativeMethods.WindowsMessages.WM_DESTROY) this.OnQuit(EventArgs.Empty); // Do the base action goto default; default: // Default action, call base base.WndProc(ref m); break; } }
/// <summary> /// Overridden. Determines if a certain key is an input key /// </summary> /// <remarks> /// The cursor keys are input keys for the web browser. /// </remarks> /// <param name="keyData">The <see cref="Keys"/> pressed</param> /// <returns>true when the key should be handled as an input key, otherwise false</returns> protected override bool IsInputKey(Keys keyData) { if ( ((keyData & Keys.Up) == Keys.Up) || ((keyData & Keys.Down) == Keys.Down) || ((keyData & Keys.Left) == Keys.Left) || ((keyData & Keys.Right) == Keys.Right)) return true; return base.IsInputKey(keyData); }
And about MS? I'm sure that they will read it. ;-) This thread is indeed very populair.
Best regards,
Jeroen Landheer | | Jeroen Landheer Tuesday, September 04, 2007 2:44 AM | Hi, Can this control be useful at design time. I have a situation where I am building a server control and I want to access the HTTP POST message at design time when that control is dragged.
Thanks Abhang
| | Abhang Tuesday, September 25, 2007 10:32 PM | Is this code available in vb .net? | | stranger457 Friday, November 16, 2007 3:02 PM | Hi Wilfrid
Can you send me an email corresponse to maxkam1@hotmail.com? | | MaxKam Monday, December 31, 2007 4:39 PM | I'm trying to integrate this into a VB.NET project in VS2005. Everything works just fine until a new window is spawned then I get the standard 0xc0000005 COM error when it tries to access the Application property.
It's generating the error on the "Application = iwb.Application" line.
Here is the relevant code snippet - I can post more if required:
Public Class ExtendedWebBrowser Inherits WebBrowser
Private cookie As AxHost.ConnectionPointCookie Private wevents As WebBrowserExtendedEvents Private iwb As IWebBrowser2
Protected Overrides Sub AttachInterfaces(ByVal nativeActiveXObject As Object) iwb = CType(nativeActiveXObject, IWebBrowser2) MyBase.AttachInterfaces(nativeActiveXObject) End Sub
Protected Overrides Sub DetachInterfaces() iwb = Nothing MyBase.DetachInterfaces() End Sub
Public ReadOnly Property Application() As Object Get Application = iwb.Application End Get End Property
| | Pat L Friday, February 29, 2008 3:27 PM | Hi,
I've not used the Application property in my project. WhenI open a new window,I use the Handle property and it seems to work. I used to use the line 'e.ppDisp = WebForm.wbPopup.ActiveXInstance' which also worked butI must have found a reason somewhere to change it.
Code Snippet Private Sub wbMain_NewWindowExtended(ByVal sender As Object, ByVal e As ExtendWebBrowserModule.WebBrowserNewWindowExtendedEventArgs) 'This event fires when the browser requests a new window is created msg = "Web site is trying to open a popup window to:" & ControlChars.CrLf & e.Url & ControlChars.CrLf & ControlChars.CrLf & "Do you wish to allow this?" Dim res As DialogResult = ShowMessageBox(msg, My.Resources.BrowserAppName, MessageBoxButtons.YesNo, MessageBoxIcon.Question, MessageBoxDefaultButton.Button2, 0) If res = Windows.Forms.DialogResult.No Then 'User does not want to open the popup. Return e.Cancel to prevent IE opening popup. e.Cancel = True Else 'User wants to open popup - return handle of wbPopup e.ppDisp = WebForm.Handle 'send URL to our WebForm browser control to display it. WebForm.wbPopup.Navigate(e.Url) 'return cancel as True to prevent IE opening a new window as we have handled it. e.Cancel = True WebForm.Visible = True WebForm.BringToFront() End If End Sub
I hope this helps. | | lavenm50 Friday, February 29, 2008 4:22 PM | Martyn, This certinaly does help - how are you going about adding the ppDisp to your ExtendedEventArgs so that it interfaces with your IWebBrowser2 interface? EDIT: Uh, nevermind ... haven't finished my coffee yet, sorry about that.  | | Pat L Friday, February 29, 2008 4:35 PM | Oh, don't thank me. It was WilfridB that posted the original code so let me in turn say manythanks to WifridB for starting this thread!
I wonder if anyone reading this thread has used this code with VS2008 yet? Or indeed whether it's even necessary anymore. | | lavenm50 Tuesday, March 11, 2008 12:20 AM | Hi, I've implemented the extendedwebbrowser and it is working brilliantly, however I have discovered a problem when using frames, I don't know if anyone has found a solution to this yet?
I am navigating to a frameset I am handling the extendednavigating event, cancelling the event and adding headers to the request The frameset loads in the control but one of the frames loads in a new window for some reason.
If I use the normal web browser control this happens as well If I use the axtive x one it doesn't happen If I don't handle the navigating event it works perfectly.
Any ideas?
| | Bozz Thursday, June 18, 2009 1:44 PM | Here, Here!
Thread still relevant.
| | JohannTheGray Saturday, August 08, 2009 3:28 AM | Great post! I translated your example to c# and it works like a charm!
However i would like to know if anyone has an idea how to intercept a XMLHttpRequest POST with beforenavigate?
:) - Edited byandurshakur Wednesday, August 19, 2009 4:03 PMtypo
-
| | andurshakur Tuesday, August 18, 2009 9:35 PM | how can a add more code to this in order to disable text selection and picture drag? thanks | | catdance Thursday, October 01, 2009 8:57 AM |
|