AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Zurück Delphi-PRAXiS Programmierung allgemein Win32/Win64 API (native code) Screenshot machen (zum 125.000sten Mal *gg*)
Thema durchsuchen
Ansicht
Themen-Optionen

Screenshot machen (zum 125.000sten Mal *gg*)

Ein Thema von Codehunter · begonnen am 9. Aug 2012 · letzter Beitrag vom 13. Aug 2012
Antwort Antwort
Benutzerbild von Codehunter
Codehunter

Registriert seit: 3. Jun 2003
Ort: Thüringen
2.272 Beiträge
 
Delphi 10.4 Sydney
 
#1

Screenshot machen (zum 125.000sten Mal *gg*)

  Alt 9. Aug 2012, 14:31
Hallo!

Ich möchte mir so eine Screenshot-Routine bauen, wie sie z.B. der PaintShopPro hat. Bisher mache ich das so:
Delphi-Quellcode:
procedure GrabScreenshotFromWindow(AHandle: HWND;
  ATargetBitmap: Graphics.TBitmap);
var
  C: TCanvas;
  R: TRect;
begin
  C:=TCanvas.Create;
  C.Handle:=GetWindowDC(AHandle);
  GetWindowRect(AHandle, R);
  ATargetBitmap.Width:=R.Right - R.Left;
  ATargetBitmap.Height:=R.Bottom - R.Top;
  ATargetBitmap.Canvas.CopyRect(R, C, R);
  ReleaseDC(0, C.Handle);
  C.Free;
end;
Geht auch erstmal soweit. Ich hole mir mit GetForegroundWindow das Handle vom aktiven Fenster. Passt. Das Problem dabei: Ich möchte dabei auch evtl. aufgeklappte Menüs mit erfassen. Die verschwinden aber wie von Zauberhand im Screenshot.

Ich vermute, die Routine im PaintShopPro arbeitet ein bisschen anders. Die holt sich zwar das WindowRect vom betreffenden Fenster, "schnappschießt" dann aber den kompletten Bildschirminhalt und schneidet dann den nicht relevanten Teil weg.

Die Preisfrage ist also: Wie "schnappschießt" man den kompletten Bildschirminhalt mit allen Fenstern, auch denen mit "exotischen" Zuständen wie aufgeklappte Menüs?

Grüße
Cody
  Mit Zitat antworten Zitat
Benutzerbild von himitsu
himitsu

Registriert seit: 11. Okt 2003
Ort: Elbflorenz
44.184 Beiträge
 
Delphi 12 Athens
 
#2

AW: Screenshot machen (zum 125.000sten Mal *gg*)

  Alt 9. Aug 2012, 14:39
Es gibt eigentlich genügend Threads zu diesem Thema.

Der Grund, warum es bei dir nicht geht:
Du holst dir das Handle des aktiven Fensters und sagts, daß dieses Fenster sich in dein Bitmap reinzeichnen soll.
Es wird dabei nichts vom Desktop kopiert.


Was mit deiner Methode gehen würde:
- Allen Fenstern (diese vorher suchen) sagen sie sollen sich in dein Bitmap zeichnen, entsprechend ihrer Position auf dem Desktop und entsprechend der Z-Ausrichtung, von hinten nach vorne.
$2B or not $2B

Geändert von himitsu ( 9. Aug 2012 um 14:41 Uhr)
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
9.648 Beiträge
 
Delphi 11 Alexandria
 
#3

AW: Screenshot machen (zum 125.000sten Mal *gg*)

  Alt 9. Aug 2012, 14:41
Dazu kommen noch Overlays usw., die man nur mit ein wenig mehr Aufwand auslesen kann.
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.686 Beiträge
 
Delphi 2007 Enterprise
 
#4

AW: Screenshot machen (zum 125.000sten Mal *gg*)

  Alt 9. Aug 2012, 14:46
Was die Menüs und anderes angeht, würde es ja schon reichen, mit dem DC vom Desktop den Screenshot zu machen, und den auf die Maße des Fensters zu begrenzen. Das Desktop-Handle ist imho immer 0, also GetDC(0).

Edit: Und zumindest unter Win7 mit aktivem Aero bekomme ich damit sogar Overlay-Fensterinhalte. Bei Fullscreen könnte das aber ggf. anders aussehen, und ohne Aero habe ich's schlicht nie getestet.
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Benutzerbild von Codehunter
Codehunter

Registriert seit: 3. Jun 2003
Ort: Thüringen
2.272 Beiträge
 
Delphi 10.4 Sydney
 
#5

AW: Screenshot machen (zum 125.000sten Mal *gg*)

  Alt 9. Aug 2012, 15:00
