50

I am trying to change the UserAgent of the WebBrowser control in a Winforms application.

I have successfully achieved this by using the following code:

[DllImport("urlmon.dll", CharSet = CharSet.Ansi)]
private static extern int UrlMkSetSessionOption(
    int dwOption, string pBuffer, int dwBufferLength, int dwReserved);

const int URLMON_OPTION_USERAGENT = 0x10000001;

public void ChangeUserAgent()
{
    List<string> userAgent = new List<string>();
    string ua = "Googlebot/2.1 (+http://www.google.com/bot.html)";

    UrlMkSetSessionOption(URLMON_OPTION_USERAGENT, ua, ua.Length, 0);
}

The only problem is that this only works once. When I try to run the ChangeUserAgent() method for the second time it doesn't work. It stays set to the first changed value. This is quite annoying and I've tried everything but it just won't change more than once.

Does anyone know of a different, more flexible approach?

Thanks

clami219
  • 2,958
  • 1
  • 31
  • 45
Proximo
  • 6,235
  • 11
  • 49
  • 67

4 Answers4

66

The easiest way:

webBrowser.Navigate("http://localhost/run.php", null, null,
                    "User-Agent: Here Put The User Agent");
Constantin
  • 2,288
  • 2
  • 24
  • 31
  • 4
    you have to terminate the custom header with `\r\n` – Smith Jul 10 '12 at 09:29
  • 11
    If the webbrowser control navigate away from that URI (when clicking a link, or moving back/forward) the user agent is reset. Ideally it the solution should work for all requests the control does. – monzonj Aug 02 '13 at 07:56
  • 8
    It only works for the original request passed to Navigate - all AJAX calls within the website will still use control's default UA – grzegorz_p Nov 11 '14 at 13:32
  • @Constantin The Webbrowser will open my default browser automatically with the url when i use this code on my project. I just want the webpage load into webbrowser but open extra browser. Did i miss something in my code?`browser.Navigate(url, "User-Agent:Mozilla/5.0 (Linux; Android 6.0; Nexus 5 Build/MRA58N) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Mobile Safari/537.36");` – 劉鎮瑲 May 22 '19 at 08:18
37

I'm not sure whether I should just copy/paste from a website, but I'd rather leave the answer here, instead of a link. If anyone can clarify in comments, I'll be much obliged.

Basically, you have to extend the WebBrowser class.

public class ExtendedWebBrowser : WebBrowser
{
    bool renavigating = false;

    public string UserAgent { get; set; }

    public ExtendedWebBrowser()
    {
        DocumentCompleted += SetupBrowser;

        //this will cause SetupBrowser to run (we need a document object)
        Navigate("about:blank");
    }

    void SetupBrowser(object sender, WebBrowserDocumentCompletedEventArgs e)
    {
        DocumentCompleted -= SetupBrowser;
        SHDocVw.WebBrowser xBrowser = (SHDocVw.WebBrowser)ActiveXInstance;
        xBrowser.BeforeNavigate2 += BeforeNavigate;
        DocumentCompleted += PageLoaded;
    }

    void PageLoaded(object sender, WebBrowserDocumentCompletedEventArgs e)
    {

    }

    void BeforeNavigate(object pDisp, ref object url, ref object flags, ref object targetFrameName,
        ref object postData, ref object headers, ref bool cancel)
    {
        if (!string.IsNullOrEmpty(UserAgent))
        {
            if (!renavigating)
            {
                headers += string.Format("User-Agent: {0}\r\n", UserAgent);
                renavigating = true;
                cancel = true;
                Navigate((string)url, (string)targetFrameName, (byte[])postData, (string)headers);
            }
            else
            {
                renavigating = false;
            }
        }
    }
}

Note: To use the method above you’ll need to add a COM reference to “Microsoft Internet Controls”.

He mentions your approach too, and states that the WebBrowser control seems to cache this user agent string, so it will not change the user agent without restarting the process.

