代码之家  ›  专栏  ›  技术社区  ›  FastAl

捕获包含.NET中半透明窗口的屏幕截图

  •  28
  • FastAl  · 技术社区  · 14 年前

    我想要一个相对不受黑客攻击的方法来做这个,有什么想法吗?例如,以下截图不包括半透明窗口:

    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函数是可以的,但是我会对用户拥有的所有对象进行升级,这些对象可以在纯框架中显示出来(开玩笑的话,但这很好)。

    如果 This 唯一的方法是,如何在VB中做到这一点?
    1米,谢谢。

    1 回复  |  直到 14 年前
        1
  •  65
  •   Hans Passant    9 年前

    具有transparencykey或opacity属性集的窗体称为分层窗口。它们使用视频适配器的“覆盖”功能显示。这使得他们能够有他们的透明度效果。

    捕获它们需要在接受copyPixelOperation参数的copyFromScreen重载中打开copyPixelOperation.captureBlt选项。

    不幸的是,这个重载有一个严重的bug,阻止了它的工作。它不能正确验证该值。仍然没有在.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为这个bug提供了一个解决方法。不确定是哪一个,我想是Win7SP1。函数的作用是:如果传递 只有 copyPixelOperation.captureBlt选项。但当然,这种变通方法并没有积极地应用于早期的Windows版本,所以您不能真正依赖它。