5

I need to send headers with every request I transmit using the System.Windows.Controls.WebBrowser control. Using C# with .Net, due to a bug described here, the BeforeNavigate2 event, which would help me with call-by-reference-parameters, is not being fired, and the BeforeNavigate event described in the bug report does not help me, as its parameters are read only.

The Solution described here are difficult to impossible to use as I have a lot of references to the Controls Web Browser that would have to be solved, leading through existing interfaces and other libraries in this project.

Like in the solution described, it would be sufficient to have a fixed string I can set externally, but I would need a solution setting Headers using the Controls Web Browser. Is there a solution that could satisfy my requirements without using Forms?

Remko
  • 7,214
  • 2
  • 32
  • 52
Andreas
  • 227
  • 5
  • 11
  • What about [Frame](http://msdn.microsoft.com/en-us/library/system.windows.controls.frame(v=vs.110).aspx)? – dovid Dec 30 '13 at 12:17
  • Even if I use a frame, I still need to use the WebBrowser Control in C#.net, and the BeforeNavigate2 event still does not fire, and I still cannot set the header. I'm not sure how using a Frame will help there. – Andreas Dec 31 '13 at 10:09
  • see http://wpfbrowsersample.codeplex.com/. use `Navigating` event. I think is triggered each pre-load. – dovid Dec 31 '13 at 11:09
  • I can't get the Browser to work as I don't have access to neither Visual Studio 11 nor .Net 4.5 (Stuck with 10 and .Net 4). Also, I have looked through the "Navigating" event, as that is an event I CAN subscribe to and is fired, and I see no possibility to edit the current header of the request with that event. – Andreas Dec 31 '13 at 11:30

1 Answers1

5

Here is another approach, it uses the low-level COM connection point container interfaces to attach to the underlying DWebBrowserEvents2 source interface directly.

Grab the full project source code from here. I don't have VS2010, so it's a VS2012 project, but it targets .NET 4.0. A compiled app is included in the Debug folder, if you're ready to try it as is.

Here is the relevant code. It's a quick proof of concept which still may have bugs:

using System;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;

namespace WpfWebBrowserEvents
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();

            this.Loaded += (s, e) =>
            {
                var sink = new WebBrowserEventSink();
                sink.Connect(this.webBrowser);

                this.webBrowser.Navigate("http://example.com");
            };
        }
    }

    /// <summary>
    /// Handling WebBrowser ActiveX events directly
    /// by Noseratio - http://stackoverflow.com/q/20838264/1768303
    /// </summary>

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [ComDefaultInterface(typeof(SHDocVw.DWebBrowserEvents2))]
    public class WebBrowserEventSink : SHDocVw.DWebBrowserEvents2
    {
        System.Runtime.InteropServices.ComTypes.IConnectionPoint _sinkCP = null;
        int _sinkCookie = int.MaxValue;

        public void Connect(System.Windows.Controls.WebBrowser webBrowser)
        {
            if (_sinkCookie != int.MaxValue)
                throw new InvalidOperationException();

            var activeXInstance = webBrowser.GetType().InvokeMember("ActiveXInstance",
                BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
                null, webBrowser, new object[] { }) as SHDocVw.WebBrowser;

            var cpc = (System.Runtime.InteropServices.ComTypes.IConnectionPointContainer)activeXInstance;
            var guid = typeof(SHDocVw.DWebBrowserEvents2).GUID;
            System.Runtime.InteropServices.ComTypes.IConnectionPoint _sinkCP;
            cpc.FindConnectionPoint(ref guid, out _sinkCP);
            _sinkCP.Advise(this, out _sinkCookie);
        }

        public void Disconnect()
        {
            if (_sinkCookie == int.MaxValue)
                throw new InvalidOperationException();
            _sinkCP.Unadvise(_sinkCookie);
            _sinkCookie = int.MaxValue;
            _sinkCP = null;
        }

        #region SHDocVw.DWebBrowserEvents2

        public void StatusTextChange(string Text)
        {
        }

        public void ProgressChange(int Progress, int ProgressMax)
        {
        }

        public void CommandStateChange(int Command, bool Enable)
        {
        }

        public void DownloadBegin()
        {
        }

        public void DownloadComplete()
        {
        }

        public void TitleChange(string Text)
        {
        }

        public void PropertyChange(string szProperty)
        {
        }

        public void BeforeNavigate2(object pDisp, ref object URL, ref object Flags, ref object TargetFrameName, ref object PostData, ref object Headers, ref bool Cancel)
        {
            MessageBox.Show("BeforeNavigate2: " + URL.ToString());
        }

        public void NewWindow2(ref object ppDisp, ref bool Cancel)
        {
        }

        public void NavigateComplete2(object pDisp, ref object URL)
        {
        }

        public void DocumentComplete(object pDisp, ref object URL)
        {
        }

        public void OnQuit()
        {
        }

        public void OnVisible(bool Visible)
        {
        }

        public void OnToolBar(bool ToolBar)
        {
        }

        public void OnMenuBar(bool MenuBar)
        {
        }

        public void OnStatusBar(bool StatusBar)
        {
        }

        public void OnFullScreen(bool FullScreen)
        {
        }

        public void OnTheaterMode(bool TheaterMode)
        {
        }

        public void WindowSetResizable(bool Resizable)
        {
        }

        public void WindowSetLeft(int Left)
        {
        }

        public void WindowSetTop(int Top)
        {
        }

        public void WindowSetWidth(int Width)
        {
        }

        public void WindowSetHeight(int Height)
        {
        }

        public void WindowClosing(bool IsChildWindow, ref bool Cancel)
        {
        }

        public void ClientToHostWindow(ref int CX, ref int CY)
        {
        }

        public void SetSecureLockIcon(int SecureLockIcon)
        {
        }

        public void FileDownload(bool ActiveDocument, ref bool Cancel)
        {
        }

        public void NavigateError(object pDisp, ref object URL, ref object Frame, ref object StatusCode, ref bool Cancel)
        {
        }

        public void PrintTemplateInstantiation(object pDisp)
        {
        }

        public void PrintTemplateTeardown(object pDisp)
        {
        }

        public void UpdatePageStatus(object pDisp, ref object nPage, ref object fDone)
        {
        }

        public void PrivacyImpactedStateChange(bool bImpacted)
        {
        }

        public void NewWindow3(ref object ppDisp, ref bool Cancel, uint dwFlags, string bstrUrlContext, string bstrUrl)
        {
        }

        public void SetPhishingFilterStatus(int PhishingFilterStatus)
        {
        }

        public void WindowStateChanged(uint dwWindowStateFlags, uint dwValidFlagsMask)
        {
        }

        public void NewProcess(int lCauseFlag, object pWB2, ref bool Cancel)
        {
        }

        public void ThirdPartyUrlBlocked(ref object URL, uint dwCount)
        {
        }

        public void RedirectXDomainBlocked(object pDisp, ref object StartURL, ref object RedirectURL, ref object Frame, ref object StatusCode)
        {
        }

        public void BeforeScriptExecute(object pDispWindow)
        {
        }

        public void WebWorkerStarted(uint dwUniqueID, string bstrWorkerLabel)
        {
        }

        public void WebWorkerFinsihed(uint dwUniqueID)
        {
        }

        #endregion
    }

}
noseratio
  • 59,932
  • 34
  • 208
  • 486
  • 1
    On the first look I think I might have tried something like this already. I will try and reply, but I don't know if I can manage to before the weekend. Just writing this to tell you I've read it. – Andreas Jan 03 '14 at 12:24
  • Apparently, the Headers seem to be Read Only, even tho they are parameters by reference. When I set a Header at "Navigate", it gets sent through. Changing the Header at BeforeNavigate2 seems not to change the Header to be sent through. There is the option to cancel the request and navigate again with the proper headers in place, but I would like to avoid that. Any idea? – Andreas Jan 03 '14 at 15:16
  • 1
    @Andreas, thus, despite the `Headers` arg is passed by `ref`, it gets ignored when you assign it? If so, there are only two possible options I can think of, both are quite non-trivial: 1) implement an in-process proxy server and configure `WebBrowser` to use it (with `UrlMkSetSessionOption`). 2) Implement an in-process [pluggable protocol handler](http://msdn.microsoft.com/en-us/library/aa767743(v=vs.85).aspx) to intercept and alter HTTP traffic. – noseratio Jan 04 '14 at 08:53
  • Thanks for those replies... for option 1) we already use a system wide proxy that I cannot change (and should/must not change, especially on IE level). For option 2) correct me if necessary, but as we also use https traffic, intercepting and altering it would be... 'difficult'. Also, I option 2) by my boss and got a no-go for it. I tried the "Cancel Navigation and Renavigate with added Headers" approach, but that led to unforseen consequences that broke the entire system, and I have yet to analyze it entirely. – Andreas Jan 07 '14 at 15:51
  • 1
    This is awesome. Really helps me a lot! – psulek Oct 14 '14 at 08:24
  • 1
    Great work. Managed to reliably enable/disable a load in progress animation not only for top level navigation this way. – bgx Nov 27 '17 at 12:18