title: Running WinForms WebBrowser control as NETWORK_SERVICE
body: <p>I need to use the WinForms WebBrowser control in an ASP.NET application in order to take screenshots of webpages.</p>
  
  <p>I'd much rather run it in a console app, and communicate to the app via the ASP.NET application, but it's not my call, I've been told it has to run as part of the website.</p>
  
  <p>It all pretty much works apart from every call to navigate starts fresh with a new session, it's like the cookies aren't being persisted. As an experiment, I changed my IIS application pool to run as me rather than NETWORK_SERVICE, and it all works fine. So it's something strange about it running as network service.</p>
  
  <p>I'm guessing that the network service account doesn't have the permissions to keep track of the ASP.NET_SessionId cookie and the auth cookie, but both these are non-persistent cookies, so I don't know why that would be.</p>
  
  <p>Here is some simplified code so you can get the gist of what I'm doing. There is a bunch of logging and storing of images that I cut out, so it's not complete code. Basically a developer/tester/admin can run this code once (task kicked off via webpage), it will generate bitmaps of every page in the system, they can release a new version of the website and then run it again, it will tell you the differences (new pages, pages removed, pages changed).</p>
  
  <pre><code>public void Run()
  {
      var t = new Thread(RunThread);
      t.SetApartmentState(ApartmentState.STA);
      t.Start();
  }
  
  private void RunThread()
  {
      try
      {
          using (var browser = new WebBrowser())
          {
              browser.DocumentCompleted += BrowserDocumentCompleted;
  
              if (!VisitPage(browser, "special page that sets some session flags for testing - makes content predictable")))
                  throw new TestRunException("Unable to disable randomness");
  
              foreach (var page in pages)
              {
                  VisitPage(browser, page);
              }
          }
      }
      // An unhandled exception in a background thread in ASP.NET causes the app to be recycled, so catch everything here
      catch (Exception)
      {
          Status = TestStatus.Aborted;
      }
  }
  
  private bool VisitPage(WebBrowser browser, string page)
  {
      finishedEvent.Reset();
  
      var timeout = false;
      stopwatch = new Stopwatch();
  
      browser.Navigate(page);
  
      stopwatch.Start();
  
      while (!timeout &amp;&amp; !finishedEvent.WaitOne(0))
      {
          Application.DoEvents();
          if (stopwatch.ElapsedMilliseconds &gt; 10000)
              timeout = true;
      }
      Application.DoEvents();
  
      if (timeout)
      {
          if (resource != null)
              ShopData.Shop.LogPageCrawlTestLine(testRunID, resource, sequence++, (int)stopwatch.ElapsedMilliseconds, null);
      }
  
      browser.Stop();
  
      return !timeout;
  }
  
  private void BrowserDocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
  {
      try
      {
          var browser = (WebBrowser)sender;
  
          var width = browser.Document.Body.ScrollRectangle.Width;
          var height = browser.Document.Body.ScrollRectangle.Height;
          var timeTaken = (int)stopwatch.ElapsedMilliseconds;
  
          if ((width == 0) || (height == 0))
          {
              return;
          }
  
          browser.Width = width;
          browser.Height = height;
  
          byte[] buffer;
  
          using (var bitmap = new Bitmap(width, height))
          using (var memoryStream = new MemoryStream())
          {
              browser.DrawToBitmap(bitmap, new Rectangle(0, 0, width, height));
              bitmap.Save(memoryStream, ImageFormat.Bmp);
              buffer = memoryStream.ToArray();
          }
          // stores image
  
      }
      finally
      {
          finishedEvent.Set();
      }
  }
  </code></pre>
  
  <p>So in summary:</p>
  
  <ul>
  <li>The above code is started by a ASP.NET page and it runs in the backgound</li>
  <li>It works fine if I run it in an IIS application pool set to run as a proper user</li>
  <li>If I run it in an IIS application pool as NETWORK_SERVICE then every navigate has a new session, rather than persisting a session for the lifetime of the WebBrowser.</li>
  <li>I can get it working fine outside of ASP.NET, but that is not an option for me currently, it has to run inside this ASP.NET application.</li>
  </ul>
  
  <p>//Update</p>
  
  <p>On the advice of my colleague I'm using impersonation in the background thread where I'm creating the WebBrowser, running as a local user. This works fine and I'm pretty happy with this solution. He's a stackoverflow user so I'll let him post the answer and get the credit :)</p>