Screen Capture

I need code for capturing the screen and the I need code for capturing the active window and storing it in an image box so when the image is saved, then the format can be decided.  If needs be, the image can be stored as a temporary bitmap so it can be desplayed.

I'll pay double points for good code.

Sorry for the bluntness, I'm new to delphi and VB just isn't working for this project.  It's due very soon!

Help :)
LVL 3
fibdevAsked:
Who is Participating?
 
EpsylonConnect With a Mentor Commented:
What Delphi version do you have?
0
 
EpsylonCommented:
Here's how to capture the de

procedure TForm1.Button1Click(Sender: TObject);
 var
    DeskTopDC: HDc;
    DeskTopCanvas: TCanvas;
    DeskTopRect: TRect;
 begin
    DeskTopDC := GetWindowDC(GetDeskTopWindow);
    DeskTopCanvas := TCanvas.Create;
    DeskTopCanvas.Handle := DeskTopDC;
    DeskTopRect := Rect(0,0,Screen.Width,Screen.Height);
    Canvas.CopyRect(DeskTopRect,DeskTopCanvas,DeskTopRect);
    ReleaseDC(GetDeskTopWindow,DeskTopDC);
end;
0
 
EpsylonCommented:
de = desktop


And to capture the active window:

procedure TForm1.Button1Click(Sender: TObject);
 var
    WndDC: HDc;
    WndCanvas: TCanvas;
    WndRect: TRect;
    Wnd: HWND;
 begin
    Wnd := GetActiveWindow;
    WndDC := GetWindowDC(Wnd);
    WndCanvas := TCanvas.Create;
    WndCanvas.Handle := WndDC;
    WndRect := Rect(0,0,Screen.Width,Screen.Height);
    Canvas.CopyRect(WndRect,WndCanvas,WndRect);
    ReleaseDC(Wnd,WndDC);
end;
0
Cloud Class® Course: Ruby Fundamentals

This course will introduce you to Ruby, as well as teach you about classes, methods, variables, data structures, loops, enumerable methods, and finishing touches.

 
fibdevAuthor Commented:
Where is the image data stored?
0
 
EpsylonCommented:
Hmmm.... GetActiveWindow does not work. It only looks for the active window in the same thread.

But here's an example of the Desktop-part of your question that stores the image in a TImage:

procedure TForm1.Button1Click(Sender: TObject);
var
  WndDC: HDc;
  WndRect: TRect;
  WndCanvas: TCanvas;
  Wnd: HWND;
  WWidth, WHeight: Integer;
begin
  Wnd := GetDesktopWindow;
  WndDC := GetWindowDC(Wnd);
  WndCanvas := TCanvas.Create;
  WndCanvas.Handle := WndDC;
  GetWindowRect(Wnd, WndRect);

  WWidth := WndRect.Right - WndRect.Left;
  WHeight := WndRect.Bottom - WndRect.Top;
  Label1.Caption := IntToStr(WWidth);
  Label2.Caption := IntToStr(WHeight);

  Image1.Width := WWidth;
  Image1.Height := WHeight;
  Image1.Canvas.CopyRect(Rect(0, 0, WWidth, WHeight), WndCanvas, WndRect);
  WndCanvas.Free;
  ReleaseDC(Wnd,WndDC);
end;
0
 
EpsylonCommented:
Another thing you can do is using the clipboard. To capture the whole desktop press PrtSc (Print Screen next to F12). To capture the active window press Alt-PrtSc. Then use this code to fetch the image from the clipboard:


uses Clipbrd;

procedure TForm1.Button2Click(Sender: TObject);
begin
  Image1.AutoSize := true;
  if Clipboard.HasFormat(CF_BITMAP) then
  begin
    Image1.Picture.Assign(Clipboard);
    Image1.Picture.SaveToFile('image.bmp');
  end
  else
    ShowMessage('No image data on clipboard');
end;


Regards,

Epsylon
0
 
fibdevAuthor Commented:
Is there a way to code my app to copy the active window to the clipboard?
0
 
EpsylonCommented:
Use this to capture the entire screen:

    keybd_event(VK_SNAPSHOT, 1, KEYEVENTF_EXTENDEDKEY or 0, 0);
    keybd_event(VK_SNAPSHOT, 1, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP, 0 );


and this to capture a window:


    keybd_event(VK_SNAPSHOT, 0, KEYEVENTF_EXTENDEDKEY or 0, 0);
    keybd_event(VK_SNAPSHOT, 0, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP, 0 );
0
 
bryan7Commented:
listenning
0
 
fibdevAuthor Commented:
[Error] uScreenShooter.pas(174): Undeclared identifier: 'Clipboard'
0
 
EpsylonCommented:
Put Clipbrd in the uses clause.
0
 
fibdevAuthor Commented:
Epsylon,

Your code works, however, When I tried to shoot the active window a second time, I'm presented with the image data from the first shot.  Is there a way to clear the image data from the clipboard when I'm done with it?

Great job so far, you're a big help.  I'll make the points worth your time.
0
 
EpsylonCommented:
You can clear the clipboard with

  Clipboard.Clear;

But that won't be enough to solve this problem. The point is that your program has to wait until the clipboard has been updated. See below for a complete example:



unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, Clipbrd;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
    procedure ClipboardChanged(var message: TMessage); message WM_DRAWCLIPBOARD;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
  keybd_event(VK_SNAPSHOT, 1, KEYEVENTF_EXTENDEDKEY or 0, 0);
  keybd_event(VK_SNAPSHOT, 1, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP, 0 );
end;

