Capturando a tela em Windows Forms

Olá pessoal,

Hoje vou demonstrar como é possível capturar a tela ou até mesmo o conteúdo de um controle e salvá-lo como um Bitmap. Imagine que você tem uma solução de atendimento ao cliente e em algum momento precise capturar a tela do seu usuário e depois anexá-la a algum requisito do software ou tratamento de um bug.

Claro que existem várias ferramentas prontas para captura de tela, mas vamos ver como é possível, através de um código em C# usando o recurso de Interop e acessando a API do Windows, criar um método reusável que pode ser utilizado para capturar vários tipos de tela no Windows.

Criando o projeto

Vamos criar um projeto no Visual Studio 2013 do tipo Windows Forms (você pode utilizar qualquer outra versão do Visual Studio):

SNAGHTML29cbc21f

Para deixar o nosso código de captura reutilizável, vamos criar uma nova classe chamada Tela.cs. Nesta classe iremos criar todo o código de captura, iniciando pelas referências a API do Windows. Esta API é muito rica e possui centenas de métodos muito interessantes, mas vamos nos concentrar basicamente nas rotinas de captura de tela.

Só para conhecimento, todos os controle do Windows são tratados como uma janela e como tal, possuem um identificador único de janela, ou um WindowsHandle. Para acessarmos qualquer informação de uma janela ou controle usando a API do Windows, precisamos deste identificador.

Trabalhando com Interop

Para começar a nossa classe, vamos colocar todo o código de Interop com a API do Windows. Eu não vou explicar em detalhes o que cada método faz, mas você pode acessar um site muito bom chamado PInvoke.net que contém referências, explicações e exemplos para a API do Windows.

O código abaixo faz uma referência para um método da API do Windows e o deixa acessível para nós no C#:

   1: public class Tela

   2:     {

   3:         [DllImport("user32.dll", EntryPoint = "GetDC")]

   4:         static extern IntPtr GetDC(IntPtr ptr);

   5:         [DllImport("user32.dll", EntryPoint = "ReleaseDC")]

   6:         static extern IntPtr ReleaseDC(IntPtr hWnd, IntPtr hDc);

   7:         [DllImport("gdi32.dll", EntryPoint = "DeleteDC")]

   8:         static extern IntPtr DeleteDC(IntPtr hDc);

   9:         [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleDC")]

  10:         static extern IntPtr CreateCompatibleDC(IntPtr hdc);

  11:         [DllImport("gdi32.dll", EntryPoint = "CreateCompatibleBitmap")]

  12:         static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight);

  13:         [DllImport("gdi32.dll", EntryPoint = "SelectObject")]

  14:         static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp);

  15:         [DllImport("gdi32.dll", EntryPoint = "BitBlt")]

  16:         static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, int RasterOp);

  17:         [DllImport("user32.dll", EntryPoint = "GetDesktopWindow")]

  18:         static extern IntPtr GetDesktopWindow();

  19:         [DllImport("gdi32.dll", EntryPoint = "DeleteObject")]

  20:         static extern IntPtr DeleteObject(IntPtr hDc);

  21:

  22:         const int SRCCOPY = 13369376;

  23:         public struct SIZE

  24:         {

  25:             public int cx;

  26:             public int cy;

  27:         }

Entendo um pouco a API do Windows

DC – Device Context, ou Dispositovo de Contexto, é um identificador para um objeto no Windows, por exemplo uma janela ou controle.

GetDC() – devolve o identificador para uma janela.

ReleaseDC() – restaura o identificador para o Windows. Isto é muito importante, todo identificador que for utilizado, deve ser devolvido.

DeleteDC() – quando nós criamos o DC, ao invés de pegá-lo do Windows, precisamos deletá-lo para liberar o recurso.

CreateCompatibleDC() – cria um DC, no nosso caso iremos criar para manipular o Bitmap.

CreateCompatibleBitmap() – cria um Bitmap compatível com um DC.

SelectObjec() – seleciona um objeto, no nosso caso vincula o DC ao Bitmap.

BitBlt() – faz a cópia do conteúdo de um identificador para outro, e no nosso caso, o identificador de destino será um Bitmap.

GetDesktopWindow() – retorna um identificador para a janela principal do Windows (Desktop).

DeleteObject() – deleta um objeto, liberando o recurso alocado.

Criando a rotina que captura a tela