Habs! Das Stichwort GetDC(0) war die Erleuchtung. Zumindest hatte ich dadurch schon mal einen Ansatz vor Augen. Dadurch habe ich im SDC schnell etwas gefunden und adaptiert:
Delphi-Quellcode:
procedure CaptureScreen(ABitmap: Graphics.TBitmap);
const
   CAPTUREBLT = $40000000;
var
   hdcScreen: HDC;
   hdcCompatible: HDC;
   hbmScreen: HBITMAP;
begin
  hdcScreen:= CreateDC('DISPLAY', nil, nil, nil);
  hdcCompatible:= CreateCompatibleDC(hdcScreen);

  hbmScreen:= CreateCompatibleBitmap(hdcScreen,
                                     GetDeviceCaps(hdcScreen, HORZRES),
                                     GetDeviceCaps(hdcScreen, VERTRES));

  SelectObject(hdcCompatible, hbmScreen);
  ABitmap.Handle:= hbmScreen;
  BitBlt(hdcCompatible, 0, 0, ABitmap.Width, ABitmap.Height, hdcScreen, 0, 0,
         SRCCOPY or CAPTUREBLT);

  DeleteDC(hdcScreen);
  DeleteDC(hdcCompatible);
end;

procedure GrabScreenshotFromWindow(AHandle: HWND;
  ATargetBitmap: Graphics.TBitmap);
var
  R: TRect;
  DeskBMP: Graphics.TBitmap;
begin
  DeskBMP:= Graphics.TBitmap.Create;
  try
    CaptureScreen(DeskBMP);
    GetWindowRect(AHandle, R);
    ATargetBitmap.Width:=R.Right - R.Left;
    ATargetBitmap.Height:=R.Bottom - R.Top;
    ATargetBitmap.Canvas.CopyRect(R, DeskBMP.Canvas, R);
  finally
    DeskBMP.Free;
  end;
end;
Scheint soweit auch mit Overlays und Alphatransparenzen klar zu kommen (Menüschatten etc.)
  Mit Zitat antworten Zitat
Benutzerbild von Codehunter
Codehunter

Registriert seit: 3. Jun 2003
Ort: Thüringen
2.272 Beiträge
 
Delphi 10.4 Sydney
 
#6

AW: Screenshot machen (zum 125.000sten Mal *gg*)

  Alt 13. Aug 2012, 10:43
So, inzwischen ist doch noch ein Problemchen aufgetaucht: Ich habe mich als gewohnheitsmäßiger Multimonitor-User an das MSDN gehalten. Zitat:
Zitat:
If there are multiple monitors on the system, calling CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL) will create a DC covering all the monitors.
Und was ist? Nischt ist! Platziert man ein Fenster über der Schnittlinie zweier Monitore, dann grabbt die Routine nur den Teil des Fensters, der auf dem primären Bildschirm liegt. Der Teil außerhalb wird zwar von den Dimensionen des Bitmaps her richtig erfasst, ist aber schön weiß eingefärbt.

Liegt das Fenster gänzlich auf einem sekundären Bildschirm, dann ist der Screenshot komplett weiß.

Hat jemand eine Idee woran das nun wieder liegt?

EDIT: *Kopfklatsch* - Das war mal wieder ein zu niedriger Kaffeepegel *gg* Ich habe mit GetDeviceCaps die Auflösung geholt und da scheints etwas inkonsequent zu werden beim Win32API. Zwar habe ich den DC für den kompletten Desktop über alle Bildschirme richtig geholt, aber GetDeviceCaps liefert trotzdem nur den primären Monitor:
Zitat:
On a multiple monitor system, if hdc is the desktop, then GetDeviceCaps returns the capabilities of the primary monitor.
Also habe ich stattdessen Screen.DesktopHeight bzw. Screen.DesktopWidth verwendet und schon gehts.
Miniaturansicht angehängter Grafiken
image2.png  

Geändert von Codehunter (13. Aug 2012 um 10:53 Uhr)
  Mit Zitat antworten Zitat
Medium

Registriert seit: 23. Jan 2008
3.686 Beiträge
 
Delphi 2007 Enterprise
 
#7

AW: Screenshot machen (zum 125.000sten Mal *gg*)

  Alt 13. Aug 2012, 10:51
Zitat:
If lpszDriver is DISPLAY or the device name of a specific display device, then lpszDevice must be NULL or that same device name. If lpszDevice is NULL, then a DC is created for the primary display device.

