0

I want to paint the whole form including its caption bar and frame on a TBitmap object.

GetFormImage is cool, but it has two problems:

  • It doesn't also paint window frame.
  • It doesn't work when the form is hidden.

Do you have any idea to workaround these problems?

Javid
  • 2,755
  • 2
  • 33
  • 60
  • http://stackoverflow.com/q/3190777/62576 – Ken White May 22 '13 at 19:49
  • @KenWhite: I had seen this question; it still doesn't solve my problems. I don't wanna copy anything to the clipboard. – Javid May 22 '13 at 20:07
  • The post I linked has a list of links to ways to capture the form to a bitmap, which is needed for most methods of copying it to the clipboard. The reason I referred you there was for those links; it had nothing to do with the clipboard part. (I also didn't mark this as a duplicate, which I would have if I thought they were the same question.) Read the post I linked, and look at some of the half-dozen links it includes. :-) – Ken White May 22 '13 at 20:13
  • 3
    The tricky part of your question, is find a method to make a screenshoot of a invisible (or minimized) window, AFAIK the only way to do this is momentarily show the window, take the image and then hide (or minimize) the window again. Or you can use the DWM API and the `DwmRegisterThumbnail` method but this only Works for top-level windows. – RRUZ May 22 '13 at 20:33

3 Answers3

6

The key to accessing non-client area is GetWindowDC function, everything else is blitting as usual:

procedure TForm5.FormClick(Sender: TObject);
var
  Bitmap: TBitmap;
  DC: HDC;
  FileName: string;
begin
  Bitmap := TBitmap.Create;
  try
    Assert(HandleAllocated);
    DC := GetWindowDC(Handle);
    Win32Check(DC <> 0);

    Bitmap.SetSize(Width, Height);

    Win32Check(BitBlt(Bitmap.Canvas.Handle, 0, 0, Width, Height, DC, 0, 0, SRCCOPY));

    FileName := Name + '.' + GraphicExtension(TBitmap);
    Bitmap.SaveToFile(FileName);

    ShellExecute(HWND_DESKTOP, nil, PChar(FileName), nil, nil, SW_NORMAL);
  finally
    ReleaseDC(Handle, DC);
    Bitmap.Free;
  end;
end;

For the second case of hidden (not painted!) window - see a comment from RRUZ.

OnTheFly
  • 2,059
  • 5
  • 26
  • 61
  • 4
    Off topic (self-promotional) note; if you'd like to include also drop shadow of the Aero composited window, you might take a look on [`this post`](http://stackoverflow.com/a/11816100/960757), section 2, `TakeScreenshot` procedure. [+1] – TLama May 22 '13 at 20:45
1

Here is a little function that I've used that you might find useful.

Edit: Oops I didn't fully read the question. This probably doesn't work with a hidden window, but I'm not sure anything would unless the form could draw the frame itself, but it doesn't, the OS does.

function GetScreenShot(const aWndHandle:THandle; AeroAware:boolean=true):TBitmap;
var
  wWindow: HDC;
  wRect : TRect;
  wDesktop : THandle;
begin
  Result  := TBitmap.Create;
  try
    if AeroAware then
      wDesktop := GetDesktopWindow
    else
      wDesktop := aWndHandle;

    wWindow := GetDC(wDesktop);
    try
      Result.PixelFormat := pf32bit;
      GetWindowRect(aWndHandle, wRect);
      Result.Width := wRect.Right-wRect.Left;
      Result.Height := wRect.Bottom-wRect.Top;
      if AeroAware then
        BitBlt(Result.Canvas.Handle, 0, 0, Result.Width, Result.Height, wWindow, wRect.Left, wRect.Top, SRCCOPY)
      else
        BitBlt(Result.Canvas.Handle, 0, 0, Result.Width, Result.Height, wWindow, 0, 0, SRCCOPY);
      Result.Modified := True;
    finally
      ReleaseDC(wDesktop, wWindow);
    end;
  except
    Result.Free;
    Result := nil;
  end;
end;
Vivian Mills
  • 2,552
  • 14
  • 19
  • I don't think aero-awareness is really necessary. DWM aggressively buffers all top-level windows, making capturing an image of invisible window possible (to the certain level of extent). – OnTheFly May 22 '13 at 20:59
  • I found that the borders of the window frame came out black if Aero was turned on and I didn't take it into account. – Vivian Mills May 22 '13 at 21:50
  • 1
    Yes, composited glass will not be as beautiful as on screen – OnTheFly May 22 '13 at 21:57
  • Using this method and passing true in the AeroAware variable allows for the captured image to be presented gracefully filled in rather than the ugly black. – Vivian Mills May 23 '13 at 00:19
1

About capturing form image when hidden, use

AlphaBlend := true;
AlphaBlendValue := 0;

while form is shown. The user will not see the form but GetFormImage() will capture its canvas. I think this can work with "OnTheFly" suggestion too.