web-dev-qa-db-ja.com

.NETの半透明ウィンドウを含むスクリーンショットをキャプチャする

これを行うための比較的ハックのない方法が欲しいのですが、何かアイデアはありますか?たとえば、次の例では、半透明のウィンドウを含まないスクリーンショットを撮ります。

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

私のグーグルフーは本当にひどいか、これは思ったほど簡単ではありません。ビデオドライバがこれを機能させるためにメモリを分離しなければならない方法のためにこれが起こっている理由はかなり確信していますが、なぜそれが機能しないのかは気にしません、私はそれなしでそれをやりたいだけです...
* Print-Screenキーのハッキング
*サードパーティソフトウェア
* SDK関数は問題ありませんが、純粋なフレームワークで表示できるユーザー所有のすべてのオブジェクトに賛成します(冗談ですが、それは素晴らしいことです)。

This がそれを行う唯一の方法である場合、VBでそれを行う方法は?
100万ありがとう。

29
FastAl

TransparencyKeyまたはOpacityプロパティが設定されているフォームは、いわゆるレイヤードウィンドウです。それらは、ビデオアダプタの「オーバーレイ」機能を使用して表示されます。これにより、透明性の効果を得ることができます。

それらをキャプチャするには、CopyPixelOperation引数を受け入れるCopyFromScreenオーバーロードでCopyPixelOperation.CaptureBltオプションをオンにする必要があります。

残念ながら、この過負荷には、これが機能しないという重大なバグがあります。値を適切に検証しません。 .NET4.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);
  }
}

それ以降のWindowsバージョンであるFwiwは、このバグの回避策を提供しました。どちらかは正確にはわかりませんが、Win7SP1だったと思います。 BitBlt()関数は、only CopyPixelOperation.CaptureBltオプションを渡すと、必要な処理を実行します。ただし、もちろん、この回避策は以前のWindowsバージョンに遡及的に適用されなかったため、実際にそれに依存することはできません。

65
Hans Passant