Capture screenshot Incluindo janelas semitransparentes no .NET

Eu gostaria de uma maneira relativamente livre de fazer isso, alguma idéia? Por exemplo, o seguinte tira uma captura de canvas que não inclui a janela semitransparente:

Public Class Form1 Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Shown Text = "Opaque Window" Dim win2 As New Form win2.Opacity = 0.5 win2.Text = "Tranparent Window" win2.Show() win2.Top = Top + 50 win2.Left = Left() + 50 Dim bounds As Rectangle = System.Windows.Forms.Screen.GetBounds(Point.Empty) Using bmp As Bitmap = New Bitmap(bounds.Width, bounds.Height) Using g As Graphics = Graphics.FromImage(bmp) g.CopyFromScreen(Point.Empty, Point.Empty, bounds.Size) End Using bmp.Save("c:\temp\scn.gif") End Using Process.Start(New Diagnostics.ProcessStartInfo("c:\temp\scn.gif") With {.UseShellExecute = True}) End Sub End Class 

Ou o meu google-fu realmente é uma droga ou isso não é tão fácil quanto parece. Tenho certeza de que isso está acontecendo por causa da maneira como o driver de vídeo teria que separar a memory para fazer isso funcionar, mas eu não me importo porque não funciona, eu só quero fazer isso sem …
* hacks de teclas na canvas de impressão
* Software de terceiros
* As funções do SDK estão OK, mas eu vou fazer um upvote de todos os objects pertencentes ao usuário, o que pode mostrar isso em um framework puro (Brincadeira, mas seria legal).

Se esta é a única maneira de fazer isso, como faço isso no VB?
1M obrigado.

Os formulários que possuem o conjunto de propriedades TransparencyKey ou Opacity são chamados de janelas em camadas. Eles são mostrados usando o recurso “overlay” do adaptador de vídeo. O que os torna capazes de ter seus efeitos de transparência.

Para capturá-los, é necessário ativar a opção CopyPixelOperation.CaptureBlt na sobrecarga CopyFromScreen que aceita o argumento CopyPixelOperation.

Infelizmente, essa sobrecarga tem um bug crítico que impede que isso funcione. Não valida o valor corretamente. Ainda não foi corrigido no .NET 4.0. Não há outra solução boa, mas recorra ao uso do P / Invoke para fazer a captura de canvas. Aqui está um exemplo:

 using System; using System.Drawing; using System.Windows.Forms; using System.Runtime.InteropServices; namespace WindowsApplication1 { public partial class Form1 : Form { public Form1() { InitializeComponent(); } private void button1_Click(object sender, EventArgs e) { Size sz = Screen.PrimaryScreen.Bounds.Size; IntPtr hDesk = GetDesktopWindow(); IntPtr hSrce = GetWindowDC(hDesk); IntPtr hDest = CreateCompatibleDC(hSrce); IntPtr hBmp = CreateCompatibleBitmap(hSrce, sz.Width, sz.Height); IntPtr hOldBmp = SelectObject(hDest, hBmp); bool b = BitBlt(hDest, 0, 0, sz.Width, sz.Height, hSrce, 0, 0, CopyPixelOperation.SourceCopy | CopyPixelOperation.CaptureBlt); Bitmap bmp = Bitmap.FromHbitmap(hBmp); SelectObject(hDest, hOldBmp); DeleteObject(hBmp); DeleteDC(hDest); ReleaseDC(hDesk, hSrce); bmp.Save(@"c:\temp\test.png"); bmp.Dispose(); } // P/Invoke declarations [DllImport("gdi32.dll")] static extern bool BitBlt(IntPtr hdcDest, int xDest, int yDest, int wDest, int hDest, IntPtr hdcSource, int xSrc, int ySrc, CopyPixelOperation rop); [DllImport("user32.dll")] static extern bool ReleaseDC(IntPtr hWnd, IntPtr hDc); [DllImport("gdi32.dll")] static extern IntPtr DeleteDC(IntPtr hDc); [DllImport("gdi32.dll")] static extern IntPtr DeleteObject(IntPtr hDc); [DllImport("gdi32.dll")] static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight); [DllImport("gdi32.dll")] static extern IntPtr CreateCompatibleDC(IntPtr hdc); [DllImport("gdi32.dll")] static extern IntPtr SelectObject(IntPtr hdc, IntPtr bmp); [DllImport("user32.dll")] public static extern IntPtr GetDesktopWindow(); [DllImport("user32.dll")] public static extern IntPtr GetWindowDC(IntPtr ptr); } } 

Fwiw, uma versão posterior do Windows, forneceu uma solução para esse bug. Não sei exatamente qual, acho que foi o Win7 SP1. A function BitBlt () agora fará o que você deseja se você passar apenas a opção CopyPixelOperation.CaptureBlt. Mas é claro que essa solução não foi aplicada retroativamente às versões anteriores do Windows, então você não pode realmente depender dela.