Agora que já entendemos um pouco da API do Window, vamos criar o método da nossa classe que irá fazer as capturas de tela. Para isto vamos adicionar o código abaixo a nossa classe Tela.cs:

   1: public static Bitmap RetornaImagemControle(IntPtr controle, Rectangle area)

   2:         {

   3:             SIZE size;

   4:             IntPtr hBitmap;

   5:

   6:             IntPtr hDC = GetDC(controle);

   7:             IntPtr hMemDC = CreateCompatibleDC(hDC);

   8:

   9:             size.cx = area.Width - area.Left;

  10:             size.cy = area.Bottom - area.Top;

  11:

  12:             hBitmap = CreateCompatibleBitmap(hDC, size.cx, size.cy);

  13:

  14:             if (hBitmap != IntPtr.Zero)

  15:             {

  16:                 IntPtr hOld = (IntPtr)SelectObject(hMemDC, hBitmap);

  17:                 BitBlt(hMemDC, 0, 0, size.cx, size.cy, hDC, 0, 0, SRCCOPY);

  18:                 SelectObject(hMemDC, hOld);

  19:                 DeleteDC(hMemDC);

  20:                 ReleaseDC(GetDesktopWindow(), hDC);

  21:                 Bitmap bmp = System.Drawing.Image.FromHbitmap(hBitmap);

  22:                 DeleteObject(hBitmap);

  23:                 return bmp;

  24:             }

  25:             else

  26:             {

  27:                 return null;

  28:             }

  29:         }

O nosso método recebe como parâmetro um IntPtr, que pode representar qualquer controle ou janela do Windows, e recebe também uma estrutura que representa a área que iremos capturar, pois poderemos querer apenas uma parte do nosso controle transformado em uma imagem.

Iniciamos pegando os identificadores necessários (linhas 6 e 7) e em seguida calculamos o tamanho do nosso Bitmap de captura (linhas 9 e 10), para depois já criarmos este Bitmap (linha 12). Caso consigamos criar o Bitmap, então vamos fazer o vínculo do Bitmap (linha 16), e finalmente iremos executar o método que faz toda a mágica, ou seja, que copia o conteúdo do controle para o nosso Bitmap (linha 17).

Após isto iremos liberar os recursos utilizados (linhas 19 e 20), criamos finalmente o nosso Bitmap (linha 21) e liberamos o último recurso alocado. Ufa! Agora é só retornar o Bitmap com o resultado.

Utilizando a classe para capturar uma informação:

Agora que já temos a classe, vamos voltar para o nosso Windows Forms e mostrar como ele irá funcionar. Para isto adicione um controle Button e também um PictureBox, de maneira que a tela fique parecida com a seguinte:

image

Vamos adicionar o código para o botão “Capturar Desktop”:

   1: private void btnCapturaDesktop_Click(object sender, EventArgs e)

   2:         {

   3:             imagemCapurada.Image = Tela.RetornaImagemControle(IntPtr.Zero, Screen.PrimaryScreen.WorkingArea);

   4:         }

Este código tem um truque bem interessante. Como eu falei no início deste post, iríamos capturar uma tela e toda tela no Windows possui um identificador, sendo assim, o nosso Desktop tem a identificação ZERO, ou seja, informando IntPtr.Zero como o nosso controle a ser capturado, iremos capturar o Desktop do Windows, e para conseguirmos capturar toda a tela, eu utilizei a propriedade PrimaryScreen.WorkingArea, que retorna o tamanho do nosso Desktop.

Para finalizar, iremos implementar um outro botão que irá capturar a nossa própria tela, para isto adicione um novo botão ao formulário e acrescente o código a seguir:

   1: private void btnCapturaTela_Click(object sender, EventArgs e)

   2:         {

   3:             imagemCapurada.Image = Tela.RetornaImagemControle(this.Handle, new Rectangle(0,0,this.Width,this.Height));

   4:         }

   5:

Veja que agora estamos informando o identificador do nosso form e também o seu tamanho, mas você pode informar o identificador de qualquer controle e qualquer tamanho, e talvez capturar uma parte do controle original.

Veja como fica a tela capturada:

image

Conclusão:

Este post mostra duas coisas bem interessantes: como interoperar com a API do Windows e como capturar o conteúdo de uma janela do Windows.

Espero que tenham gostado e que principalmente seja útil no seu dia a dia.

Um grande abraço e até a próxima.

Carlos dos Santos.

Leave a Reply

Your email address will not be published. Required fields are marked *

Anti-spam: complete the taskWordPress CAPTCHA


This blog is kept spam free by WP-SpamFree.