Мне нужно использовать элемент управления WinForms WebBrowser в приложении ASP.NET, чтобы делать скриншоты веб-страниц.
Я бы предпочел запустить его в консольном приложении и связываться с ним через приложение ASP.NET, но это не мой вызов, мне сказали, что он должен работать как часть веб-сайта.
Все это в значительной степени работает независимо от того, что каждый вызов начинается заново с нового сеанса, как будто файлы cookie не сохраняются. В качестве эксперимента я изменил пул приложений IIS, чтобы он выполнялся как я, а не как NETWORK_SERVICE, и все работает нормально. Поэтому что-то странное в том, что он работает как сетевой сервис.
Я предполагаю, что учетная запись сетевой службы не имеет прав для отслеживания файла cookie ASP.NET_SessionId и файла cookie auth, но оба они являются непостоянными файлами cookie, поэтому я не знаю, почему это будет.
Вот несколько упрощенных кодов, чтобы вы могли понять суть того, что я делаю. Есть куча регистрации и хранения изображений, которые я вырезал, так что это не полный код. По сути, разработчик / тестер / администратор может запустить этот код один раз (задание запускается через веб-страницу), он будет генерировать растровые изображения каждой страницы в системе, они могут выпустить новую версию веб-сайта и затем запустить ее снова, он сообщит вам различия (новые страницы, страницы удалены, страницы изменены).
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 && !finishedEvent.WaitOne(0))
{
Application.DoEvents();
if (stopwatch.ElapsedMilliseconds > 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();
}
}
Итак, в итоге:
- Приведенный выше код запускается страницей ASP.NET и выполняется в фоновом режиме
- Работает нормально, если я запускаю его в пуле приложений IIS, настроенном для работы от имени пользователя
- Если я запускаю его в пуле приложений IIS с именем NETWORK_SERVICE, то у каждой навигации будет новый сеанс, а не постоянный сеанс в течение всего срока службы веб-браузера.
- Я могу заставить его работать нормально вне ASP.NET, но сейчас это не вариант для меня, он должен работать внутри этого приложения ASP.NET.
// Обновить
По совету моего коллеги я использую олицетворение в фоновом потоке, где я создаю WebBrowser, работающий как локальный пользователь. Это прекрасно работает, и я очень доволен этим решением. Он пользователь stackoverflow, поэтому я позволю ему опубликовать ответ и получить кредит:)