views:

1986

answers:

6

For full screenshots, I use this code:

form1.Hide;
sleep(500);
bmp := TBitmap.Create;
bmp.Height := Screen.Height;
bmp.Width := Screen.Width;
DCDesk := GetWindowDC(GetDesktopWindow);
BitBlt(bmp.Canvas.Handle, 0, 0, Screen.Width, Screen.Height, DCDesk, 0, 0, SRCCOPY);
form1.Show ;
FileName := 'Screenshot_'+FormatDateTime('mm-dd-yyyy-hhnnss',now());
bmp.SaveToFile(Format('C:\Screenshots\%s.bmp', [FileName]));
ReleaseDC(GetDesktopWindow, DCDesk);
bmp.Free;

How can I convert that to take a screenshot of only the active window.

A: 

Use GetForegroundWindow() instead of GetDesktopWindow().

You'll have to save the handle which GetForegroundWindow() return and pass the saved value into ReleaseDC() - to be sure that GetWindowDC() and ReleaseDC() are called exactly for the same window in case the active window changes between calls.

sharptooth
Ok now I have this: http://pastebin.com/m2e334a4aIt still takes the fullscreen though.
PuppyKevin
Check what the handle value is. If it's null there's no active window and you effectively dump the entire desktop.
sharptooth
I'm confused. What is the handle value? Also, how do I check it?
PuppyKevin
I hope you have a variable that you assign the result of GetForegroundWindow(). You can add a watch to see the actual value of that variable.
sharptooth
Here, this is my entire procedure: http://pastebin.com/m711bc0c4No, I don't have a variable that has the result of GetForegroundWindow()
PuppyKevin
You need a variable to store the value which GetForegroundWindow() returns anyway. How do you think your code will work if active window changes between the GetWindowDC() and ReleaseDC() calls?
sharptooth
+4  A: 
  1. First of all you have to get the right window. As sharptooth already noted you should use GetForegroundWindow instead of GetDesktopWindow. You have done it right in your improved version.
  2. But then you have to resize your bitmap to the actual size of the DC/Window. You haven't done this yet.
  3. And then make sure you don't capture some fullscreen window!

When I executed your code, my the Delphi IDE was captured and as it is on fullscreen by default, thus creating the illusion of a fullscreen screenshot. (Even though your code is mostly correct)

Considering the above steps, I was successfuly able to create a single-window screenshot with your code.

Just a hint: You can GetDC instead of GetWindowDC if you are only interested in the client area. (No window borders)

EDIT: Here's what I made with your code:

You should not use this code! Look at the improved version below.

procedure TForm1.Button1Click(Sender: TObject);
const
  FullWindow = True; // Set to false if you only want the client area.
var
  hWin: HWND;
  dc: HDC;
  bmp: TBitmap;
  FileName: string;
  r: TRect;
  w: Integer;
  h: Integer;
begin
  form1.Hide;
  sleep(500);
  hWin := GetForegroundWindow;

  if FullWindow then
  begin
    GetWindowRect(hWin,r);
    dc := GetWindowDC(hWin) ;
  end else
  begin
    Windows.GetClientRect(hWin, r);
    dc := GetDC(hWin) ;
  end;

  w := r.Right - r.Left;
  h := r.Bottom - r.Top;

  bmp := TBitmap.Create;
  bmp.Height := h;
  bmp.Width := w;
  BitBlt(bmp.Canvas.Handle, 0, 0, w, h, DC, 0, 0, SRCCOPY);
  form1.Show ;
  FileName := 'Screenshot_'+FormatDateTime('mm-dd-yyyy-hhnnss',now());
  bmp.SaveToFile(Format('C:\Screenshots\%s.bmp', [FileName]));
  ReleaseDC(hwin, DC);
  bmp.Free;
end;

EDIT 2: As requested I'm adding a better version of the code, but I'm keeping the old one as a reference. You should seriously consider using this instead of your original code. It'll behave much nicer in case of errors. (Resources are cleaned up, your form will be visible again, ...)

procedure TForm1.Button1Click(Sender: TObject);
const
  FullWindow = True; // Set to false if you only want the client area.
var
  Win: HWND;
  DC: HDC;
  Bmp: TBitmap;
  FileName: string;
  WinRect: TRect;
  Width: Integer;
  Height: Integer;
begin
  Form1.Hide;
  try
    Application.ProcessMessages; // Was Sleep(500);
    Win := GetForegroundWindow;

    if FullWindow then
    begin
      GetWindowRect(Win, WinRect);
      DC := GetWindowDC(Win);
    end else
    begin
      Windows.GetClientRect(Win, WinRect);
      DC := GetDC(Win);
    end;
    try
      Width := WinRect.Right - WinRect.Left;
      Height := WinRect.Bottom - WinRect.Top;

      Bmp := TBitmap.Create;
      try
        Bmp.Height := Height;
        Bmp.Width := Width;
        BitBlt(Bmp.Canvas.Handle, 0, 0, Width, Height, DC, 0, 0, SRCCOPY);
        FileName := 'Screenshot_' + 
          FormatDateTime('mm-dd-yyyy-hhnnss', Now());
        Bmp.SaveToFile(Format('C:\Screenshots\%s.bmp', [FileName]));
      finally
        Bmp.Free;
      end;
    finally
      ReleaseDC(Win, DC);
    end;
  finally
    Form1.Show;
  end;
