WebBrowser Madness
Редактировать: оригинальный вопрос был длинным со многими дикими догадками. Я сократил это до оставшихся тайн..
Я боролся и ломал голову весь день и думаю, что должен представить свою проблему сообществу.
Он возник из поста под названием Скриншот метод генерирует черные изображения. Оригинальный постер хочет постоянно делать скриншоты своей программы, включая веб-браузер, каждые n секунд, даже если пользователь вышел из системы.
Когда пользователь вышел из системы, у него больше нет экрана. Поэтому любая попытка прочитать экран потерпит неудачу. Если используется дескриптор окна, результатом является черный ящик, а при использовании CopyFromScreen исключение GDI-ошибки.
Но окно программы все еще там и использует DrawToBitmap
работает нормально, даже когда пользователь вышел из системы.
Вот условия и остающиеся проблемы:
Пользователь не должен касаться / нажимать
WebBrowser
в любом случае. Если он делает это, скажем, прокрутка, щелчок, навигация по подкатегорииDrawToBitmap
звонки приводят к пустому полю.В то время как
WebBrowser
остается нетронутым, достаточно сделатьRefresh
до следующего вызова DrawToBitmap.После прикосновения к нему необходимо снова загрузить URL, выполнив
webBrowser1.Url = new Uri(URLpath);
При навигации новый URL должен быть сохранен для этого. Я делаю это в событии Navigated.
Не важно что,
DrawToBitmap
не удается (с пустым полем), если веб-страница содержит<input type="text" ..> field
,Вандализмом
DocumentText
сReplace("<input", "<in_put");
это можно вылечить, но без дальнейших уловок это приведет к потере листов CSS.
Чтобы проверить это бросить два Buttons, a Label, a Timer, a Combobox and a WebBrowser on a Form
и скопируйте код; измените путь к файлу в папку, которая соответствует вашим настройкам и смотрите..:
public Form1()
{
InitializeComponent();
this.button1.Click += new System.EventHandler(this.button1_Click);
this.button2.Click += new System.EventHandler(this.button2_Click);
this.button1.Text = "Start";
this.button2.Text = "Stop";
this.timer1.Tick += new System.EventHandler(this.timer1_Tick);
this.comboBox1.Items.AddRange(new object[] {
"https://stackru.com/questions",
"http://webcam.zirndorf.de/marktplatz/gross.jpg"});
scapeRect = this.ClientRectangle;
webBrowser1.Url = new Uri("https://stackru.com/questions");
this.comboBox1.SelectedIndexChanged +=
new System.EventHandler(this.comboBox1_SelectedIndexChanged);
}
Rectangle scapeRect = Rectangle.Empty;
int imgIndex = 0;
int urlIndex = 0;
private void button1_Click(object sender, EventArgs e)
{
timer1.Interval = 10 * 1000; // every 10 seconds
timer1.Start();
}
private void button2_Click(object sender, EventArgs e)
{
timer1.Stop();
}
private void timer1_Tick(object sender, EventArgs e)
{
imgIndex ++;
label1.Text = imgIndex .ToString();
webBrowser1.Url = new Uri(comboBox1.Text); // this works almost always
//webBrowser1.Refresh(); // this works only if the WB is 'untouched'
string filename = "d:\\scrape\\AB_sos_Screen" + imgIndex .ToString("000") + ".png";
Bitmap bmp = new Bitmap(scapeRect.Width, scapeRect.Height);
this.DrawToBitmap(bmp, scapeRect);
bmp.Save(filename, System.Drawing.Imaging.ImageFormat.Png);
bmp.Dispose();
}
private void comboBox1_SelectedIndexChanged(object sender, EventArgs e)
{
if (comboBox1.Text != "") webBrowser1.Url = new Uri(comboBox1.Text);
}
private void webBrowser1_Navigated(object sender, WebBrowserNavigatedEventArgs e)
{
if (!comboBox1.Items.Contains(e.Url.ToString()))
urlIndex = comboBox1.Items.Add(e.Url.ToString());
else
urlIndex = comboBox1.Items.IndexOf(e.Url.ToString());
if (urlIndex >= 0) comboBox1.SelectedIndex = urlIndex;
button1.Focus();
}
Теперь я могу перемещаться почти свободно, и соскоб экрана продолжает работать - за исключением страниц с текстовыми полями ввода, например, страницы " Пользователи" или " Теги".
Интересно, может ли кто-нибудь воспроизвести..?
Или объяснить??
Или я все-таки просто "охота за призраками", а дело просто ненадежное???
Окончательное редактирование:
Хотя было бы неплохо получить объяснение, получение рабочего решения, вероятно, должно быть достаточно хорошим. ОП нашел код, который использует PrintWindow
позвонить user32.dll
и решает все проблемы. Работает при выходе из системы, работает с Refreshing
даже после нажатия на WebBrowser
и очищает все страницы, включая страницы с текстовыми полями ввода. Вот моя версия этого:
using System.Runtime.InteropServices;
//...
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);
public Bitmap CaptureControl(Control ctl)
{
//Bitmap bmp = new Bitmap(ctl.Width, ctl.Height); // includes borders
Bitmap bmp = new Bitmap(ctl.ClientRectangle.Width, ctl.ClientRectangle.Height); // content only
using (Graphics graphics = Graphics.FromImage(bmp))
{
IntPtr hDC = graphics.GetHdc();
try { PrintWindow(ctl.Handle, hDC, (uint)0); }
finally { graphics.ReleaseHdc(hDC); }
}
return bmp;
}
Это может захватить форму или элемент управления с границами или без них.
1 ответ
Просто хотел добавить свой опыт после долгого дня, когда я бился головой об этой проблеме.
Выше PrintWindow
на основе метода просто нарисовал черный прямоугольник над большей частью WebBrowser
контроль, хотя, как ни странно, последние несколько строк отображаемого текста, казалось, прошли. Так что даже черный прямоугольник несовместим! Но я смог получить DrawToBitmap()
работать.
Однако существуют всевозможные скрытые требования.
- Во-первых, вы можете иметь только один
WebBrowser
контроль в вашей форме - когда я попытался добавить второй, он будет отображаться нормально, но он будет пустым, когда обращается к растровому изображению. - Во-вторых,
WebBrowser
должен быть верхним элементом управления в вашей форме, и к нему не должно быть применено никаких верхних / нижних полей. Нарушение этого приводило к обрезанию нижней части моего отображаемого HTML-кода, а достаточно большие верхние / нижние поля имели тенденцию приводить к вертикальному растяжению содержимого страницы при обращении к растровому изображению. - В-третьих, чтобы сохранить
WebBrowser
от прикосновения создать инвалидаControl
обернуть его и положитьWebBrowser
внутри этого элемента управления (сDock
изFill
). Вам придется иметь дело с отображением содержимого всего HTML-документа, большая часть которого описана здесь (т. Е. Настроить ваш веб-браузер и содержать размер элемента управления в соответствии с веб-браузеромDocument.Body.ScrollRectangle
вDocumentCompleted
обработчик события).
Но пока этот метод работает последовательно для меня.