Скриншот метод генерирует черные изображения
После того, как я не смог использовать control.drawtobitmap в C#, я выбрал второй вариант - сделать скриншоты рабочего стола и вырезать нужные разделы. Мой сбой обнаруживается, когда я переключаю учетные записи пользователей, хотя программа не падает, когда пользователь переключается, программа генерирует только чисто черные изображения.
Я использовал этот код в качестве ссылки: WebBrowser.DrawToBitmap () или другие методы?
Я думаю, логично, что это имеет смысл, так как это поможет Windows сэкономить ресурсы.
Какие варианты / решения у меня есть в моей ситуации?
Правка 1 внесла изменения в код для тестирования:
int c = 0;
while (true)
{
try
{
c++;
Rectangle formBounds = this.Bounds;
Bitmap bmp = new Bitmap(formBounds.Width, formBounds.Height);
using (Graphics g = Graphics.FromImage(bmp))
g.CopyFromScreen(formBounds.Location, Point.Empty, formBounds.Size);
bmp.Save("picture" + c.ToString() + ".jpg");
Thread.Sleep(5000);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString());
}
}
это отлично работает в то время как на учетной записи пользователя, но как только я переключаю пользователей, он возвращает исключение: дескриптор недействителен.
Есть идеи?
Изменить 2:
Ошибка в DrawToBitmap не совсем случайна... если я использовал предоставленный вами код:
Bitmap bmp = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);
this.DrawToBitmap(bmp, this.ClientRectangle);
bmp.Save(".\\picture.jpg");
это работает отлично, пример: http://oi61.tinypic.com/1z23ynp.jpg
Однако, как только я щелкну правой кнопкой мыши на элементе управления веб-браузера, DrawToBitmap вернет пустое изображение.
пример: http://oi60.tinypic.com/9ay0yc.jpg
Так что я могу легко преодолеть эту ошибку, добавив
((Control)webbrowser1).Enabled = false;
это делает невозможным любое нажатие в веб-браузере, но, к сожалению, его деактивация сделает мой проект бесполезным, поскольку его основная функция - эмулировать щелчки мышью на элементе управления веб-браузера. хотя это также может быть проблемой, если окно скрыто.
в настоящее время я смотрю на этот пост, где предоставляется код, чтобы дать вам дескриптор окна.
Имитация клика в скрытое окно, кажется, что оно может иметь какое-то значение... посмотрите.
3 ответа
Какие у вас были проблемы с DrawToBitmap? Здесь он отлично работает (W8.1, VS2013) даже с Webcontrol, а также после переключения пользователей. (Но см. Редактирование в конце для условий!)
Bitmap bmp = new Bitmap(this.ClientRectangle.Width, this.ClientRectangle.Height);
this.DrawToBitmap(bmp, this.ClientRectangle);
// Clipboard.SetImage(bmp); for testing only
bmp.Dispose();
Вот код для скриншота вашего окна:
Rectangle formBounds = this.Bounds;
Bitmap bmp = new Bitmap(formBounds.Width, formBounds.Height );
using (Graphics g = Graphics.FromImage(bmp))
g.CopyFromScreen(formBounds.Location, Point.Empty, formBounds.Size);
//Clipboard.SetImage(bmp); for testing only
bmp.Dispose();
Я могу переключать пользователей так, как хочу, программа продолжает работать.
Кстати, ссылка, которую вы разместили, действительно старая, многие вещи могли улучшиться.
Редактировать:
С обновленным вопросом все стало намного понятнее.
Итак, вы хотите постоянно снимать скриншот своей программы, даже если пользователь изменился, верно? и вы хотите отобразить WebControl, верно?
Пользователь может иметь три типа рабочего стола: экран входа / выхода, экранную заставку и один или несколько обычных экранов рабочего стола. Но пока пользователь вышел из системы, у него вообще нет экрана рабочего стола.
Поэтому метод скриншота не будет работать, если у пользователя нет активного рабочего стола, ни как g.CopyFromScreen
, что приведет к ошибке GDI или к использованию дескриптора окна, как в различных решениях в Интернете, включая те, к которым ведет ваша ссылка. Все это в лучшем случае покажет пустой или черный экран.
Таким образом, метод DrawToBitmap работает только один.
Вы написали, что в нем есть случайные ошибки. Это не то, что я вижу.
Проблемы возникают предсказуемо, когда пользователь взаимодействует с WebBrowser
любым способом. Это включает прокрутку или щелчок с или без навигации. После этих взаимодействий WebBrowser
будет отображаться как пустое поле, пока его URL не будет перезагружен - не только обновлен - но и действительно перезагружен webBrowser1.Uri = new Uri(uriPath)
, Это можно сделать, см. Мой другой пост
WebBrowser
также есть другая проблема при выполнении DrawToBitmap
: Он потерпит неудачу (с указанным пустым полем) для любых страниц, которые включают <input type="text"
элемент. Я не уверен, что это лучший способ обойти это, не говоря уже о том, почему это происходит в первую очередь.. Метод скриншотов не имеет такой конкретной проблемы.
Изменить 2:
Код ОП выкопал код, который, используя вызов PrintWindow
, кажется, решает все проблемы, которые у нас были: он работает при выходе из системы, работает с Refeshing даже после нажатия на WebBrowser
и очищает все страницы, включая страницы с текстовыми полями ввода. Hoorah!
После устранения слабины, вот версия, которая может создать копию Form
или просто WebBroser
(или любой другой Control
) с границами или без них:
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);
public Bitmap CaptureWindow(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;
}
Наконец, этот код, кажется, работает, даже когда я сменил пользователей.
Код для снимка экрана любого несохраненного процесса "Блокнот" ("Без названия - Блокнот")
private void Form1_Load(object sender, EventArgs e)
{
//loop for debugging
int c = 0;
while(true)
{
c++;
System.Drawing.Bitmap image = CaptureWindow(FindWindow(null, "Untitled - Notepad"));
image.Save(".\\picture"+c.ToString()+".jpg");
Thread.Sleep(5000);
}
}
[DllImport("user32.dll")]
public static extern bool PrintWindow(IntPtr hwnd, IntPtr hdcBlt, uint nFlags);
[DllImport("user32.dll")]
public static extern IntPtr GetWindowDC(IntPtr hWnd);
[DllImport("user32.dll", SetLastError = true)]
static extern IntPtr FindWindow(string lpClassName, string lpWindowName);
public System.Drawing.Bitmap CaptureWindow(IntPtr hWnd)
{
System.Drawing.Rectangle rctForm = System.Drawing.Rectangle.Empty;
using (System.Drawing.Graphics grfx = System.Drawing.Graphics.FromHdc(GetWindowDC(hWnd)))
{
rctForm = System.Drawing.Rectangle.Round(grfx.VisibleClipBounds);
}
System.Drawing.Bitmap pImage = new System.Drawing.Bitmap(rctForm.Width, rctForm.Height);
System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(pImage);
IntPtr hDC = graphics.GetHdc();
try
{
PrintWindow(hWnd, hDC, (uint)0);
}
finally
{
graphics.ReleaseHdc(hDC);
}
return pImage;
}
Обратите внимание, что окно может быть скрыто, но оно все равно должно быть развернуто в учетной записи пользователя, чтобы получить полный снимок экрана.
Управление цифровыми правами может остановить это, потому что Windows добавляет защиту для цифровых носителей.
Если, например, вы пытаетесь создать снимок экрана чего-либо в Media Player или Media Center, используя рендеринг графики от Microsoft - да, Microsoft собирается "защитить вас от любого потенциального судебного иска".
Попробуйте это: нажмите "Print Screen" на клавиатуре, затем перейдите в Microsoft Paint и попробуйте вставить скриншот в него. Там что-нибудь есть?
Я столкнулся с той же проблемой, и я не мог использовать метод CopyFromScreen, потому что моя форма с WebBrowser может быть скрыта. Метод PrintWindow также не сработал, генерируя изображения с черными областями, особенно когда моя форма MDI частично покрыта родительской формой MDI.
Наконец, я использовал метод OleDraw, как в этом разделе о SO, но интегрировал его в класс, производный от WebBrowser. По сути, он просто отменяет стандартную контрольную реакцию на сообщение WM_PRINT. Это позволяет создать обычный Control.DrawToBitmap не только для WebBrowser, но и для формы с WebBrowser. Это также работает, если форма скрыта (покрыта другой формой, включая родительскую форму MDI) и должна работать, когда пользователь заблокировал сеанс с Win+L (я не проверял его).
public class WebBrowserEx : WebBrowser
{
private const uint DVASPECT_CONTENT = 1;
[DllImport("ole32.dll", PreserveSig = false)]
private static extern void OleDraw([MarshalAs(UnmanagedType.IUnknown)] object pUnk,
uint dwAspect,
IntPtr hdcDraw,
[In] ref System.Drawing.Rectangle lprcBounds
);
protected override void WndProc(ref Message m)
{
const int WM_PRINT = 0x0317;
switch (m.Msg)
{
case WM_PRINT:
Rectangle browserRect = new Rectangle(0, 0, this.Width, this.Height);
// Don't know why, but drawing with OleDraw directly on HDC from m.WParam.
// results in badly scaled (stretched) image of the browser.
// So, drawing to an intermediate bitmap first.
using (Bitmap browserBitmap = new Bitmap(browserRect.Width, browserRect.Height))
{
using (var graphics = Graphics.FromImage(browserBitmap))
{
var hdc = graphics.GetHdc();
OleDraw(this.ActiveXInstance, DVASPECT_CONTENT, hdc, ref browserRect);
graphics.ReleaseHdc(hdc);
}
using (var graphics = Graphics.FromHdc(m.WParam))
{
graphics.DrawImage(browserBitmap, Point.Empty);
}
}
// ignore default WndProc
return;
}
base.WndProc(ref m);
}
}