1

I want to capture an image of a desktop that ignores my form when captured. I like this answer, but have not been able to capture the desktop content, only a black screen.

Image of form with blank black content

So, I need of help to try fix this trouble.

Here is my version with little changes:

private
    { Private declarations }
    DesktopBMP: TBitmap;
    procedure WMEraseBkgnd( var Message: TWMEraseBkgnd ); message WM_ERASEBKGND;
  public
    { Public declarations }
    protected
    procedure Paint; override;
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
DesktopBMP := TBitmap.Create;
  DesktopBMP.SetSize( Screen.Width, Screen.Height );
  DoubleBuffered := True;
end;

procedure TForm1.tmr1Timer(Sender: TObject);
begin
  Width := 0;
  Height := 0;
  Width := Screen.Width;
  Height := Screen.Height;
end;

procedure TForm1.Paint;
begin
  inherited;
  //Canvas.Draw( 0, 0, DesktopBMP );
  DesktopBMP.SaveToFile('c:\tela.bmp');
end;

procedure TForm1.WMEraseBkgnd( var Message: TWMEraseBkgnd );
var
  DesktopDC: HDC;
  DesktopHwnd: Hwnd;
  DesktopCanvas: TCanvas;
begin
  DesktopHwnd := GetDesktopWindow;
  DesktopDC := GetDC( DesktopHwnd );
  try
    DesktopCanvas := TCanvas.Create;
    DesktopCanvas.Handle := DesktopDC;
    DesktopBMP.Canvas.CopyRect( Rect( 0, 0, Screen.Width, Screen.Height ), DesktopCanvas, Rect( 0, 0, Screen.Width, Screen.Height ) );
  finally
    DesktopCanvas.Free;
    ReleaseDc( DesktopHwnd, DesktopDC );
  end;
  Message.Result := 1;
  inherited;
end;
J...
  • 30,968
  • 6
  • 66
  • 143
  • @TomBrunberg, then not is possible erase this black screen on Form? **WM_ERASEBKGND** works fine, but generate a black screen (on background of Form) and not can see screenshot of desktop. –  Feb 05 '18 at 09:43
  • @TomBrunberg, my goal is see behind a full screen Form (already was made) but the trouble here is that instead of background of Form stay clean, generates a black color and this is showed on result of screen capture like you can see above. Now i want know if exists some possibility to remove this black part on Form (without hide and show Form again) showed on result of screenshot? –  Feb 05 '18 at 10:11
  • You are telling Windows that you took care of erasing the background with `Message.Result := 1;` but your code doesn't do anything apart from copying the Desktop canvas to your bmp. What happens if you comment out that line? – nil Feb 05 '18 at 10:43
  • @nil, `What happens if you comment out that line?`. Nothing. –  Feb 05 '18 at 11:18
  • The reason for the black screen in the image is possibly/probably due to the code in `OnTimer`: `Width := 0; Width := Screen.Width;` (and the same for height). To confirm, outcomment the settings to 0. This might give you only one screenshot and it will not be a final solution, but it will confirm the problem. Then you will need to find another way to trigger updating the screenshot. Btw, saving a file in every paint event of a form doesn't seem like a good idea. – Tom Brunberg Feb 05 '18 at 11:49
  • @TomBrunberg, yes confirmed! –  Feb 05 '18 at 12:21

1 Answers1

2

Here's a solution based on the code you presented.

The overlay form is a borderless one (BorderStyle = bsNone), and it has two buttons, one to take a screenshot of the underlying screen and one to terminate the application (as we have no buttons in the caption).

The main changes to your code are

Two private fields in the form

DoSnapShot: boolean; // to control when to copy the screen
ScreenRect: TRect;   // to hold the rectangle of the overlay

and a procedure

procedure TakeScreenShot;

TakeScreenShotreplaces the OnTimer handler you had in your code, and adds setting the boolean DoSnapShot = True just before resetting the Width and Height