Jean Azzopardi
  • 2,289
  • 23
  • 36
  • Is there a way to restart the process, maybe by using threading? I have limited experience with winforms. – Proximo Jun 02 '09 at 02:23
  • I'm not so sure, I'd try it myself, but I think the approach I gave in my answer should work. It's less elegant, though. I also wonder if you could remove the control and make a new one at runtime but I guess that might be a bit expensive. – Jean Azzopardi Jun 02 '09 at 02:28
  • I actually added that class to my project and it didn't work at all. So now I guess I have to try the restarting the process approach. – Proximo Jun 02 '09 at 02:44
  • 16
    link is invalid now. So yes, nearly two years later, copying and pasting turned out to be the right thing to do :) – Courtney Christensen Mar 28 '11 at 22:31
  • 2
    @zourtney, heh it paid off in the end! – Jean Azzopardi Mar 29 '11 at 11:54
  • i've been doing this (different code to yours) for years, no need to restart, no need to use msinet.ocx (Ms Internet Controls)... if you still want the code, let me know and i can show you how to do it. in fact, i can even make it work with Visual Studio 6.0 :). – Erx_VB.NExT.Coder Jan 25 '12 at 18:18
  • 1
    Is there still a way to do this? Apparently the WPF-Variant of WebBrowser is sealed and can't be used this way. – Andreas Dec 31 '13 at 10:16
  • 1
    BeforeNavigate2 does not fire if control is hosted in .net application http://support.microsoft.com/kb/325079 – grzegorz_p Nov 11 '14 at 13:25
  • 2
    What is this `SHDocVw.WebBrowser xBrowser = (SHDocVw.WebBrowser)ActiveXInstance`?? – developerwjk Apr 11 '17 at 18:16
  • @Andreas you can `using System.Windows.Forms;` instead `using System.Windows.Controls;` – vinsa Jul 08 '18 at 17:47
23

Also, there is a refresh option in the function (according to MSDN). It worked well for me (you should set it before any user agent change). Then the question code could be changed like this:

[DllImport("urlmon.dll", CharSet = CharSet.Ansi)]
private static extern int UrlMkSetSessionOption(
    int dwOption, string pBuffer, int dwBufferLength, int dwReserved);

const int URLMON_OPTION_USERAGENT = 0x10000001;
const int URLMON_OPTION_USERAGENT_REFRESH = 0x10000002;

public void ChangeUserAgent()
{
    string ua = "Googlebot/2.1 (+http://www.google.com/bot.html)";

    UrlMkSetSessionOption(URLMON_OPTION_USERAGENT_REFRESH, null, 0, 0);
    UrlMkSetSessionOption(URLMON_OPTION_USERAGENT, ua, ua.Length, 0);
}
natenho
  • 5,231
  • 4
  • 27
  • 52
  • 1
    why are you not using the `userAgent` variable? – Sebastian Jan 08 '14 at 19:20
  • It's been extracted from my own code, shouldn't be there. Tks for pointing it out. – natenho Jan 09 '14 at 12:28
  • 2
    The accepted answer won't work if you are, for example, logging into Facebook and need to get permissions approval from the user ("renavigate" causes problems). So create your embedded browser and use URLMON_OPTION_USERAGENT_REFRESH then URLMON_OPTION_USERAGENT to set your custom UA. As pointed out above repeat as needed or only set URLMON_OPTION_USERAGENT_REFRESH to go back to the default IE UA. –  Apr 23 '14 at 16:28
  • 1
    Looking back I must say I liked this answer, too bad it's 4 years late :) – Proximo Mar 23 '15 at 06:26
  • So easy. Thank you! :) – Jack Jul 23 '15 at 17:22
  • 1
    just a note, if using forms, run before InitializeComponent(); – toster-cx Jan 23 '16 at 10:09
  • 1
    It works perfect on Windows 7 and 10 even with Guest account, tested. First call ChangeUserAgent() in your app, then you are ready to navigate with brand new UA. Repeat on every runtime. Be careful thoug - libraries like jquery could not work as expected because they use different methods for IE browsers. – vinsa Jul 09 '18 at 22:01
  • I think this should be the accepted answer; it's simpler, cleaner and there were less "side-effects" such as opening the IE setup window. For me anyway; I needed to pop up an SSO auth browser windows from with a class library (thanks oAuth2!). – SteveCinq May 02 '22 at 19:43
4

I'd like to add to @Jean Azzopardi's answer.

void BeforeNavigate(object pDisp, ref object url, ref object flags, ref object targetFrameName,
        ref object postData, ref object headers, ref bool cancel)
{
    // This alone is sufficient, because headers is a "Ref" parameters, and the browser seems to pick this back up.
    headers += string.Format("User-Agent: {0}\r\n", UserAgent);
}

This solution worked best for me. Using the renavigating caused other weird issues for me, like the browser content suddenly vanishing, and sometimes still getting Unsupported Browser. With this technique, all the requests in Fiddler had the correct User Agent.

harsimranb
  • 2,231
  • 1
  • 35
  • 55