Cyotek Development Bloghttps://devblog.cyotek.com/tag/webbrowser/atom.xml2019-01-20T19:17:34ZReading cookies from Internet Explorerurn:uuid:8c68829f-767f-485e-84f2-b089ee8d20c12019-01-20T19:17:34Z2019-01-20T19:17:34Z<p>In order to work around cases where it wasn't possible
automatically authenticate with a website, I wanted the ability
to use an embedded Internet Explorer window for manual
authentication and then reuse the cookies. This article
describes how to read cookies indirectly using
<code>InternetGetCookieEx</code> or directly from a <code>WebBrowser</code> control.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/getcookies-1b.png" class="gallery" title="Reading cookies via InternetGetCookieEx" ><img src="https://images.cyotek.com/image/thumbnail/devblog/getcookies-1b.png" alt="Reading cookies via InternetGetCookieEx" decoding="async" loading="lazy" /></a><figcaption>Reading cookies via InternetGetCookieEx</figcaption></figure><h2 id="reading-cookies">Reading cookies</h2>
<p>The <a href="https://docs.microsoft.com/en-us/windows/desktop/api/wininet/nf-wininet-internetgetcookieexw" rel="external nofollow noopener"><code>InternetGetCookieEx</code></a> Win32 API can be used to read
cookie name value pairs for a given URI. You can use it to read
all cookies or a named cookie. You can also specify which type
of cookies to include.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
BOOLAPI InternetGetCookieExW<span class="symbol">(</span>
 LPCWSTR lpszUrl<span class="symbol">,</span>
 LPCWSTR lpszCookieName<span class="symbol">,</span>
 LPWSTR lpszCookieData<span class="symbol">,</span>
 LPDWORD lpdwSize<span class="symbol">,</span>
 DWORD dwFlags<span class="symbol">,</span>
 LPVOID lpReserved
<span class="symbol">)</span><span class="symbol">;</span>
</pre>
</figure>
<p>To use this function, we specify the URL we want to query, the
name of the cookie to lookup (or <code>null</code> for all), a buffer to
store the cookie data, the length of the buffer and then flags
to describe what we want returned. The function returns <code>true</code>
if it succeeds, otherwise <code>false</code>. In line with other Win32
calls, you can use <code>GetLastError</code> to get the error code in the
event of a failure.</p>
<p>The last part is quite important as if the buffer provided is
too small, the function will fail and calling <code>GetLastError</code>
will return <code>ERROR_INSUFFICIENT_BUFFER</code>. In this case, the
buffer length parameter will contain the size required to
contain the data so that you can allocate a new buffer and call
the function again.</p>
<p>I saw a number of naïve implementations on the internet that
called <code>InternetGetCookieEx</code> without doing any sort of error
checking; this could lead to subtle bugs in the event that the
cookies are longer than the supplied buffer as no data will be
returned.</p>
<p>The following function can be used to get all cookies for a
given URI. I create a buffer that will hold 1024 characters and
make a call to <code>InternetGetCookieEx</code>. If it fails but the error
code is <code>ERROR_INSUFFICIENT_BUFFER</code> then I increase the buffer
size and try again.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="symbol">[</span>DllImport<span class="symbol">(</span><span class="string">&quot;wininet.dll&quot;</span><span class="symbol">,</span> CharSet <span class="symbol">=</span> CharSet<span class="symbol">.</span>Auto<span class="symbol">,</span> SetLastError <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">]</span>
<span class="keyword">static</span> <span class="keyword">extern</span> <span class="keyword">bool</span> InternetGetCookieEx<span class="symbol">(</span><span class="keyword">string</span> lpszUrl<span class="symbol">,</span> <span class="keyword">string</span> lpszCookieName<span class="symbol">,</span> StringBuilder lpszCookieData<span class="symbol">,</span> <span class="keyword">ref</span> <span class="keyword">int</span> lpdwSize<span class="symbol">,</span> <span class="keyword">int</span> dwFlags<span class="symbol">,</span> IntPtr lpReserved<span class="symbol">)</span><span class="symbol">;</span>

<span class="keyword">const</span> <span class="keyword">int</span> ERROR_INSUFFICIENT_BUFFER <span class="symbol">=</span> <span class="number">122</span><span class="symbol">;</span>

<span class="keyword">const</span> <span class="keyword">int</span> INTERNET_COOKIE_HTTPONLY <span class="symbol">=</span> <span class="number">0x00002000</span><span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> GetCookies<span class="symbol">(</span><span class="keyword">string</span> uri<span class="symbol">)</span>
<span class="symbol">{</span>
 StringBuilder buffer<span class="symbol">;</span>
 <span class="keyword">string</span> result<span class="symbol">;</span>
 <span class="keyword">int</span> bufferLength<span class="symbol">;</span>
 <span class="keyword">int</span> flags<span class="symbol">;</span>

 bufferLength <span class="symbol">=</span> <span class="number">1024</span><span class="symbol">;</span>
 buffer <span class="symbol">=</span> <span class="keyword">new</span> StringBuilder<span class="symbol">(</span>bufferLength<span class="symbol">)</span><span class="symbol">;</span>

 flags <span class="symbol">=</span> INTERNET_COOKIE_HTTPONLY<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>InternetGetCookieEx<span class="symbol">(</span>uri<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> buffer<span class="symbol">,</span> <span class="keyword">ref</span> bufferLength<span class="symbol">,</span> flags<span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> buffer<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="keyword">null</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>Marshal<span class="symbol">.</span>GetLastWin<span class="number">32</span>Error<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">==</span> ERROR_INSUFFICIENT_BUFFER<span class="symbol">)</span>
 <span class="symbol">{</span>
 buffer<span class="symbol">.</span>Length <span class="symbol">=</span> bufferLength<span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>NativeMethods<span class="symbol">.</span>InternetGetCookieEx<span class="symbol">(</span>uri<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">,</span> buffer<span class="symbol">,</span> <span class="keyword">ref</span> bufferLength<span class="symbol">,</span> flags<span class="symbol">,</span> IntPtr<span class="symbol">.</span>Zero<span class="symbol">)</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> buffer<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<blockquote>
<p>In the example above, I'm requesting to include HTTP only
cookies. The documentation states: &quot;Do not use this flag if
you expose a scriptable interface, because this has security
implications. It is imperative that you use this flag only if
you can guarantee that you will never expose the cookie to
third-party code by way of an extensibility mechanism you
provide.&quot;</p>
</blockquote>
<p>According to the documentation, the <code>lpdwSize</code> parameter will be
updated to specify the number of <em>bytes</em> required to hold the
value, however, I'm creating a buffer that uses that value in
<em>chars</em> to keep it simple.</p>
<h2 id="using-the-webbrowser-control-directly">Using the WebBrowser control directly</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/getcookies-1a.png" class="gallery" title="Reading cookies using both InternetGetCookieEx and an embedded WebBrowser control" ><img src="https://images.cyotek.com/image/thumbnail/devblog/getcookies-1a.png" alt="Reading cookies using both InternetGetCookieEx and an embedded WebBrowser control" decoding="async" loading="lazy" /></a><figcaption>Reading cookies using both InternetGetCookieEx and an embedded WebBrowser control</figcaption></figure>
<p>While using <code>InternetGetCookieEx</code> would be my preferred
approach, if you already have a <code>WebBrowser</code> control you can
read the cookies directly from the control. The <code>Document</code>
property of a <code>WebBrowser</code> control returns a <code>HtmlDocument</code> that
in turn contains the <code>Cookie</code> property. The format of the
returned data matches that of <code>InternetGetCookieEx</code>.</p>
<blockquote>
<p>There is one important difference between using
<code>InternetGetCookieEx</code> and <code>HtmlDocument.Cookie</code> - the later
<strong>does not</strong> include HTTP only cookies. If you require access
to all cookies for a given URI, then use <code>InternetGetCookieEx</code>
instead.</p>
</blockquote>
<h2 id="what-about-the-cookie-properties">What about the cookie properties?</h2>
<p>The methods describe above only seem to return the names and
values of cookies, excluding properties such as paths and expiry
dates. Unfortunately I don't know of a method of accessing these
extended properties in modern versions of Windows.</p>
<h2 id="using-the-cookies">Using the cookies</h2>
<p>As Windows and browsers have evolved, there is probably little
use for this particular API call. Internet Explorer is obsolete
and third party browsers (and Edge) use their own cookie storage
mechanisms that this API cannot access. Speaking for myself, the
only use I have for this technique is to make custom requests to
an internet resource after using an embedded IE window to
authenticate with a website.</p>
<p>The following example demonstrates how you could assign cookies
to a <code>HttpWebRequest</code>. Note that the <code>SetCookies</code> method of the
<code>CookieContainer</code> class requires the cookies to be comma
separated, rather than the semi-colon seperated values returned
by the above function.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
CookieContainer cookies<span class="symbol">;</span>
Uri uri<span class="symbol">;</span>
<span class="keyword">string</span> cookieData<span class="symbol">;</span>

uri <span class="symbol">=</span> <span class="keyword">new</span> Uri<span class="symbol">(</span><span class="string">&quot;https://demo.cyotek.com/features/cookies.php&quot;</span><span class="symbol">)</span><span class="symbol">;</span>

cookieData <span class="symbol">=</span> GetCookies<span class="symbol">(</span>uri<span class="symbol">)</span><span class="symbol">;</span>

cookies <span class="symbol">=</span> <span class="keyword">new</span> CookieContainer<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
cookies<span class="symbol">.</span>SetCookies<span class="symbol">(</span>uri<span class="symbol">,</span> cookieData<span class="symbol">.</span>Replace<span class="symbol">(</span><span class="string">&quot;; &quot;</span><span class="symbol">,</span> <span class="string">&quot;,&quot;</span><span class="symbol">)</span><span class="symbol">)</span><span class="symbol">;</span>

HttpWebRequest request<span class="symbol">;</span>

request <span class="symbol">=</span> WebRequest<span class="symbol">.</span>CreateHttp<span class="symbol">(</span>uri<span class="symbol">)</span><span class="symbol">;</span>
request<span class="symbol">.</span>CookieContainer <span class="symbol">=</span> cookies<span class="symbol">;</span>
</pre>
</figure>
<h2 id="demonstration-program">Demonstration program</h2>
<p>The attached demonstration program includes a complete sample
for reading cookies either via <code>InternetGetCookieEx</code> or from a
<code>WebBrowser</code> control, and is also available from our <a href="https://github.com/cyotek/InternetGetCookieExDemo" rel="external nofollow noopener">GitHub
page</a>.</p>
<h2 id="postscript">Postscript</h2>
<p>I originally wrote this demonstration back in October 2018 but I
delayed it while trying to find out a solution to the missing
properties. I was also curious as to why Edge didn't seem to use
the same system and had a vague idea looking into that. Then
came the news that Microsoft have decided to <a href="https://blogs.windows.com/windowsexperience/2018/12/06/microsoft-edge-making-the-web-better-through-more-open-source-collaboration/" rel="external nofollow noopener">drop their custom
Edge engine and use Chromium instead</a>.</p>
<p>This seems astonishingly short sighted to me and is a real
disappointment.</p>
<p>While I confess I don't use Edge as my primary browser (Firefox
is my daily driver due to its extension support and excellent
developer tools), I found it to be a decent browser which didn't
let me down when I did use it. To me it is perplexing that they
are essentially ceding control to Google. It's like Windows
Phone all over again and I <strong>like</strong> my Windows Phone. It is also
frustrating as currently you can get a <em>reasonable</em> browsing
experience by embedding Internet Explorer via the <code>WebBrowser</code>
control into your application (with a <a href="/post/configuring-the-emulation-mode-of-an-internet-explorer-webbrowser-control">little help</a>) without
having to worry about shipping any dependencies and I had hopes
of doing the same with Edge for a truly modern experience.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2019-01-20 - First published</li>
<li>2020-11-22 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/reading-cookies-from-internet-explorer .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.comConfiguring the emulation mode of an Internet Explorer WebBrowser controlurn:uuid:66dfda59-d846-47d7-9dce-5c19ca3a15ef2014-06-28T09:47:00Z2014-06-28T09:47:00Z<p>Occasionally I need to embed HTML in my applications. If it is
just to display some simple layout with basic interactions, I
might use a component such as <a href="https://github.com/ArthurHub/HTML-Renderer" rel="external nofollow noopener">HtmlRenderer</a>. In most cases
however, I need a more complex layout, JavaScript or I might
want to display real pages from the internet - in which case I'm
lumbered with the <code>WebBrowser</code> control.</p>
<blockquote>
<p>I'm aware other embeddable browsers exist, but the idea of
shipping additional multi-MB dependencies doesn't make sense
unless an application makes heavy use of HTML interfaces</p>
</blockquote>
<p>The <code>WebBrowser</code> control annoys me in myriad ways, but it does
get the job done. One of the things that occasionally frustrates
me is that by default it is essentially an embedded version of
Internet Explorer 7 - or enabling Compatibility Mode in a modern
IE session. Not so good as more and more sites use HTML5 and
other goodies.</p>
<p>Rather fortunately however, Microsoft provide the ability to
configure the emulation mode your application will use. It's not
as simple as setting some properties on a control as it involves
setting some registry values and other caveats, but it is still
a reasonable process.</p>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/browseremulation-1a.png" class="gallery" title="Do you really want to greet your users with script errors when displaying modern websites in the WebBrowser control?" ><img src="https://images.cyotek.com/image/thumbnail/devblog/browseremulation-1a.png" alt="Do you really want to greet your users with script errors when displaying modern websites in the WebBrowser control?" decoding="async" loading="lazy" /></a><figcaption>Do you really want to greet your users with script errors when displaying modern websites in the WebBrowser control?</figcaption></figure><h2 id="about-browser-emulation-versions">About browser emulation versions</h2>
<p>The table below (<a href="http://msdn.microsoft.com/en-us/library/ee330730(v=vs.85).aspx#browser_emulation" rel="external nofollow noopener">source</a>) lists the currently supported
emulation versions at the time of writing. As you can see, it's
possible to emulate all &quot;recent&quot; versions of Internet Explorer
in one of two ways - either by forcing a standards mode, or
allowing <code>!DOCTYPE</code> directives to control the mode. The
exception to this dual behaviour is version 7 which is as is.</p>
<p>According to the documentation the IE8 (8000) and IE9 (9000) modes will switch to IE10 (10000) mode if installed. The documentation doesn't mention if this is still the case regarding IE11 so I'm not sure on the behaviour in that regard.</p>
<table>
<thead>
<tr>
<th style="text-align: right;">Value</th>
<th>Description</th>
</tr>
</thead>
<tbody>
<tr>
<td style="text-align: right;">11001</td>
<td>Internet Explorer 11. Webpages are displayed in IE11 edge mode, regardless of the !DOCTYPE directive.</td>
</tr>
<tr>
<td style="text-align: right;">11000</td>
<td>IE11. Webpages containing standards-based !DOCTYPE directives are displayed in IE11 edge mode. Default value for IE11.</td>
</tr>
<tr>
<td style="text-align: right;">10001</td>
<td>Internet Explorer 10. Webpages are displayed in IE10 Standards mode, regardless of the !DOCTYPE directive.</td>
</tr>
<tr>
<td style="text-align: right;">10000</td>
<td>Internet Explorer 10. Webpages containing standards-based !DOCTYPE directives are displayed in IE10 Standards mode. Default value for Internet Explorer 10.</td>
</tr>
<tr>
<td style="text-align: right;">9999</td>
<td>Windows Internet Explorer 9. Webpages are displayed in IE9 Standards mode, regardless of the !DOCTYPE directive.</td>
</tr>
<tr>
<td style="text-align: right;">9000</td>
<td>Internet Explorer 9. Webpages containing standards-based !DOCTYPE directives are displayed in IE9 mode. Default value for Internet Explorer 9.</td>
</tr>
<tr>
<td style="text-align: right;">8888</td>
<td>Webpages are displayed in IE8 Standards mode, regardless of the !DOCTYPE directive.</td>
</tr>
<tr>
<td style="text-align: right;">8000</td>
<td>Webpages containing standards-based !DOCTYPE directives are displayed in IE8 mode. Default value for Internet Explorer 8</td>
</tr>
<tr>
<td style="text-align: right;">7000</td>
<td>Webpages containing standards-based !DOCTYPE directives are displayed in IE7 Standards mode. Default value for applications hosting the WebBrowser Control.</td>
</tr>
</tbody>
</table>
<h2 id="setting-the-browser-emulation-version">Setting the browser emulation version</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/browseremulation-1c.png" class="gallery" title="Browser emulation support is configured in the Registry" ><img src="https://images.cyotek.com/image/thumbnail/devblog/browseremulation-1c.png" alt="Browser emulation support is configured in the Registry" decoding="async" loading="lazy" /></a><figcaption>Browser emulation support is configured in the Registry</figcaption></figure>
<p>Setting the emulation version is very straightforward - add a
value to the registry in the below key containing the name of
your executable file and a value from the table above.</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
HKEY_LOCAL_MACHINE (or HKEY_CURRENT_USER)
 SOFTWARE
 Microsoft
 Internet Explorer
 Main
 FeatureControl
 FEATURE_BROWSER_EMULATION
 yourapp.exe = (DWORD) version
</pre>
</figure>
<blockquote>
<p>Note: If you do this from an application you're debugging
using Visual Studio and the Visual Studio Hosting Process
option is enabled you'll find the executable name may not be
what you expect. When enabled, a stub process with a slightly
modified name is used instead. For example, if your
application is named <code>calc.exe,</code> you'll need to add the value
<code>calc.vshost.exe</code> in order to set the emulated version for the
correct process.</p>
</blockquote>
<h2 id="getting-the-internet-explorer-version">Getting the Internet Explorer version</h2>
<p>As it makes more sense to detect the version of IE installed on
the user's computer and set the emulation version to match,
first we need a way of detecting the IE version.</p>
<p>There are various ways of getting the installed IE version, but
the sensible method is reading the value from the registry as
everything else we are doing in this article involves the
registry in some fashion.</p>
<figure class="lang-text highlight"><figcaption><span>text</span></figcaption><pre class="code">
HKEY_LOCAL_MACHINE
 SOFTWARE
 Microsoft
 Internet Explorer
 svcVersion or Version
</pre>
</figure>
<p>Older versions of IE used the <code>Version</code> value, while newer
versions use <code>svcVersion</code>. In either case, this value contains
the version string.</p>
<p>We can use the following version to pull out the major digit.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">string</span> InternetExplorerRootKey <span class="symbol">=</span> <span class="string">@&quot;Software\Microsoft\Internet Explorer&quot;</span><span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">int</span> GetInternetExplorerMajorVersion<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> result<span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="number">0</span><span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 RegistryKey key<span class="symbol">;</span>

 key <span class="symbol">=</span> Registry<span class="symbol">.</span>LocalMachine<span class="symbol">.</span>OpenSubKey<span class="symbol">(</span>InternetExplorerRootKey<span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>key <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">object</span> value<span class="symbol">;</span>

 value <span class="symbol">=</span> key<span class="symbol">.</span>GetValue<span class="symbol">(</span><span class="string">&quot;svcVersion&quot;</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span> <span class="symbol">??</span> key<span class="symbol">.</span>GetValue<span class="symbol">(</span><span class="string">&quot;Version&quot;</span><span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> version<span class="symbol">;</span>
 <span class="keyword">int</span> separator<span class="symbol">;</span>

 version <span class="symbol">=</span> value<span class="symbol">.</span>ToString<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
 separator <span class="symbol">=</span> version<span class="symbol">.</span>IndexOf<span class="symbol">(</span><span class="string">&#39;.&#39;</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>separator <span class="symbol">!=</span> <span class="symbol">-</span><span class="number">1</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">int</span><span class="symbol">.</span>TryParse<span class="symbol">(</span>version<span class="symbol">.</span>Substring<span class="symbol">(</span><span class="number">0</span><span class="symbol">,</span> separator<span class="symbol">)</span><span class="symbol">,</span> <span class="keyword">out</span> result<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>SecurityException<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// The user does not have the permissions required to read from the registry key.</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>UnauthorizedAccessException<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// The user does not have the necessary registry rights.</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h3 id="points-to-note">Points to note</h3>
<ul>
<li>I'm returning an <code>int</code> with the major version component rather
a <code>Version</code> class. In this example, I don't need a full
version to start with and it avoids crashes if the version
string is invalid</li>
<li>For the same reason, I'm explicitly catching (and ignoring)
<code>SecurityException</code> and <code>UnauthorizedAccessException</code>
exceptions which will be thrown if the user doesn't have
permission to access those keys. Again, I don't really want
the function crashing for those reasons.</li>
</ul>
<p>You can always remove the <code>try</code> block to have all exceptions
thrown instead of the access exceptions being ignored.</p>
<h2 id="getting-the-browser-emulation-version">Getting the browser emulation version</h2>
<figure class="screenshot" ><a href="https://images.cyotek.com/image/devblog/browseremulation-1b.png" class="gallery" title="By using Internet Explorer's browser emulation support, we can choose how the WebControl behaves" ><img src="https://images.cyotek.com/image/thumbnail/devblog/browseremulation-1b.png" alt="By using Internet Explorer's browser emulation support, we can choose how the WebControl behaves" decoding="async" loading="lazy" /></a><figcaption>By using Internet Explorer's browser emulation support, we can choose how the WebControl behaves</figcaption></figure>
<blockquote>
<p>The functions to get and set the emulation version are using
<code>HKEY_CURRENT_USER</code> to make them per user rather than for the
entire machine.</p>
</blockquote>
<p>First we'll create an enumeration to handle the different
versions described above so that we don't have to deal with
magic numbers.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">enum</span> BrowserEmulationVersion
<span class="symbol">{</span>
 Default <span class="symbol">=</span> <span class="number">0</span><span class="symbol">,</span>
 Version<span class="number">7</span> <span class="symbol">=</span> <span class="number">7000</span><span class="symbol">,</span>
 Version<span class="number">8</span> <span class="symbol">=</span> <span class="number">8000</span><span class="symbol">,</span>
 Version<span class="number">8</span>Standards <span class="symbol">=</span> <span class="number">8888</span><span class="symbol">,</span>
 Version<span class="number">9</span> <span class="symbol">=</span> <span class="number">9000</span><span class="symbol">,</span>
 Version<span class="number">9</span>Standards <span class="symbol">=</span> <span class="number">9999</span><span class="symbol">,</span>
 Version<span class="number">10</span> <span class="symbol">=</span> <span class="number">10000</span><span class="symbol">,</span>
 Version<span class="number">10</span>Standards <span class="symbol">=</span> <span class="number">10001</span><span class="symbol">,</span>
 Version<span class="number">11</span> <span class="symbol">=</span> <span class="number">11000</span><span class="symbol">,</span>
 Version<span class="number">11</span>Edge <span class="symbol">=</span> <span class="number">11001</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>Next, a function to detect the current emulation version in use
by our application, and another to quickly tell if an emulation
version has previously been set.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">private</span> <span class="keyword">const</span> <span class="keyword">string</span> BrowserEmulationKey <span class="symbol">=</span> InternetExplorerRootKey <span class="symbol">+</span> <span class="string">@&quot;\Main\FeatureControl\FEATURE_BROWSER_EMULATION&quot;</span><span class="symbol">;</span>

<span class="keyword">public</span> <span class="keyword">static</span> BrowserEmulationVersion GetBrowserEmulationVersion<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 BrowserEmulationVersion result<span class="symbol">;</span>

 result <span class="symbol">=</span> BrowserEmulationVersion<span class="symbol">.</span>Default<span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 RegistryKey key<span class="symbol">;</span>

 key <span class="symbol">=</span> Registry<span class="symbol">.</span>CurrentUser<span class="symbol">.</span>OpenSubKey<span class="symbol">(</span>BrowserEmulationKey<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="keyword">if</span> <span class="symbol">(</span>key <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> programName<span class="symbol">;</span>
 <span class="keyword">object</span> value<span class="symbol">;</span>

 programName <span class="symbol">=</span> Path<span class="symbol">.</span>GetFileName<span class="symbol">(</span>Environment<span class="symbol">.</span>GetCommandLineArgs<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>
 value <span class="symbol">=</span> key<span class="symbol">.</span>GetValue<span class="symbol">(</span>programName<span class="symbol">,</span> <span class="keyword">null</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>value <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 result <span class="symbol">=</span> <span class="symbol">(</span>BrowserEmulationVersion<span class="symbol">)</span>Convert<span class="symbol">.</span>ToInt<span class="number">32</span><span class="symbol">(</span>value<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>SecurityException<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// The user does not have the permissions required to read from the registry key.</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>UnauthorizedAccessException<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// The user does not have the necessary registry rights.</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">bool</span> IsBrowserEmulationSet<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">return</span> GetBrowserEmulationVersion<span class="symbol">(</span><span class="symbol">)</span> <span class="symbol">!=</span> BrowserEmulationVersion<span class="symbol">.</span>Default<span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<h2 id="setting-the-emulation-version">Setting the emulation version</h2>
<p>And finally, we need to be able to set the emulation version.
I've provided two functions for doing this, one which allows you
to explicitly set a value, and another that uses the best
matching value for the installed version of Internet Explorer.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">bool</span> SetBrowserEmulationVersion<span class="symbol">(</span>BrowserEmulationVersion browserEmulationVersion<span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">bool</span> result<span class="symbol">;</span>

 result <span class="symbol">=</span> <span class="keyword">false</span><span class="symbol">;</span>

 <span class="keyword">try</span>
 <span class="symbol">{</span>
 RegistryKey key<span class="symbol">;</span>

 key <span class="symbol">=</span> Registry<span class="symbol">.</span>CurrentUser<span class="symbol">.</span>OpenSubKey<span class="symbol">(</span>BrowserEmulationKey<span class="symbol">,</span> <span class="keyword">true</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>key <span class="symbol">!=</span> <span class="keyword">null</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">string</span> programName<span class="symbol">;</span>

 programName <span class="symbol">=</span> Path<span class="symbol">.</span>GetFileName<span class="symbol">(</span>Environment<span class="symbol">.</span>GetCommandLineArgs<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">[</span><span class="number">0</span><span class="symbol">]</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>browserEmulationVersion <span class="symbol">!=</span> BrowserEmulationVersion<span class="symbol">.</span>Default<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// if it&#39;s a valid value, update or create the value</span>
 key<span class="symbol">.</span>SetValue<span class="symbol">(</span>programName<span class="symbol">,</span> <span class="symbol">(</span><span class="keyword">int</span><span class="symbol">)</span>browserEmulationVersion<span class="symbol">,</span> RegistryValueKind<span class="symbol">.</span>DWord<span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="comment">// otherwise, remove the existing value</span>
 key<span class="symbol">.</span>DeleteValue<span class="symbol">(</span>programName<span class="symbol">,</span> <span class="keyword">false</span><span class="symbol">)</span><span class="symbol">;</span>
 <span class="symbol">}</span>

 result <span class="symbol">=</span> <span class="keyword">true</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>SecurityException<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// The user does not have the permissions required to read from the registry key.</span>
 <span class="symbol">}</span>
 <span class="keyword">catch</span> <span class="symbol">(</span>UnauthorizedAccessException<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="comment">// The user does not have the necessary registry rights.</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> result<span class="symbol">;</span>
<span class="symbol">}</span>

<span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">bool</span> SetBrowserEmulationVersion<span class="symbol">(</span><span class="symbol">)</span>
<span class="symbol">{</span>
 <span class="keyword">int</span> ieVersion<span class="symbol">;</span>
 BrowserEmulationVersion emulationCode<span class="symbol">;</span>

 ieVersion <span class="symbol">=</span> GetInternetExplorerMajorVersion<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>

 <span class="keyword">if</span> <span class="symbol">(</span>ieVersion <span class="symbol">&gt;=</span> <span class="number">11</span><span class="symbol">)</span>
 <span class="symbol">{</span>
 emulationCode <span class="symbol">=</span> BrowserEmulationVersion<span class="symbol">.</span>Version<span class="number">11</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="keyword">else</span>
 <span class="symbol">{</span>
 <span class="keyword">switch</span> <span class="symbol">(</span>ieVersion<span class="symbol">)</span>
 <span class="symbol">{</span>
 <span class="keyword">case</span> <span class="number">10</span><span class="symbol">:</span>
 emulationCode <span class="symbol">=</span> BrowserEmulationVersion<span class="symbol">.</span>Version<span class="number">10</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="number">9</span><span class="symbol">:</span>
 emulationCode <span class="symbol">=</span> BrowserEmulationVersion<span class="symbol">.</span>Version<span class="number">9</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">case</span> <span class="number">8</span><span class="symbol">:</span>
 emulationCode <span class="symbol">=</span> BrowserEmulationVersion<span class="symbol">.</span>Version<span class="number">8</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="keyword">default</span><span class="symbol">:</span>
 emulationCode <span class="symbol">=</span> BrowserEmulationVersion<span class="symbol">.</span>Version<span class="number">7</span><span class="symbol">;</span>
 <span class="keyword">break</span><span class="symbol">;</span>
 <span class="symbol">}</span>
 <span class="symbol">}</span>

 <span class="keyword">return</span> SetBrowserEmulationVersion<span class="symbol">(</span>emulationCode<span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>As mentioned previously, I don't really want these functions
crashing for anticipated reasons, so these functions will also
catch and ignore <code>SecurityException</code> and
<code>UnauthorizedAccessException</code> exceptions. The
<code>SetBrowserEmulationVersion</code> function will return <code>true</code> if a
value was updated.</p>
<h2 id="simple-usage">Simple Usage</h2>
<p>If you just want &quot;fire and forget&quot; updating of the browser
emulation version, you can use the following lines.</p>
<figure class="lang-csharp highlight"><figcaption><span>csharp</span></figcaption><pre class="code">
<span class="keyword">if</span> <span class="symbol">(</span><span class="symbol">!</span>InternetExplorerBrowserEmulation<span class="symbol">.</span>IsBrowserEmulationSet<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">)</span>
<span class="symbol">{</span>
 InternetExplorerBrowserEmulation<span class="symbol">.</span>SetBrowserEmulationVersion<span class="symbol">(</span><span class="symbol">)</span><span class="symbol">;</span>
<span class="symbol">}</span>
</pre>
</figure>
<p>This will apply the best matching IE version if an emulation
version isn't set. However, it means if the user updates their
copy if IE to something newer, your application will potentially
continue to use the older version. I shall leave that as an
exercise for another day!</p>
<h2 id="caveats-and-points-to-note">Caveats and points to note</h2>
<h3 id="changing-the-emulation-version-while-your-application-is-running">Changing the emulation version while your application is running</h3>
<p>While experimenting with this code, I did hit a major caveat.</p>
<p>In the original application this code was written for, I was
applying the emulation version just before the first window
containing a <code>WebBrowser</code> control was loaded, and this worked
perfectly well.</p>
<p>However, setting the emulation version doesn't seem to work if
an instance of the <code>WebBrowser</code> control has already been created
in your application. I tried various things such as recreating
the <code>WebBrowser</code> control or reloading the <code>Form</code> the control was
hosted on, but couldn't get the new instance to honour the
setting without an application restart.</p>
<p>The attached demonstration program has gone with the &quot;restart
after making a selection&quot; hack - please don't do this in
production applications!</p>
<h3 id="should-i-change-the-emulation-version-of-my-application">Should I change the emulation version of my application?</h3>
<p>You should carefully consider where or not to change the
emulation version of your application. If it's currently working
fine, then it's probably better to leave it as is. If however,
you wish to make use of modern standards compliant HTML, CSS or
JavaScript then setting the appropriate emulation version will
save you a lot of trouble.</p>
<h2 id="further-reading">Further Reading</h2>
<p>The are <em>a lot</em> of different options you can apply to Internet
Explorer and the <code>WebBrowser</code> control. These options allow you
to change behaviours, supported features and quite a few more.
This article has touched upon one of the more common
requirements, but there are a number of other options that are
worth looking at for advanced application scenarios.</p>
<p>An index of all available configuration options can be found on
<a href="http://msdn.microsoft.com/en-us/library/ee330733(v=vs.85).aspx" rel="external nofollow noopener">MSDN</a>.</p>
<h2 id="update-history">Update History</h2>
<ul>
<li>2014-06-28 - First published</li>
<li>2020-11-21 - Updated formatting</li>
</ul>

<p><small>
All content <a href="https://devblog.cyotek.com/copyright-and-trademarks">Copyright (c) by Cyotek Ltd</a> or its respective writers. Permission to reproduce news and web log entries and other RSS feed content in unmodified form without notice is granted provided they are not used to endorse or promote any products or opinions (other than what was expressed by the author) and without taking them out of context. Written permission from the copyright owner must be obtained for everything else.<br />Original URL of this content is https://devblog.cyotek.com/post/configuring-the-emulation-mode-of-an-internet-explorer-webbrowser-control .
</small></p>Richard Mosshttps://www.cyotek.com/richard.moss@cyotek.com