-2

Please check this code:

procedure ScreenShotBMP(DestBitmap : TBitmap; AActiveWindow: Boolean = True) ;
var
  DC: HDC;
begin
  if AActiveWindow then
    DC := GetDC(GetForegroundWindow)
  else
    DC := GetDC(GetDesktopWindow);
  try
    DestBitmap.Width := GetDeviceCaps(DC, HORZRES);
    DestBitmap.Height := GetDeviceCaps(DC, VERTRES);
    BitBlt(DestBitmap.Canvas.Handle, 0, 0, DestBitmap.Width, DestBitmap.Height, DC, 0, 0, SRCCOPY);
  finally
    if AActiveWindow then
      ReleaseDC(GetForegroundWindow, DC)
    else
      ReleaseDC(GetDesktopWindow, DC);
  end;
end;

It generates screenshot properly, of Desktop or Active screen but computer stuck a little big during that operation.

I need app to make screenshots on regular time frames (less then one sec), but running this slows down computer.

It's not CPU consuming, taskmanager doesn't show any abnormal activity, simple entire system is stuck. No matter if I run this code inside main thread or another thread.

Is there any other method to create screenshot that won't slow down machine?

Thanks.

Ivan Mark
  • 463
  • 1
  • 5
  • 17
  • See [This Question](http://stackoverflow.com/questions/7154574/bitblt-performance-with-aero-enabled). You've not actually supplied enough information here, though - e.g. screen size, actual time of BitBlt, time of bitmap resize (and you really should resize the bitmap using setSize) – Anya Shenanigans Nov 27 '13 at 17:31
  • Matheus - There is no AV software present. – Ivan Mark Nov 27 '13 at 17:34
  • Petesh - 1) Screensize is 1920x1080 2) BitBlt consumes about 30-40ms 3) There is no bit resize, just BitBlt Entire system stucks during BitBlt process. – Ivan Mark Nov 27 '13 at 17:35
  • What are you doing with the bitmap after this call returns? It sounds like a memory related issue, but you've provided insufficient info. If you're taking a screen capture that size at a rate of < 1 second apart, it's not going to take long to run out of RAM and have to start swapping to disk. – Ken White Nov 27 '13 at 17:37
  • Ken White - problem is not in speed of memory etc. I can perform many bitblts with regular images - also it takes 1-2ms on my CPU; but when I BitBlt from Desktop DC it's very slow and stucks computer. Please have on mind that my working PC has 8GB RAM and quite fast CPU (I7 2600K). – Ivan Mark Nov 27 '13 at 17:39
  • You're getting involved with DWM & Aero once you aim for the desktop DC. If you want good performance you will have to use something other than BitBlt for the desktop window. Look up the reference in the question I linked regarding undocumented DWM functions (and if the link doesn't resolve use google to find a cache of it) – Anya Shenanigans Nov 27 '13 at 17:51
  • Petesh - I will check the link you gave me. Thanks. – Ivan Mark Nov 27 '13 at 18:32
  • I never said "speed of memory". I said "memory related issue", which means either you've got severely fragmented memory (lots and lots of allocations of different sizes repeatedly that can't be allocated from the same block), leaks (creating but not properly freeing the bitmap), or overuse (too many bitmaps in memory at the same time, meaning the system is having to use virtual memory). Having 8GB of RAM is meaningless if you have a 32-bit application, because your app is still (mostly) limited to a total of 2GB. – Ken White Nov 27 '13 at 21:56

2 Answers2

2

I can't reproduce your problem, based on a quick test on XE5 VCL Win32 application, running on Win7 64-bit, 1280x1024 resolution, on an Intel Core i7 860 @2.80GHz (according to CPU-Z), 4GB DDR3 RAM, using the following test code:

function CaptureWindow(const WindowHandle: HWnd): TBitmap;
var
  DC: HDC;
  wRect: TRect;
  Width, Height: Integer;
begin
  DC := GetWindowDC(WindowHandle);
  Result := TBitmap.Create;
  try
    GetWindowRect(WindowHandle, wRect);
    Width := wRect.Right - wRect.Left;
    Height := wRect.Bottom - wRect.Top;
    Result.Width := Width;
    Result.Height := Height;
    Result.Modified := True;
    BitBlt(Result.Canvas.Handle, 0, 0, Width, Height, DC, 0, 0, SRCCOPY);
  finally
    ReleaseDC(WindowHandle, DC);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  Bmp: TBitmap;
  SW: TStopwatch;
  W, H: Integer;
