如何在Windows中拍摄没有背景/壁纸图片的整个桌面的屏幕截图

时间:2013-09-30 15:13:50

标签: delphi screen-capture desktop-wallpaper

我需要捕捉没有背景图片(壁纸)的屏幕截图。我可以尝试禁用壁纸,拍摄屏幕然后再启用它,但是:

  1. 目前我不知道如何禁用/恢复壁纸(在简单的情况下,它是指定为桌面的图片文件,带有一些平铺选项,但它可以在现代版本的Windows中有所不同吗?)。
  2. 如果用户在我将壁纸切换回来之前杀死了应用程序,那么壁纸仍会被禁用而且效果不佳。
  3. 有谁知道解决方案或想法在哪里搜索解决方案? 也许有可能暂时禁用壁纸?

    更新:屏幕截图是注册错误程序的一部分,所以我需要所有可能有用的信息(可见表单,任务栏,...),非常希望保持屏幕拍摄无损格式(更可读,更快的压缩) 。其中一个选项是捕获将它们存储为AVI的镜头,因此处理时间也很重要。背景使图像更大,这是我试图删除它的唯一原因。我可以使用一些算法来减少使用的颜色,它大大提高了压缩率,但这是一个耗时的过程。所以最重要的是删除背景图片。

    更新2:为了从镜头序列生成AVI,我使用了FrançoisPIETTE的单位(基于this article):

    Avi := TAviFromBitmaps.CreateAviFile(
      nil,
      AviFilename,
      MKFOURCC('S', 'C', 'L', 'S'),     // msu-sc-codec
      2, 1);                            // 2 frames per second
    
    // called by timer
    procedure TfrmSnapshot.RecordFrame;
    begin
      TakeSnapshot; // get snap shot to BMP:TBitmap
      Avi.AppendNewFrame(Bmp.Handle);
    end;
    

    因此,如果我能够从快照中删除背景,AVI压缩也会得到改善。

    我使用的最终代码部分:

      TAppRects = class
      protected
        FMonitor: TMonitor;
        FRects: TList<TRect>;
    
        function GetRegion(AArea: TRect): HRGN;
      public
        constructor Create(AMonitor: TMonitor);
        destructor Destroy; override;
    
        // fill all Area which is not covered by Rects (application forms)
        procedure FillBackground(ABmp: TBitmap; AArea: TRect);
    
        property Rects: TList<TRect> read FRects;
        property Monitor: TMonitor read FMonitor;
      end;
    
    // Check for WS_EX_APPWINDOW will hide start button  menu/popup menus outside of
    // the forms etc, but it makes final AVI much smaller (and usually it is anough
    // to have main forms recorded).
    function EnumWindowsProc(hwnd: HWND; lParam: LPARAM): BOOL; stdcall;
    var
      r: TRect;
    begin
      Result := True;
      if IsWindowVisible(hwnd) and
        (GetWindow(hwnd, GW_OWNER)=0) and // is not owned by another window
        (GetWindowLongPtr(hwnd, GWL_STYLE) and WS_EX_APPWINDOW<>0) and // is app
        GetWindowRect(hwnd, r) and
        (r.Width>0) and (r.Height>0)
      then
        with TAppRects(lParam) do
          if (FMonitor=nil) or
            (FMonitor.Handle=0) or
            (FMonitor.Handle=MonitorFromWindow(hwnd, MONITOR_DEFAULTTONEAREST))
          then
            FRects.Add(r);
    end;
    
    { TAppRects }
    
    constructor TAppRects.Create(AMonitor: TMonitor);
    begin
      FMonitor := AMonitor;
      FRects := TList<TRect>.Create;
      EnumWindows(@EnumWindowsProc, NativeInt(self));
    end;
    
    destructor TAppRects.Destroy;
    begin
      FreeAndNil(FRects);
    end;
    
    function TAppRects.GetRegion(AArea: TRect): HRGN;
    var
      c: array of integer;
      p: array of TPoint;
      i: Integer;
    begin
      setlength(c, FRects.Count);
      setlength(p, FRects.Count*4);
      for i := 0 to FRects.Count-1 do
      begin
        c[i] := 4;
        with FRects[i] do
        begin
          p[i*4  ] := Point(Left,Top);
          p[i*4+1] := Point(Right,Top);
          p[i*4+2] := Point(Right,Bottom);
          p[i*4+3] := Point(Left,Bottom);
        end;
      end;
      result := CreatePolyPolygonRgn(p[0], c[0], length(c), WINDING);
    end;
    
    procedure TAppRects.FillBackground(ABmp: TBitmap; AArea: TRect);
    var
      h1,h2,h3: HRGN;
    begin
      h1 := 0;
      h2 := 0;
      h3 := 0;
      try
        h1 := GetRegion(AArea);
        if h1=0 then
          exit;
        h2 := CreateRectRgn(AArea.Left,AArea.Top,AArea.Right,AArea.Bottom);
        h3 := CreateRectRgn(AArea.Left,AArea.Top,AArea.Right,AArea.Bottom);
        if (h2<>0) and (h3<>0) and
          not (CombineRgn(h3, h2,h1, RGN_DIFF) in [NULLREGION,RGN_ERROR])
        then
          FillRgn(ABmp.Canvas.Handle, h3, ABmp.Canvas.Brush.Handle);
      finally
        if h1<>0 then DeleteObject(h1);
        if h2<>0 then DeleteObject(h2);
        if h3<>0 then DeleteObject(h3);
      end;
    end;
    
    procedure RemoveBackground(ASnapshot: TBitmap; AMonitor: TMonitor);
    var
      e: TAppRects;
      c: TColor;
    begin
      e := nil;
      try
        e := TAppRects.Create(AMonitor);
        c := ASnapshot.Canvas.Brush.Color;
        ASnapshot.Canvas.Brush.Color := $FEA249; // kind of blue (~default for win8)
        e.FillBackground(ASnapshot, e.Monitor.WorkareaRect);
        ASnapshot.Canvas.Brush.Color := c;
      finally
        e.free;
      end;
    end;
    

2 个答案:

答案 0 :(得分:4)

禁用壁纸会导致烦人的闪烁/重绘。无论如何,我期待。 枚举桌面上可见的所有窗口,找到它们的尺寸/位置,然后确定所有这些矩形之外的区域将更加清晰。使该区域不可见。即白色,打印时节省纸张,或其他颜色以满足您的目的。这个答案只是为了描述一般方法,但我认为这是要走的路,除非出现一些神奇的“银弹”。

答案 1 :(得分:0)

有一种简单的方法可以做到这一点:

  1. 获取当前背景图片的副本并将其放入2d数组*。
  2. 拍摄一个屏幕截图并将其放入二维数组*。
  3. XOR两个数组的内容。
  4. *)或​​操纵位图就好像它们是数组一样。 确保两个位图的颜色深度相同。

    所有相同的像素将显示为黑色。

    现在把你操控的结果放到面具里;屏蔽截图,用PNG压缩并发送。

    当我离开iPad时会产生代码。