procedure TForm1.ClipboardChanged(var message: TMessage);
begin
  if Clipboard.HasFormat(CF_BITMAP) then
    Image1.Picture.Assign(Clipboard);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Image1.AutoSize := true;
  SetClipboardViewer(Form1.Handle);
end;

end.
0
 
fibdevAuthor Commented:
Adjusted points to 200
0
 
fibdevAuthor Commented:
Epsylon,

For some reason or another this isn't working:

if Clipboard.HasFormat(CF_BITMAP) then
  Image1.Picture.Assign(Clipboard);
  form1.visible := true;
end;

It gets as far as form1.visible := true;
but it never puts the graphic into image1
0
 
MadshiCommented:
Just one hint: Instead of GetActiveWindow you should use GetForegroundWindow...
0
 
EpsylonCommented:
if Clipboard.HasFormat(CF_BITMAP) then
begin   // <---- don't forget
  Image1.Picture.Assign(Clipboard);
  form1.visible := true;
end;

0
 
EpsylonCommented:
Hi Madshi, I couldn't remember that one. Thanks!
0
 
EpsylonCommented:
This should be it then:

procedure TForm1.Button2Click(Sender: TObject);
var
  WndDC: HDc;
  WndRect: TRect;
  WndCanvas: TCanvas;
  Wnd: HWND;
  WWidth, WHeight: Integer;
begin
  Sleep(3000); // 3 secs to make another window active
  Wnd := GetForegroundWindow; // or GetDesktopWindow to capture the desktop
  WndDC := GetWindowDC(Wnd);
  WndCanvas := TCanvas.Create;
  WndCanvas.Handle := WndDC;
  GetWindowRect(Wnd, WndRect);

  WWidth := WndRect.Right - WndRect.Left;
  WHeight := WndRect.Bottom - WndRect.Top;

  Image1.Width := WWidth;
  Image1.Height := WHeight;
  Image1.Canvas.CopyRect(Rect(0, 0, WWidth, WHeight), WndCanvas, Rect(0, 0, WWidth, WHeight));
  WndCanvas.Free;
  ReleaseDC(Wnd,WndDC);
end;
0
 
fibdevAuthor Commented:
Still no image :(
0
 
EpsylonCommented:
Does this work?

unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls, Clipbrd;

type
  TForm1 = class(TForm)
    Image1: TImage;
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    Wnd: HWND;
    procedure ClipboardChanged(var message: TMessage); message WM_DRAWCLIPBOARD;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.DFM}

procedure TForm1.Button1Click(Sender: TObject);
begin
  Clipboard.Clear;
  Wnd := SetClipboardViewer(Form1.Handle);
  keybd_event(VK_SNAPSHOT, 1, KEYEVENTF_EXTENDEDKEY or 0, 0);
  keybd_event(VK_SNAPSHOT, 1, KEYEVENTF_EXTENDEDKEY or KEYEVENTF_KEYUP, 0 );
end;

procedure TForm1.ClipboardChanged(var message: TMessage);
begin
  if Clipboard.HasFormat(CF_BITMAP) then
  begin
    Image1.Picture.Assign(Clipboard);
    ChangeClipboardChain(Form1.Handle, Wnd);
  end;
end;

end.
0
 
fibdevAuthor Commented:
Access Violation at this point:
Wnd := SetClipboardViewer(Form1.Handle);
0
 
fibdevAuthor Commented:
Causes Access Violation:

Wnd := SetClipboardViewer(Form1.Handle);
0
 
EpsylonCommented:
Strange, it works perfectly here. And you have been using 'SetClipboardViewer' before...
0
 
fibdevAuthor Commented:
I don't understand it :(

Now It won't even capture the desktop and it was before.

I'm going to scrap the whole thing and start a new project.
0
 
EpsylonCommented:
How about going back to the last 'GetForegroundWindow/GetWindowDC' example..?

I've uploaded my demo (both versions) on

http://www3.ewebcity.com/joep/capture.zip

Please try it or show/mail me what you have right now.

epsylon3@hotmail.com
0
 
fibdevAuthor Commented:
I got it back to where it was.  I can shoot the desktop again.  I'll download your examples and look at them.  I don't know if this makes a diference, but  I'm using Delphi 5.

0
 
fibdevAuthor Commented:
hmmm,

Can't open it.

I'll send you mine.
0
 
gandalf_the_whiteCommented:
listening...
0
 
EpsylonCommented:
Gandalf, did you try my demo at

http://www3.ewebcity.com/joep/capture.zip 

and can you get it to work?
0
 
fibdevAuthor Commented:
I tried your demo,  Couldn't load it. I'm looking at the one you sent me in the email.
0
 
fibdevAuthor Commented:
Thanks for all your help Epsylon
0
 
EpsylonCommented:
0
 
fibdevAuthor Commented:
Epsylon,

mind if i send you an email?  I've ran into a bug.  That code you gave me won't release the canvas.  If I shoot a window, I can't shoot a window any larger because the whole image won't appear.  It won't size to fit the new, larger image.  I've tried to work around this, but I can't figure it out.

fibdev
0
 
EpsylonCommented:
Make sure that:

  Image1.AutoSize := true;


and change:

  Image1.Width := WWidth;
  Image1.Height := WHeight;

to

  Image1.Picture.Bitmap.Width := WWidth;
  Image1.Picture.Bitmap.Height := WHeight;

0
 
fibdevAuthor Commented:
Thanks Epsylon,

That did the trick!
0
Question has a verified solution.

Are you are experiencing a similar issue? Get a personalized answer when you ask a related question.

Have a better answer? Share it in a comment.

All Courses

From novice to tech pro — start learning today.