WMEraseBkgnd is modified to only attempt to copy the underlying screen if DoSnapShot = True.

Complete code follows

type
  TForm3 = class(TForm)
    ScreenBtn: TButton;
    ExitBtn: TButton;
    procedure FormCreate(Sender: TObject);
    procedure ScreenBtnClick(Sender: TObject);
    procedure ExitBtnClick(Sender: TObject);
  private
    DesktopBMP: TBitmap;
    DoSnapShot: boolean; // to control when to copy the screen
    ScreenRect: TRect;   // to hold the rectangle of the overlay
    procedure TakeScreenShot;
    procedure WMEraseBkgnd( var Message: TWMEraseBkgnd ); message WM_ERASEBKGND;
  protected
    procedure Paint; override;
  public
    { Public declarations }
  end;

var
  Form3: TForm3;

implementation

{$R *.dfm}

procedure TForm3.ScreenBtnClick(Sender: TObject);
begin
  TakeScreenShot;
end;

procedure TForm3.ExitBtnClick(Sender: TObject);
begin
  Application.Terminate;
end;

procedure TForm3.FormCreate(Sender: TObject);
begin
  Left := 0;
  Top := 0;
  Width := Screen.Width;
  Height := Screen.Height-10;

  ScreenRect := Rect(Left, Top, Width, Height);

  DesktopBMP := TBitmap.Create;
  DesktopBMP.SetSize( Width, Height );
end;

procedure TForm3.Paint;
begin
  inherited;
  Canvas.Draw( 0, 0, DesktopBMP );
end;

procedure TForm3.TakeScreenShot;
begin
  Width := 0;   // will not trigger copying
  Height := 0;  //
  DoSnapShot := True;  // now enable copying the underlying screen
  Width := ScreenRect.Width;    //
  Height := ScreenRect.Height;  // and trigger it in WMEraseBkgnd
end;

procedure TForm3.WMEraseBkgnd(var Message: TWMEraseBkgnd);
var
  DesktopDC: HDC;
  DesktopHwnd: Hwnd;
  DesktopCanvas: TCanvas;
begin
  if DoSnapShot then
  begin
    DoSnapShot := False; // Disable repeated copying
    DesktopHwnd := GetDesktopWindow;
    DesktopDC := GetDC( DesktopHwnd );
    try
      DesktopCanvas := TCanvas.Create;
      DesktopCanvas.Handle := DesktopDC;
      DesktopBMP.Canvas.CopyRect( ScreenRect , DesktopCanvas, ScreenRect );
    finally
      DesktopCanvas.Free;
      ReleaseDc( DesktopHwnd, DesktopDC );
    end;
  end;
  Message.Result := 1;
  inherited;
end;

end.

And the .dfm:

object Form3: TForm3
  Left = 0
  Top = 0
  BorderStyle = bsNone
  Caption = 'Form3'
  ClientHeight = 139
  ClientWidth = 225
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'Tahoma'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object ScreenBtn: TButton
    Left = 8
    Top = 8
    Width = 75
    Height = 25
    Caption = 'ScreenShot'
    TabOrder = 0
    OnClick = ScreenBtnClick
  end
  object ExitBtn: TButton
    Left = 8
    Top = 40
    Width = 75
    Height = 25
    Caption = 'Exit'
    TabOrder = 1
    OnClick = ExitBtnClick
  end
end
Tom Brunberg
  • 20,312
  • 8
  • 37
  • 54
  • seems work :-), but how i can avoid Form "flicker" everytime that **Screenshot** button is clicked? Some idea? –  Feb 05 '18 at 19:32
  • I don't see any flicker except at the buttons. OTOH it doesn't surprise me if it flickers with those width and height manipulations. Solving that would be another challenge. I answered the question you asked, which was concerning the black screen. – Tom Brunberg Feb 05 '18 at 19:47
  • I see now the flickering, if I open more apps and windows in general. It is a genuine problem. – Tom Brunberg Feb 05 '18 at 20:04