Recent

Author Topic: Problem with BitBlt on Windows 2012 R2  (Read 2744 times)

gwiesenekker

  • New Member
  • *
  • Posts: 12
Problem with BitBlt on Windows 2012 R2
« on: October 09, 2015, 11:17:01 am »
I am using the following code to capture the bitmap of the front window:

Code: Pascal  [Select]
  1. procedure TMainForm.Grab(bm: TBitMap);
  2. var
  3.   hWnd: THandle;
  4.   pPid: DWORD;
  5.   hProcess: THandle;
  6.   n: Integer;
  7.   hdcSrc: THandle;
  8.   SourceRect: TRect;
  9.   //bmp: TBitmap;
  10.   //cBmp: HBITMAP;
  11. begin
  12.   hWnd := GetForeGroundWindow;
  13.   if hWnd = 0 then Exit;
  14.   try
  15.     //RedrawWindow(hWnd, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW);
  16.  
  17.     GetWindowThreadProcessID(hWnd, pPid);
  18.     hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, pPid);
  19.     SetLength(ModuleFileName, 255);
  20.     n := GetProcessImageFileName(hProcess, PChar(ModuleFileName), 255);
  21.     //On Windows 2003 GetProcessImageFileName always returns 129?
  22.     n := Strlen(PChar(ModuleFileName));
  23.     SetLength(ModuleFileName, n);
  24.  
  25.     SetLength(ClassName, 255);
  26.     n := GetClassName(hWnd, PChar(ClassName), 255);
  27.     SetLength(ClassName, n);
  28.  
  29.     SetLength(WindowTitle, 255 );
  30.     n := SendMessage(hWnd, WM_GETTEXT, Length(WindowTitle), Integer(WindowTitle));
  31.     SetLength(WindowTitle, n);
  32. {$IFDEF LOGFILE}
  33.     writeln(LogFile, 'ClassName=', ClassName);
  34.     writeln(LogFile, 'ModuleFileName=', ModuleFileName);
  35.     writeln(LogFile, 'WindowTitle=', WindowTitle);
  36. {$ENDIF}
  37.  
  38.     hdcSrc := GetDC(hWnd);
  39.     //hdcSrc := GetDCEx(hWnd, 0, 0);
  40.  
  41.     Windows.GetClientRect(hWnd, SourceRect);
  42. {$IFDEF LOGFILE}
  43.     writeln(LogFile, 'SourceRect Top/Left/Bottom/Right=', SourceRect.Top, ' ', SourceRect.Left, ' ',
  44.       SourceRect.Bottom, ' ', SourceRect.Right);
  45. {$ENDIF}
  46.     bm.Width  := SourceRect.Right - SourceRect.Left;
  47.     bm.Height := SourceRect.Bottom - SourceRect.Top;
  48.  
  49.     //bmp := Graphics.TBitmap.Create;
  50.     //cBmp := CreateCompatibleBitmap(hdcSrc, bm.Width, bm.Height);
  51.     //bmp.Handle := cBmp;
  52.     //BitBlt(bmp.Canvas.Handle, 0, 0, bm.Width, bm.Height, hdcSrc, SourceRect.Left, SourceRect.Top, SRCCOPY);
  53.     //bmp.Canvas.Changed;
  54.  
  55.     //StretchBlt(bm.Canvas.Handle, 0, 0, bm.Width, bm.Height, hdcSrc,
  56.       //0, 0, SourceRect.Right - SourceRect.Left, SourceRect.Bottom - SourceRect.Top,
  57.       //SRCCOPY);
  58.     BitBlt(bm.Canvas.Handle, 0, 0, bm.Width, bm.Height, hdcSrc, SourceRect.Left, SourceRect.Top, SRCCOPY);
  59.     //BitBlt(bm.Canvas.Handle, 0, 0, bm.Width, bm.Height, bmp.Canvas.Handle, SourceRect.Left, SourceRect.Top, SRCCOPY);
  60.     bm.Canvas.Changed;
  61.     Application.ProcessMessages;
  62.   finally
  63.     ReleaseDC(hWnd, hdcSrc);
  64.   end;
  65.  

This code works fine on Windows XP, Windows 7, Windows 8, Windows 2003 and Windows 2008 but on Windows 2012 R2 often not and if it works it shows strange behaviour. If I take a screenshot of Firefox for example it seems to work the first time, but if I go to another page and take the screenshot again it shows the previous page, even when I re-launch the executable and take the screenshot..
As you can see from the commented-out code I already tried some of the suggestions I found on the forums, but none worked until now.

Any ideas?

Regards,
Gijsbert

GetMem

  • Hero Member
  • *****
  • Posts: 3543
Re: Problem with BitBlt on Windows 2012 R2
« Reply #1 on: October 09, 2015, 01:59:58 pm »
First of all when you call a winapi you should always check for function result. You did in the case of GetForeGroundwindow, you should do the same with GetDC and GetClientRect. Although I don't have have w 2012 R2 to test this, I assume GetDC fails to get the correct handle. Try to take a screenshot from the entire screen, then copy the part you need according to GetClientRect. If this works the culprit is GetDC.
 

gwiesenekker

  • New Member
  • *
  • Posts: 12
Re: Problem with BitBlt on Windows 2012 R2
« Reply #2 on: October 09, 2015, 10:27:22 pm »
Thank you! The problem is indeed with GetDC: the function result is not 0 but invalid as your suggested workaround getting the DC from the desktop window instead works:

Code: Pascal  [Select]
  1. procedure TMainForm.Grab(bm: TBitMap);
  2. var
  3.   hWnd: THandle;
  4.   pPid: DWORD;
  5.   hProcess: THandle;
  6.   n: Integer;
  7.   hdcSrc: THandle;
  8.   SourceRect: TRect;
  9.   //bmp: TBitmap;
  10.   //cBmp: HBITMAP;
  11.   TopLeft: TPoint;
  12. begin
  13.   hWnd := GetForeGroundWindow;
  14.   if hWnd = 0 then Exit;
  15.   try
  16.     //RedrawWindow(hWnd, nil, 0, RDW_INVALIDATE or RDW_UPDATENOW);
  17.     //Application.ProcessMessages;
  18.  
  19.     GetWindowThreadProcessID(hWnd, pPid);
  20.     hProcess := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_READ, False, pPid);
  21.     SetLength(ModuleFileName, 255);
  22.     n := GetProcessImageFileName(hProcess, PChar(ModuleFileName), 255);
  23.     //On Windows 2003 GetProcessImageFileName always returns 129?
  24.     n := Strlen(PChar(ModuleFileName));
  25.     SetLength(ModuleFileName, n);
  26.  
  27.     SetLength(ClassName, 255);
  28.     n := GetClassName(hWnd, PChar(ClassName), 255);
  29.     SetLength(ClassName, n);
  30.  
  31.     SetLength(WindowTitle, 255 );
  32.     n := SendMessage(hWnd, WM_GETTEXT, Length(WindowTitle), Integer(WindowTitle));
  33.     SetLength(WindowTitle, n);
  34. {$IFDEF LOGFILE}
  35.     writeln(LogFile, 'ClassName=', ClassName);
  36.     writeln(LogFile, 'ModuleFileName=', ModuleFileName);
  37.     writeln(LogFile, 'WindowTitle=', WindowTitle);
  38. {$ENDIF}
  39.  
  40.     hdcSrc := GetDC(GetDesktopWindow);
  41.     if hdcSrc = 0 then
  42.       raise(CGException.Create('hdcSrc returned nil'));
  43.  
  44.     Windows.GetClientRect(hWnd, SourceRect);
  45. {$IFDEF LOGFILE}
  46.     writeln(LogFile, 'SourceRect Top/Left/Bottom/Right=', SourceRect.Top, ' ', SourceRect.Left, ' ',
  47.       SourceRect.Bottom, ' ', SourceRect.Right);
  48. {$ENDIF}
  49.     bm.Width  := SourceRect.Right - SourceRect.Left;
  50.     bm.Height := SourceRect.Bottom - SourceRect.Top;
  51.  
  52.     TopLeft.X := SourceRect.Left;
  53.     TopLeft.Y := SourceRect.Top;
  54.     Windows.ClientToScreen(hWnd, TopLeft);
  55.     {$IFDEF LOGFILE}
  56.     writeln(LogFile, 'TopLeft X/Y=', TopLeft.X, ' ', TopLeft.Y);
  57.     {$ENDIF}
  58.  
  59.     //bmp := Graphics.TBitmap.Create;
  60.     //cBmp := CreateCompatibleBitmap(hdcSrc, bm.Width, bm.Height);
  61.     //bmp.Handle := cBmp;
  62.     //BitBlt(bmp.Canvas.Handle, 0, 0, bm.Width, bm.Height, hdcSrc, SourceRect.Left, SourceRect.Top, SRCCOPY);
  63.     //bmp.Canvas.Changed;
  64.  
  65.     //StretchBlt(bm.Canvas.Handle, 0, 0, bm.Width, bm.Height, hdcSrc,
  66.       //0, 0, SourceRect.Right - SourceRect.Left, SourceRect.Bottom - SourceRect.Top,
  67.       //SRCCOPY);
  68.     BitBlt(bm.Canvas.Handle, 0, 0, bm.Width, bm.Height, hdcSrc, TopLeft.X, TopLeft.Y, SRCCOPY);
  69.     //BitBlt(bm.Canvas.Handle, 0, 0, bm.Width, bm.Height, bmp.Canvas.Handle, SourceRect.Left, SourceRect.Top, SRCCOPY);
  70.     bm.Canvas.Changed;
  71.   finally
  72.     ReleaseDC(GetDesktopWindow, hdcSrc);
  73.   end;
  74.  

Regards,
Gijsbert
« Last Edit: October 09, 2015, 10:36:57 pm by gwiesenekker »