Скриншот метод генерирует черные изображения

После того, как я не смог использовать 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);
    }
}
Другие вопросы по тегам