If there are multiple monitors on the system, calling CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL) will create a DC covering all the monitors.
Das ist wiedersprüchlich im MSDN. Im oberen Teil steht quasi:
CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL) --> DC für primären Bildschirm
CreateDC(TEXT("DISPLAY"),TEXT("DISPLAY"),NULL,NULL ) --> DC für alle Bildschirme

Während im unteren deine Interpetation steht:
CreateDC(TEXT("DISPLAY"),NULL,NULL,NULL) --> DC für alle Bildschirme

Probier mal mit 2x 'DISPLAY', vielleicht war es das ja schon. (Wie ich solche Finten in Dokus liebe...)
"When one person suffers from a delusion, it is called insanity. When a million people suffer from a delusion, it is called religion." (Richard Dawkins)
  Mit Zitat antworten Zitat
Benutzerbild von Codehunter
Codehunter

Registriert seit: 3. Jun 2003
Ort: Thüringen
2.272 Beiträge
 
Delphi 10.4 Sydney
 
#8

AW: Screenshot machen (zum 125.000sten Mal *gg*)

  Alt 13. Aug 2012, 10:56
Hat sich zwischenzeitlich erledigt. Siehe EDIT im Post vorher. Bei solchen Gelegenheiten merkt man dann wieder, dass der Multimonitor-Betrieb nicht zum ursprünglichen Design von Windows gehörte sondern bei Win98 quasi "drangeflanscht" wurde.

Richtig eklig wird es immer dann, wenn Bildschirmkoordinaten plötzlich negativ werden weil ein Monitor logisch gesehen links vom primären Bildschirm liegt.
  Mit Zitat antworten Zitat
Benutzerbild von Codehunter
Codehunter

Registriert seit: 3. Jun 2003
Ort: Thüringen
2.272 Beiträge
 
Delphi 10.4 Sydney
 
#9

AW: Screenshot machen (zum 125.000sten Mal *gg*)

  Alt 13. Aug 2012, 12:28
So, das sollte sie nun sein, die Screenshotfunktion die unter allen erdenklichen Multimonitor-Geometrien funktioniert. Man sollte aber bedenken, dass CreateDC('DISPLAY' laut MSDN erst ab Windows 2000 funktioniert.
Delphi-Quellcode:
procedure CaptureScreen(ABitmap: Graphics.TBitmap);
const
   CAPTUREBLT = $40000000;
var
   hdcScreen: HDC;
   hdcCompatible: HDC;
   hbmScreen: HBITMAP;
   DeskHeight, DeskWidth, LeftOffset, TopOffset: Integer;
begin
  hdcScreen:= CreateDC('DISPLAY', NIL, NIL, NIL);
  hdcCompatible:= CreateCompatibleDC(hdcScreen);
  DeskHeight:= Screen.DesktopHeight;
  DeskWidth:= Screen.DesktopWidth;
  LeftOffset:= Screen.DesktopLeft;
  TopOffset:= Screen.DesktopTop;

  hbmScreen:= CreateCompatibleBitmap(hdcScreen,
                                     DeskWidth,
                                     DeskHeight);

  SelectObject(hdcCompatible, hbmScreen);
  ABitmap.Handle:= hbmScreen;
  BitBlt(hdcCompatible, 0, 0, ABitmap.Width, ABitmap.Height, hdcScreen,
         LeftOffset, TopOffset, SRCCOPY or CAPTUREBLT);

  DeleteDC(hdcScreen);
  DeleteDC(hdcCompatible);
end;

procedure GrabScreenshotFromWindow(AHandle: HWND;
  ATargetBitmap: Graphics.TBitmap);
var
  R: TRect;
  DeskBMP: Graphics.TBitmap;
  WinHeight, WinWidth, LeftOffset, TopOffset: Integer;
begin
  DeskBMP:= Graphics.TBitmap.Create;
  R:= Screen.DesktopRect;
  LeftOffset:= Abs(R.Left);
  TopOffset:= Abs(R.Top);
  try
    CaptureScreen(DeskBMP);
    GetWindowRect(AHandle, R);
    Inc(R.Left, LeftOffset);
    Inc(R.Top, TopOffset);
    Inc(R.Right, LeftOffset);
    Inc(R.Bottom, TopOffset);
    WinHeight:= R.Bottom - R.Top;
    WinWidth:= R.Right - R.Left;
    ATargetBitmap.Width:= WinWidth;
    ATargetBitmap.Height:= WinHeight;
    ATargetBitmap.Canvas.CopyRect(Rect(0, 0, WinWidth, WinHeight), DeskBMP.Canvas, R);
  finally
    DeskBMP.Free;
  end;
end;
  Mit Zitat antworten Zitat
Antwort Antwort


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 13:05 Uhr.
Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz