How To: Set the proxy for the WebBrowser control in .NET


I see some horrible solutions for this out on the web.  Most involve setting the proxy for the entire machine and then toggling it back off.  Here is a class that will allow you to set it ONLY FOR THE PROCESS that the control is hosted in.  You could call it like this:

WinInetInterop.SetConnectionProxy(“localhost:8888”);

and then restore it to the default set in IE with this call:

// read the default settings for IE and restore these as the proxy
WinInetInterop.RestoreSystemProxy();
           

Let me know if you thought this was useful!

Listing:

Using System;
using System.Runtime.InteropServices;

namespace SetProxy
{
    public static class WinInetInterop
    {
        public static string applicationName;

            [DllImport(“wininet.dll”, SetLastError = true, CharSet = CharSet.Auto)]
            private static extern IntPtr InternetOpen(
                string lpszAgent, int dwAccessType, string lpszProxyName,
                string lpszProxyBypass, int dwFlags);

            [DllImport(“wininet.dll”, SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            private static extern bool InternetCloseHandle(IntPtr hInternet);

            [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
            private struct INTERNET_PER_CONN_OPTION_LIST
            {
                public int Size;

                // The connection to be set. NULL means LAN.
                public System.IntPtr Connection;

                public int OptionCount;
                public int OptionError;

                // List of INTERNET_PER_CONN_OPTIONs.
                public System.IntPtr pOptions;
            }
            private enum INTERNET_OPTION
            {
                // Sets or retrieves an INTERNET_PER_CONN_OPTION_LIST structure that specifies
                // a list of options for a particular connection.
                INTERNET_OPTION_PER_CONNECTION_OPTION = 75,

                // Notify the system that the registry settings have been changed so that
                // it verifies the settings on the next call to InternetConnect.
                INTERNET_OPTION_SETTINGS_CHANGED = 39,

                // Causes the proxy data to be reread from the registry for a handle.
                INTERNET_OPTION_REFRESH = 37

            }

            private enum INTERNET_PER_CONN_OptionEnum
            {
                INTERNET_PER_CONN_FLAGS = 1,
                INTERNET_PER_CONN_PROXY_SERVER = 2,
                INTERNET_PER_CONN_PROXY_BYPASS = 3,
                INTERNET_PER_CONN_AUTOCONFIG_URL = 4,
                INTERNET_PER_CONN_AUTODISCOVERY_FLAGS = 5,
                INTERNET_PER_CONN_AUTOCONFIG_SECONDARY_URL = 6,
                INTERNET_PER_CONN_AUTOCONFIG_RELOAD_DELAY_MINS = 7,
                INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_TIME = 8,
                INTERNET_PER_CONN_AUTOCONFIG_LAST_DETECT_URL = 9,
                INTERNET_PER_CONN_FLAGS_UI = 10
            }
            private const int INTERNET_OPEN_TYPE_DIRECT = 1;  // direct to net
            private const int INTERNET_OPEN_TYPE_PRECONFIG = 0; // read registry
            /// <summary>
            /// Constants used in INTERNET_PER_CONN_OPTON struct.
            /// </summary>
            private enum INTERNET_OPTION_PER_CONN_FLAGS
            {
                PROXY_TYPE_DIRECT = 0x00000001,   // direct to net
                PROXY_TYPE_PROXY = 0x00000002,   // via named proxy
                PROXY_TYPE_AUTO_PROXY_URL = 0x00000004,   // autoproxy URL
                PROXY_TYPE_AUTO_DETECT = 0x00000008   // use autoproxy detection
            }

            /// <summary>
            /// Used in INTERNET_PER_CONN_OPTION.
            /// When create a instance of OptionUnion, only one filed will be used.
            /// The StructLayout and FieldOffset attributes could help to decrease the struct size.
            /// </summary>
            [StructLayout(LayoutKind.Explicit)]
            private struct INTERNET_PER_CONN_OPTION_OptionUnion
            {
                // A value in INTERNET_OPTION_PER_CONN_FLAGS.
                [FieldOffset(0)]
                public int dwValue;
                [FieldOffset(0)]
                public System.IntPtr pszValue;
                [FieldOffset(0)]
                public System.Runtime.InteropServices.ComTypes.FILETIME ftValue;
            }

            [StructLayout(LayoutKind.Sequential)]
            private struct INTERNET_PER_CONN_OPTION
            {
                // A value in INTERNET_PER_CONN_OptionEnum.
                public int dwOption;
                public INTERNET_PER_CONN_OPTION_OptionUnion Value;
            }
            /// <summary>
            /// Sets an Internet option.
            /// </summary>
            [DllImport(“wininet.dll”, CharSet = CharSet.Ansi, SetLastError = true)]
            private static extern bool InternetSetOption(
                IntPtr hInternet,
                INTERNET_OPTION dwOption,
                IntPtr lpBuffer,
                int lpdwBufferLength);

            /// <summary>
            /// Queries an Internet option on the specified handle. The Handle will be always 0.
            /// </summary>
            [DllImport(“wininet.dll”, CharSet = CharSet.Ansi, SetLastError = true,
                EntryPoint = “InternetQueryOption”)]
            private extern static bool InternetQueryOptionList(
                IntPtr Handle,
                INTERNET_OPTION OptionFlag,
                ref INTERNET_PER_CONN_OPTION_LIST OptionList,
                ref int size);

            /// <summary>
            /// Set the proxy server for LAN connection.
            /// </summary>
            public static bool SetConnectionProxy(string proxyServer )
            {

                IntPtr hInternet = InternetOpen(applicationName,INTERNET_OPEN_TYPE_DIRECT, null, null, 0);

                //// Create 3 options.
                //INTERNET_PER_CONN_OPTION[] Options = new INTERNET_PER_CONN_OPTION[3];

                // Create 2 options.
                INTERNET_PER_CONN_OPTION[] Options = new INTERNET_PER_CONN_OPTION[2];

                // Set PROXY flags.
                Options[0] = new INTERNET_PER_CONN_OPTION();
                Options[0].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS;
                Options[0].Value.dwValue = (int)INTERNET_OPTION_PER_CONN_FLAGS.PROXY_TYPE_PROXY;

                // Set proxy name.
                Options[1] = new INTERNET_PER_CONN_OPTION();
                Options[1].dwOption =
                    (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_SERVER;
                Options[1].Value.pszValue = Marshal.StringToHGlobalAnsi(proxyServer);

                //// Set proxy bypass.
                //Options[2] = new INTERNET_PER_CONN_OPTION();
                //Options[2].dwOption =
                //    (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_BYPASS;
                //Options[2].Value.pszValue = Marshal.StringToHGlobalAnsi(“local”);

                //// Allocate a block of memory of the options.
                //System.IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(Options[0])
                //    + Marshal.SizeOf(Options[1]) + Marshal.SizeOf(Options[2]));

                // Allocate a block of memory of the options.
                System.IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(Options[0])
                    + Marshal.SizeOf(Options[1]));

                System.IntPtr current = buffer;

                // Marshal data from a managed object to an unmanaged block of memory.
                for (int i = 0; i < Options.Length; i++)
                {
                    Marshal.StructureToPtr(Options[i], current, false);
                    current = (System.IntPtr)((int)current + Marshal.SizeOf(Options[i]));
                }

                // Initialize a INTERNET_PER_CONN_OPTION_LIST instance.
                INTERNET_PER_CONN_OPTION_LIST option_list = new INTERNET_PER_CONN_OPTION_LIST();

                // Point to the allocated memory.
                option_list.pOptions = buffer;

                // Return the unmanaged size of an object in bytes.
                option_list.Size = Marshal.SizeOf(option_list);

                // IntPtr.Zero means LAN connection.
                option_list.Connection = IntPtr.Zero;

                option_list.OptionCount = Options.Length;
                option_list.OptionError = 0;
                int size = Marshal.SizeOf(option_list);

                // Allocate memory for the INTERNET_PER_CONN_OPTION_LIST instance.
                IntPtr intptrStruct = Marshal.AllocCoTaskMem(size);

                // Marshal data from a managed object to an unmanaged block of memory.
                Marshal.StructureToPtr(option_list, intptrStruct, true);

                // Set internet settings.
                bool bReturn = InternetSetOption(hInternet,
                    INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION, intptrStruct, size);

                // Free the allocated memory.
                Marshal.FreeCoTaskMem(buffer);
                Marshal.FreeCoTaskMem(intptrStruct);
                InternetCloseHandle(hInternet);

                // Throw an exception if this operation failed.
                if (!bReturn)
                {
                    throw new ApplicationException(” Set Internet Option Failed!”);
                }

                return bReturn;
            }

 

            /// <summary>
            /// Backup the current options for LAN connection.
            /// Make sure free the memory after restoration.
            /// </summary>
            private static INTERNET_PER_CONN_OPTION_LIST GetSystemProxy()
            {

                // Query following options.
                INTERNET_PER_CONN_OPTION[] Options = new INTERNET_PER_CONN_OPTION[3];

                Options[0] = new INTERNET_PER_CONN_OPTION();
                Options[0].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_FLAGS;
                Options[1] = new INTERNET_PER_CONN_OPTION();
                Options[1].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_SERVER;
                Options[2] = new INTERNET_PER_CONN_OPTION();
                Options[2].dwOption = (int)INTERNET_PER_CONN_OptionEnum.INTERNET_PER_CONN_PROXY_BYPASS;

                // Allocate a block of memory of the options.
                System.IntPtr buffer = Marshal.AllocCoTaskMem(Marshal.SizeOf(Options[0])
                    + Marshal.SizeOf(Options[1]) + Marshal.SizeOf(Options[2]));

                System.IntPtr current = (System.IntPtr)buffer;

                // Marshal data from a managed object to an unmanaged block of memory.
                for (int i = 0; i < Options.Length; i++)
                {
                    Marshal.StructureToPtr(Options[i], current, false);
                    current = (System.IntPtr)((int)current + Marshal.SizeOf(Options[i]));
                }

                // Initialize a INTERNET_PER_CONN_OPTION_LIST instance.
                INTERNET_PER_CONN_OPTION_LIST Request = new INTERNET_PER_CONN_OPTION_LIST();

                // Point to the allocated memory.
                Request.pOptions = buffer;

                Request.Size = Marshal.SizeOf(Request);

                // IntPtr.Zero means LAN connection.
                Request.Connection = IntPtr.Zero;

                Request.OptionCount = Options.Length;
                Request.OptionError = 0;
                int size = Marshal.SizeOf(Request);

                // Query internet options.
                bool result = InternetQueryOptionList(IntPtr.Zero,
                    INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION,
                    ref Request, ref size);
                if (!result)
                {
                    throw new ApplicationException(” Set Internet Option Failed! “);
                }

                return Request;
            }

            /// <summary>
            /// Restore the options for LAN connection.
            /// </summary>
            /// <param name=”request”></param>
            /// <returns></returns>
            public static bool RestoreSystemProxy()
            {

                IntPtr hInternet = InternetOpen(applicationName, INTERNET_OPEN_TYPE_DIRECT, null, null, 0);

                INTERNET_PER_CONN_OPTION_LIST request = GetSystemProxy();
                int size = Marshal.SizeOf(request);

                // Allocate memory.
                IntPtr intptrStruct = Marshal.AllocCoTaskMem(size);

                // Convert structure to IntPtr
                Marshal.StructureToPtr(request, intptrStruct, true);

                // Set internet options.
                bool bReturn = InternetSetOption(hInternet,
                    INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION,
                    intptrStruct, size);

                // Free the allocated memory.
                Marshal.FreeCoTaskMem(request.pOptions);
                Marshal.FreeCoTaskMem(intptrStruct);

                if (!bReturn)
                {
                    throw new ApplicationException(” Set Internet Option Failed! “);
                }

                // Notify the system that the registry settings have been changed and cause
                // the proxy data to be reread from the registry for a handle.
                InternetSetOption(hInternet, INTERNET_OPTION.INTERNET_OPTION_SETTINGS_CHANGED,
                    IntPtr.Zero, 0);
                InternetSetOption(hInternet, INTERNET_OPTION.INTERNET_OPTION_REFRESH,
                    IntPtr.Zero, 0);

                InternetCloseHandle(hInternet);

                return bReturn;
            }

    }
}


Comments (28)

  1. Great code!! thanks!

  2. Hi!

    Let me check this solution.

    If this work then this will be great!.

    I am searching for this solution for week,s and did not found a single solution!

  3. Thanks!

    It works.Will you please  clarify it,s explanation(Comment the working of whole code  in beginning the flow of whole code ).

    It works but it is not understandable.

    Thanks!

  4. jpsanders says:

    Sure, Let me know if the explanation helps!

    You could call it like this:

    WinInetInterop.SetConnectionProxy(“localhost:8888”);

    and then restore it to the default set in IE with this call:

    // read the default settings for IE and restore these as the proxy
    WinInetInterop.RestoreSystemProxy();

     

    Just about every line in these functions is commented.  Is there any confusion there?

  5. Mahesh says:

    if you search on google you won't find this page .

    I found your code used at 1code.codeplex.com/…/62253

    and it's working superb they have given credits to you!!!

    Many Many thinks!!

  6. jpsanders says:

    Mahesh,

    I am glad the code helped!  If you search with http://bing.com do you find this article :-).  What are the search terms you used (so I can add it to this post)?

    -Jeff

  7. lifeinsuranceph says:

    jpsanders, thank you for your excellent article.

    From my understanding of your code, the proxy is set for the current process, right? Taking it further, perhaps you can answer these:

    1. Is it possible to launch multiple threads with each thread on a separate proxy server from the same application?

    2. is it possible to have multiple proxies running on the process but using multiple WebBrowser control instances? (e.g. a form has three browser controls with each control using a different proxy server)

    If so, I hope you can share some code.

    Thanks

    http://www.lifeinsuranceph.com

  8. jpsanders says:

    Hi Life,

    1. No

    2. No

    The WebBrowser controls will all share WinINet.dll where all of this is controlled from.

    -Jeff

  9. Kallol says:

    Hi,

    Thx for this. This is too good. How do I change your code to support accessing SSL sites. e.g https://mail.yahoo.com. Currently it gives a navigation cancelled error.

    Regards.

    Kallol

  10. jpsanders says:

    SSL sites should be no different.  This sounds like you have a proxy problem.  What happens when you set the proxy manually in IE with the same proxy value.  Can you get to that URL then?

  11. ZAX says:

    I got "Set Internet Option Failed!" error.

    How can I find what the problem is?

  12. jpsanders says:

    Hi ZAX,

    What OS are you running on and what version of IE.  You are not trying to run this in a service or non-interactive account are you?

    -Jeff

  13. ZAX says:

    Hi Jeff!

    I'm running a recently installed Win 7  SP1 with the default IE 9 in it – I use Chrome otherwise so never updated anything in IE.

    I see that this

    bool bReturn = InternetSetOption(hInternet,

                       INTERNET_OPTION.INTERNET_OPTION_PER_CONNECTION_OPTION, intptrStruct, size);

    generates the exception but I don't know how to troubleshoot the problem further cause I don't really understand working with wininet.dll.

    I tried to test with a private proxy with this command:

    WinInetInterop.SetConnectionProxy("proxyuser:proxypass@ip:port");

    my aim is to be able to change the proxy every few minutes for the browser control of my WinForms app in C#

    thx for Your attention

    ZAX

  14. jpsanders says:

    Hi ZAX,

    You will want to get the last error when this fails.  Use this function: msdn.microsoft.com/…/system.runtime.interopservices.marshal.getlastwin32error.aspx

    What is the error you get from this?

  15. ZAX says:

    Hi Jeff!

    I posted back on Friday already but maybe i had misclicked sg as it never appeared so I try again:

    I got back the 87 "wrong parameter error" from Marshal.GetLastWin32Error.

    Immediately after that I tried the code using a public proxy in a simple format of "ip:port".

    and it worked!

    So the only question is if this code can handle private proxy credentials too?

    And if yes, what is the exact format of the user/pass/ip/port that I have to feed in?

    (so far I used user:pass@ip:port but this always brings up the wrong parameter error)

    I think it would be useful for all to have this info.

    thx

    ZAX

  16. jpsanders says:

    Hi ZAX,  

    What type of authentication does your proxy provide?  You could always use straight WinINet functions to do proxy auth once you get the 407 response from the proxy.  Here is an article on that (no I do not have a .NET sample but you could write one I am sure): msdn.microsoft.com/…/aa384220(v=vs.85).aspx

    -Jeff

  17. Ravinder Singh, PMP says:

    I was looking for such a solution for one of my ClickOnce application and your code just did the trick! Just wanted to say a big thank you to you for posting this.

    Keep it up!

  18. Arif says:

    Hi,

    I have a question regarding the usage of your "WinInetInterop" class. I want to set the proxy settings of my Windows form application as settings for IE, the method you wrote and which i can use if RestoreProxySettings(),

    I am a beginner but need this application to work, could you please tell me where should I call this method, I mean in the Onload_form method or in Contructor and whether to take some extra steps to achieve this task.

    A quick reply will be appreciated.

  19. jpsanders says:

    Hi Arif,

    You cannot set the proxy for your WinForms application.  You can however set the proxy for the Internet Explorer web browser control.  Is that what you are trying to do?

    -Jeff

  20. Arif says:

    Let me be more in detail. I have some application which uses Trident (the web engine for IE). And now I have some styling issues with html pages, and this web engine  is not enough. So I want to introduce a different web engine like webkit (the web engine for Chrome) because the displaying problems are solved by webkit engine.

    In order to do this I have to test Webkit with my application (Windows Form application). And So I want to Copy the proxy settings for IE, and than use it as proxy setting for my application in order to check that the new web engine works fine and than embed it permanently in my application.

    I hope I have made it clear what I mean.  I am stuck with this problem since days and your method could solve this if used property.

    Thanks in advance ,

  21. jpsanders says:

    Hi Arif,

    This will not do what you want.  You need to first discover how your web engine uses/gets the proxy.  This solution is for WinINet based solutions only and only if you want to temporarily set a proxy DIFFERENT from the default proxy.  You should engage WebKit support or forums to see how this is done.  I am sure that it can already read the default IE proxy settings some how!

    -Jeff

  22. Z says:

    thanks for your code

  23. Jesse W. says:

    This is fantastic! Thanks so much for making this awesome contribution to the community. I, and many others, very much appreciate it.

  24. Hill says:

    Can I ask the variable  string applicationName; is the name of the application that we want to change the web browser control proxy. For example, my application is "SomeName" then I may be set applicationName = SomeName?

    Thanks

  25. jpsanders says:

    Hi Hill,

    Take a look at the documentation for the InternetOpen function (they describe it adequately) : msdn.microsoft.com/…/aa385096(v=vs.85).aspx

    Jeff

  26. Hill says:

    Hi Jeff,

    Thanks so much, I understood.

  27. someone says:

    thankyou very much your a hero