begin
  SW := TStopwatch.StartNew;
  Bmp := CaptureWindow(GetDesktopWindow);
  try
    Image1.Picture.Assign(Bmp);
    W := Bmp.Width;
    H := Bmp.Height;
  finally
    Bmp.Free;
  end;
  SW.Stop;
  Self.Caption := Format('W: %d H: %d %d:%d %d',
                         [W,
                          H,
                          SW.Elapsed.Minutes, 
                          SW.Elapsed.Seconds,
                          SW.Elapsed.Milliseconds]);
end;

The caption displays: W: 1280 H: 1024 0:0 42, which is an elapsed time of 42 milliseconds for creating the bitmap, capturing the screen and BitBlting it, assigning it to a TImage for display, and freeing the bitmap (not to mention two calls to the high resolution timer within the stopwatch code and the calculations for the elapsed time).

Note: The CaptureWindow code is adapted from something someone posted here a while back. The Aero-aware parts of it didn't seem necessary, as testing showed it worked fine both with and without Aero enabled on Windows 7. (I'm guessing it was something needed under Vista originally.)

Community
  • 1
  • 1
Ken White
  • 123,280
  • 14
  • 225
  • 444
  • 42ms is not the problem, but entire system stuck in that small period. Entire system freeze for some small fragment of time. This sample is basically pretty much identical to what I sent earlier. – Ivan Mark Nov 29 '13 at 15:29
  • As I said, I can't reproduce your problem. Clicking the button over and over and over again simply keeps doing the same thing in the same amount of time. 42ms is the **entire time it takes**; there **is no freeze** at all. – Ken White Nov 29 '13 at 15:31
  • Try to put Timer control (or any other thread) that will trigger this function on every let say 1000ms and then try to move that window (on anyother window). You will see that in that moment entire system stuck. – Ivan Mark Dec 02 '13 at 12:53
  • If you're doing an entire screen capture every millisecond, you're running out of memory and having to swap to disk, which is the problem (just save one image to disk and check its size). Also, while your code is capturing in the timer like that, look at the light for hard disk activity; it's probably on steadily or flashing like a strobe. – Ken White Dec 02 '13 at 14:20
  • No, it's not every MS, but every Second. Interval is 1 sec (not ms). This is the case also when interval is 5000ms, it's the same, on that moment (when screenshot is about to be captured) entire system will stuck for the moment. – Ivan Mark Dec 02 '13 at 14:39
  • Sorry, of course it is. It's still the same problem. It's still too much memory being used, and probably partially due to memory fragmentation and swap disk activity. Did you check the file size and drive activity as I suggested? – Ken White Dec 02 '13 at 14:40
  • Yes, it's not the problem - problem is that moment when Windows tries to render all window layers. I have a pretty fast computer, with lots of memory, SSD, I can handle much bigger images much much faster - can have 500 full screen bitblts in the second, but here it's not the case when I try to blit something from the Desktop DC. – Ivan Mark Dec 04 '13 at 11:38
  • I give up; you're just not listening. It doesn't matter how much RAM you have; a 32-bit app is limited to 2GB total memory, even if you have 64GB RAM on the machine. Once you get close to that 2GB (or the RAM that's actually installed) and try to allocate more, the OS swaps something out to disk, and causes these types of delay. I've mentioned this several times, and twice now asked you to check file size and drive activity (but you haven't indicated you have done so either time). If you won't read what I write and try to understand, I'm wasting time here. Good luck. :-) – Ken White Dec 04 '13 at 11:57
0

1) Which value PixelFormat property of DestBitmap has? For fast BitBlt color format of source and destination must be the same. In your case PixelFormat must has pfDevice value.

2) Why do you use GetDeviceCaps instead of GetWindowRect in case of GetForegroundWindow? You use larger dimension -> BitBlt tries copy more bytes -> BitBlt works slower.

3) Foreground window may be changed between GetForegroundWindow and ReleaseDC(GetForegroundWindow, DC) -> it is better to keep window handle in the separate variable.

Denis Anisimov
  • 3,297
  • 1
  • 10
  • 18
  • 1) Pixel Format is the same, 2) I will check this, but I think it won't speed up process, 3) This is not problem, I can do that but that won't speed up anything. The problem I have is probably related to Aero and slow rendering to DC. – Ivan Mark Nov 27 '13 at 18:33