Сделать снимок экрана Включая полупрозрачные окна в .NET - PullRequest
28 голосов
/ 18 июня 2010

Я бы хотел относительно безрукий способ сделать это, есть идеи?Например, следующее делает снимок экрана, который не включает полупрозрачное окно:

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

Либо мой google-fu действительно отстой, либо это не так просто, как кажется.Я почти уверен, почему это происходит из-за того, что видеодрайвер должен разделять память, чтобы это работало, но мне все равно, почему это не работает, я просто хочу сделать это без ...
* ключи экрана печати взламывают
* Программное обеспечение сторонних производителей
* Функции SDK в порядке, но я опишу каждый объект, принадлежащий пользователю, который может показать его в чистом каркасеотлично).

Если Это единственный способ сделать это, как мне это сделать в VB?
1M спасибо.

1 Ответ

65 голосов
/ 18 июня 2010

Формы с установленным свойством TransparencyKey или Opacity являются так называемыми многоуровневыми окнами. Они отображаются с помощью функции «наложения» видеоадаптера. Что позволяет им иметь эффект прозрачности.

Для их захвата необходимо включить параметр CopyPixelOperation.CaptureBlt в перегрузке CopyFromScreen, которая принимает аргумент CopyPixelOperation.

К сожалению, эта перегрузка имеет критическую ошибку, которая мешает этому работать. Это не проверяет значение должным образом. Все еще не исправлено в .NET 4.0. Нет другого хорошего решения, кроме как использовать P / Invoke для создания снимка экрана. Вот пример:

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, более поздняя версия Windows предоставила обходной путь для этой ошибки. Не совсем уверен, какой, я думаю, что это был Win7 SP1. Функция BitBlt () теперь будет делать то, что вы хотите, если вы передаете только опцию CopyPixelOperation.CaptureBlt. Но, конечно, этот обходной путь не применялся ретроактивно к более ранним версиям Windows, поэтому вы не можете от него зависеть.

...