Entwickler-Ecke

Windows API - Screenshot eines Fensters erstellen


Boldar - Sa 26.06.10 00:09
Titel: Screenshot eines Fensters erstellen
Hallo,
ich habe mal wieder ein Problem:
ich möchte unter Win7 x64 einen Screenshot eines speziellen Fensters erstellen und auf die Platte schmeissen.
Ich habe schon Das hier aus der library [http://www.delphi-library.de/topic_ScreenShot+von+einem+Fenster+erstellen_21561,0.html] sowie das hier aus der DP [http://www.delphipraxis.net/114041-form-screenshot.html] und das hier von Assarbad [http://www.delphipraxis.net/7212-screenshot-machen-als-bmp-oder-png.html] probiert.
Nichts geht. Bei Assarbads Version kriege ich keine Datei, obwohl das Handle gültig ist, bei den anderen ist die Datei leer, also 0 byte groß, aber immerhin vorhanden.
Das kann doch nicht so schwer sein. Ich suche einfach eine Möglichkeit, alle 18ms einen Screenshot eines Fensters zu speichern.
mfg Boldar


elundril - Sa 26.06.10 03:15

schon mal probiert sich das bild direkt in delphi anzeigen zu lassen um zu sehen ob der code zumindestens was macht?


jaenicke - Sa 26.06.10 06:59

Zwei gängige Probleme wären:
1. Der Zielprozess wurde mit Adminrechten gestartet. Es kann sein, dass das dann nicht klappt, da dann die Kommunikation eingeschränkt ist, wenn der eigene Prozess keine hat.
2. Es handelt sich um ein DirectX- oder OpenGL-Fenster. In diesem Fall geht es nicht so einfach.

Hast du einmal versucht von einem anderen Fenster mit dem selben Code einen Screenshot zu machen? Um den Code an sich zu testen meine ich.


Boldar - Sa 26.06.10 13:57

Ich habe als Fenster testweise Notepad genommen, das ist wohl kaum dirctx, das Handle ist gültig und meine IDE wird mit Adminrechten gestartet, also erben die Programme diese doch?
Ich werde das mit dem Anzeigen lassen gleich mal probieren.


jaenicke - Sa 26.06.10 15:03

Dann zeig doch einfach mal deinen Code, in dem du das Fenster von Notepad findest. :gruebel:

Denn den Code hier aus der Library hatte ich selbst schonmal mit Notepad ausprobiert soweit ich mich erinnere. Ich werde es heute Abend noch einmal testen, wenn ich vorn der Arbeit komme (hab Pause :D).


Boldar - Sa 26.06.10 15:20

Ich habe momentan folgenden Code:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
program gamestream;

{$APPTYPE CONSOLE}

uses
  SysUtils,
  windows,
  classes,
  graphics,
  tlhelp32,
  dialogs;

var WIndowhandle:THandle;

function FormularSaveScreenShot(FileName: String; h : hWnd): Boolean;
var
  Rec: TRect;
  iWidth, iHeight: Integer;
begin
  with TBitmap.Create do try
    GetWindowRect(h, Rec);

    iWidth  := Rec.Right - Rec.Left;

    iHeight := Rec.Bottom - Rec.Top;
    Width := iWidth;
    Height := iHeight;
    BitBlt(Canvas.Handle, 00, iWidth, iHeight, GetWindowDC(h), 00, SRCCOPY);
    Result := True;
    try SaveToFile(Filename) except Result := False end;
  finally
    ReleaseDC(h, GetWindowDC(h));
    Free;
  end;
end;

function MyEnumWindowProc(AHandle: THandle; LParam: LongWord): boolean; stdcall;
var
  ProcessID: THandle;
begin
  ProcessID := 0;
  GetWindowThreadProcessID(AHandle, ProcessID);
  Result := not (ProcessID = LParam);
  if not Result then
    WindowHandle := AHandle;
end;


function GetWindowHandleByExeName(const AExeName: string): THandle;
var
  SnapShot: THandle;
  p: TProcessEntry32;
  ProcessHandle: THandle;
begin
  Result := 0;
  WindowHandle := 0;
  ProcessHandle := 0;
  p.dwSize := SizeOf(p);
  SnapShot := CreateToolhelp32Snapshot(TH32CS_SnapProcess, 0);
    try
    if Process32First(SnapShot, p) then
      repeat
      if AnsiLowerCase(AExeName) = AnsiLowerCase(p.szExeFile) then
        ProcessHandle := p.th32ProcessID;
      until (ProcessHandle <> 0or not Process32Next(SnapShot, p);
    EnumWindows(@MyEnumWindowProc, ProcessHandle);
    Result := WindowHandle;
    finally
    CloseHandle(SnapShot);
    end;
end;

var H: THandle;
var bmp: TBitmap;

begin
  { TODO -oUser -cConsole Main : Hier Code einfügen }
  h:=Getwindowhandlebyexename('notepad.exe');
  sleep(1000);
  if formularsavescreenshot ('C:\img.bmp', h) then showmessage ('succes');
  readln;
end.


Dude566 - Sa 26.06.10 15:31

Ich habe mir das Programm von user profile iconjaenicke schon länger nicht mehr angesehen, aber vielleicht findest du darin ja Hilfe.

http://www.delphi-forum.de/topic_SJ+ScreenSchotter+05+alpha+3_96542.html


elundril - Sa 26.06.10 15:35

Folgendes zu diesem Befehl:


Delphi-Quelltext
1:
CreateToolhelp32Snapshot                    


MSDN hat folgendes geschrieben:

If the specified process is a 64-bit process and the caller is a 32-bit process, this function fails and the last error code is ERROR_PARTIAL_COPY (299).


Ich denke das könnte der Grund sein??? Hast du eigentlich GetLastError() mal aufgerufen?

lg elundril


Boldar - Sa 26.06.10 17:29

Das handle ist gültig, ich habe es auch schon mit einem 32-bit-Process probiert, und getlasterror ist null.
Und formularsavescreenshot gibt true zurück. Alles irgendwie sehr komisch.

---Moderiert von user profile iconNarses: Beiträge zusammengefasst---

Also, folgendes geht:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure MakeScreenShot(const ATarget: TBitmap);
var
  DesktopDC: HDC;
begin
  DesktopDC := CreateDC('DISPLAY'nilnilnil);
  try
    ATarget.PixelFormat := pfDevice;
    ATarget.Width := 1280;
    ATarget.Height := 1024;
 
    BitBlt(ATarget.Canvas.Handle, 0012801024, DesktopDC, 00, SRCCOPY);
  finally
    DeleteDC(DesktopDC);
  end;
end;

und gibt ein screenshot des Desktops zurück.
Folgendes:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
function FormularSaveScreenShot(FileName: String; h : hWnd): Boolean;
var
  Rec: TRect;
  iWidth, iHeight: Integer;
begin
  result:=false;
  with TBitmap.Create do try
    GetWindowRect(h, Rec);

    iWidth  := Rec.Right - Rec.Left;
    iHeight := Rec.Bottom - Rec.Top;
    Width := iWidth;
    Height := iHeight;
    BitBlt(Canvas.Handle, 00, iWidth, iHeight, GetWindowDC(h), 00, SRCCOPY);
    Result := True;
    try SaveToFile(Filename) except Result := False end;
  finally
    ReleaseDC(h, GetWindowDC(h));

    Free;
  end;
end;

geht nicht und gibt ein leeres Bitmap zurück. Selbst mit admin-rechten. Wenn ich allerdings als h das Handle des eigenen Fensters übergebe, funktioniert das einwandfrei.


Delete - Sa 26.06.10 20:02

Klasse Fehlerbehandlung:Result := True;. :roll: Woher weißt du, dass alle vorherigen Funktionsaufrufe fehlerfrei waren?

Zitat:
Selbst mit admin-rechten. Wenn ich allerdings als h das Handle des eigenen Fensters übergebe, funktioniert das einwandfrei.

Lass doch mal das rumngewurstel mit den unterschiedlichen Benutzerkonten und starte dein Programm und das Programm, von dem du den Screenshot haben willst im gleichen Kontext.


Boldar - Sa 26.06.10 20:06

user profile iconLuckie hat folgendes geschrieben Zum zitierten Posting springen:
Klasse Fehlerbehandlung:Result := True;. :roll: Woher weißt du, dass alle vorherigen Funktionsaufrufe fehlerfrei waren?

Ist ja nicht von mir, aber wenns ne exception gibt, kommt er ja nie dahin.
user profile iconLuckie hat folgendes geschrieben Zum zitierten Posting springen:

Zitat:
Selbst mit admin-rechten. Wenn ich allerdings als h das Handle des eigenen Fensters übergebe, funktioniert das einwandfrei.

Lass doch mal das rumngewurstel mit den unterschiedlichen Benutzerkonten und starte dein Programm und das Programm, von dem du den Screenshot haben willst im gleichen Kontext.

Ich habe sowohl beide als Admin, als auch beide als normaler User, als auch verschiedene gemischte Varianten probiert.
Naja, wenns so nicht geht, mache ich halt nen screenshot und schneide das dann raus.


Delete - Sa 26.06.10 21:33

user profile iconBoldar hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconLuckie hat folgendes geschrieben Zum zitierten Posting springen:
Klasse Fehlerbehandlung:Result := True;. :roll: Woher weißt du, dass alle vorherigen Funktionsaufrufe fehlerfrei waren?

Ist ja nicht von mir, aber wenns ne exception gibt, kommt er ja nie dahin.

API-Funktionen werfen keine Exception.


elundril - Sa 26.06.10 21:39

Ich zitiere mich ja nur ungern aber:

user profile iconelundril hat folgendes geschrieben Zum zitierten Posting springen:

Hast du eigentlich GetLastError() mal aufgerufen?

lg elundril


jaenicke - Sa 26.06.10 22:54

Das wird damit gar nix zu tun haben. Das gefundene Fenster ist einfach irgendein Fenster vom Editor. Das muss aber nicht das sichtbare sein...

Wie wäre es, wenn du in deiner Callback-Prozedur schaust, ob das Fenster zumindest sichtbar ist? ;-)
(Ob es das ist was du willst, ist nochmal ne andere Frage, hab grad keine Lust zu testen.)

So in der Art meine ich in MyEnumWindowProc:

Delphi-Quelltext
1:
  Result := not ((ProcessID = LParam) and IsWindowVisible(AHandle));