end;
DR
Ok, this is my current code now: http://pastebin.com/m43958302This is how the picture turns out: http://i43.tinypic.com/xpcvw1.jpgAny suggestions?
PuppyKevin
You have to be more careful :) 1. You are exchanging the height and width at BitBlt. 2. You are capturing the client area, but you are sizing the bitmap according to the full width.
DR
DR, could you show me what you made from my code? I think I can learn better if I see someone else's work.
PuppyKevin
Done. Hope that it'll get me an up-vote :)
DR
@PuppyKevin: Show us first that you made some effort. Your code is halfway there, you just need to do what DR told you. And replace the Sleep() call with Application.ProcessMessages() to let the other forms redraw themselves.
mghie
DR, I must say, I tip my hat to you. You are one very helpful person :)The code you gave me was very close to what I had after some revision, I was just missing a few things. Also, sorry for my beginner skills, I'm still trying to learn.
PuppyKevin
@DR: Now that you have gotten the deserved up votes and accepted check mark, could you *please* make your code really helpful for beginners: Use try and finally, dispose off resources in the reverse order they were acquired, and so on? Thanks.
mghie
@PuppyKevin: No problem, I once was a beginner, too, and there have been others helping me, too, so I'm just glad to help :)
DR
@DR: Thanks, now you got another +1 ;-)
mghie
+4  A: 

Your code could be a lot simpler. When you have decided on which form you want to save, try the code I use:

procedure SaveFormBitmapToBMPFile( AForm : TCustomForm; AFileName : string = '' );
// Copies this form's bitmap to the specified file
var
  Bitmap: TBitMap;
begin
  Bitmap := AForm.GetFormImage;
  try
    Bitmap.SaveToFile( AFileName );
  finally
    Bitmap.Free;
  end;
end;
Brian Frost
That would only work with forms belonging to the same application. But in that case it's waaay better than messing with the Windows API.
DR
+2  A: 

JCL comes to the rescue once again..

    hwnd := GetForegroundWindow;
    Windows.GetClientRect(hwnd, r);
    JclGraphics.ScreenShot(theBitmap, 0, 0, r.Right - r.Left, r.Bottom - r.Top, hwnd);

    // use theBitmap...
utku_karatas
+2  A: 

Noone here has posted a good answer. The solution that has been so far proposed it to take a screen shot that is 'cropped' at the position of the target window. What if that window is behind another one and isn't currently being rendered by the Operating System? That's why you need to use this function introduced in windows XP.

After a quick Google, here's some example code: http://delphi.about.com/od/delphitips2008/qt/print_window.htm

+1 for having a more robust answer (handle more cases and nice link to great example). I was actually looking for something like this and found this answer to be the best one.
Erich Mirabal
+1  A: 

This combines all the approaches described so far. It also handles multiple-monitor scenarios.

Pass in the kind of screenshot you want, and a TJpegImage, and it will assign your requested screenshot to that image.

///////////
uses
  Jpeg;

type  //define an ENUM to describe the possible screenshot types.
  TScreenShotType = (sstActiveWindow, sstActiveClientArea,
    sstPrimaryMonitor, sstDesktop);
///////////

procedure TfrmMain.GetScreenShot(shotType: TScreenShotType;
  var img: TJpegImage);
var
  w,h: integer;
  DC: HDC;
  hWin: Cardinal;
  r: TRect;
  tmpBmp: TBitmap;
begin
  hWin := 0;
  case shotType of
    sstActiveWindow:
      begin
        //only the active window
        hWin := GetForegroundWindow;
        dc := GetWindowDC(hWin);
        GetWindowRect(hWin,r);
        w := r.Right - r.Left;
        h := r.Bottom - r.Top;
      end;  //sstActiveWindow
    sstActiveClientArea:
      begin
        //only the active client area (active window minus title bars)
        hWin := GetForegroundWindow;
        dc := GetDC(hWin);
        GetWindowRect(hWin,r);
        w := r.Right - r.Left;
        h := r.Bottom - r.Top;
      end;  //sstActiveClientArea
    sstPrimaryMonitor:
      begin
        //only the primary monitor.  If 1 monitor, same as sstDesktop.
        hWin := GetDesktopWindow;
        dc := GetDC(hWin);
        w := GetDeviceCaps(DC,HORZRES);
        h := GetDeviceCaps(DC,VERTRES);
      end;  //sstPrimaryMonitor
    sstDesktop:
      begin
        //ENTIRE desktop (all monitors)
        dc := GetDC(GetDesktopWindow);
        w := Screen.DesktopWidth;
        h := Screen.DesktopHeight;
      end;  //sstDesktop
    else begin
      Exit;
    end;  //case else
  end;  //case

  //convert to jpg
  tmpBmp := TBitmap.Create;
  try
    tmpBmp.Width := w;
    tmpBmp.Height := h;
    BitBlt(tmpBmp.Canvas.Handle,0,0,tmpBmp.Width,
      tmpBmp.Height,DC,0,0,SRCCOPY);
    img.Assign(tmpBmp);
  finally
    ReleaseDC(hWin,DC);
    FreeAndNil(tmpBmp);
  end;  //try-finally
end;
JosephStyons