Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Win32/Win64 API (native code) (https://www.delphipraxis.net/17-win32-win64-api-native-code/)
-   -   Delphi Screenshot eines anderen Programmes mit wm_print (https://www.delphipraxis.net/71058-screenshot-eines-anderen-programmes-mit-wm_print.html)

SirThornberry 8. Jun 2006 11:09


Screenshot eines anderen Programmes mit wm_print
 
Hallo,

ich versuche mit folgendem Quelltext ein Screenshot eines anderen Programmes zu machen. Leider bleibt mein Bild leer. Muss ich das Handle unbedingt im anderen Prozess erzeugen oder sorgt SendMessage dafür das dieses Handle im anderen Prozess gültigkeit erlangt (wie es bei wm_copydata dafür sorgt das die Daten im anderen Adressraum gültig sind)?
Delphi-Quellcode:
  GetWindowPlacement(lHandle, @lPlacement);
  lRect := lPlacement.rcNormalPosition;

  lBitmap := TBitmap.Create;
  lBitmap.Height := lRect.Bottom - lRect.Top + 1;
  lBitmap.Width := lRect.Right - lRect.Left + 1;
  SendMessage(lHandle, WM_PRINT, Integer(lBitmap.Canvas.Handle), PRF_ERASEBKGND or PRF_CLIENT or PRF_CHILDREN);
  lBitmap.SaveToFile('d:\ab1.bmp');
  lBitmap.Free;
[Edit]
Mit der Funktion PrintWindow funktioniert es. Mich würde jedoch trotzdem interessieren warum es so wie ich es versucht hab nicht funktioniert.
[/Edit]

Vjay 8. Jun 2006 15:40

Re: Screenshot eines anderen Programmes mit wm_print
 
Ja du musst in den anderen Prozess rüber.
Und selbst dann funktioniert es nicht immer, es gibt halt Controls die die WM_PRINT ignorieren.

Luckie 8. Jun 2006 15:44

Re: Screenshot eines anderen Programmes mit wm_print
 
Reservier die Speicher im fremden prozess mit VirtualAllocEx, erstell dort dein Bitamp und dann kopierst du es dir mit ReadProcessMemory in eine identsiche Struktur in deinem Prozess. Siehe dazu auch meine LuckieDIPS.

Vjay 8. Jun 2006 16:00

Re: Screenshot eines anderen Programmes mit wm_print
 
Liste der Anhänge anzeigen (Anzahl: 1)
Ich habe das vor einiger Zeit mal mit DLL injection gelöst.
Ich hänge dir den Code mal an, aber Achtung: Das ganze ist alles andere als sauber programmiert! Das war so ein, "so lange basteln bis es funktioniert" - Ding.

An einer Lösung ohne DLL wäre ich aber auch jederzeit interessiert, nur wie erzeugt man ein Bitmap in einem anderen Prozess, ohne darin zu laufen?

PS. Aufruf für ein einzelnes Fenster:

function doSnapshot( const handle: integer; const usePaint, doEnumAfter: Boolean; const dllFilePath: string): TBitmap;

handle: logisch
usePaint: true = WM_PAINT / false = WM_PRINT
doEnumAfter: Wenn true enumchildwindows und nochmals WM_PAINT an diese senden
dllFilePath: Pfad zur DLL
result: NIL wenn Fehler, ansonsten das erstellte TBitmap

Die anderen Funktionen sind um Screenshots von ganzen Applikationen zu machen. Die Unit verwendet noch eine Mainfunc, die ich aber nicht includen werde, musst sie dir halt umbasteln wenn du sie verwenden willst.

seRiaLizA 15. Mai 2007 11:02

Re: Screenshot eines anderen Programmes mit wm_print
 
wenn ich dein

doSnapshot

aufrufe, enthällt das Image was zurückgegeben wird folgendes:

"Anwendung reagiert nicht."

OS: WinXP

Weiß jemand warum?

zeras 14. Feb 2009 19:46

Re: Screenshot eines anderen Programmes mit wm_print
 
Da ich einen Screenshot unter Win2000 brauche, hilft mir die Funktion "Printwindow" nicht weiter. Diese gibt es erst ab XP.
Nun habe ich versucht, mit LuckieDIPS mir was zusammenzubauen, aber die Windowsfunktionen sind nicht so mein Ding.
Ich habe mal meinen Versuch angehängt, aber er bringt nur immer eine weißes Canvas und sonst nichts.
Hat jemand eine fertige Funktion dafür, abgesehen von der DLL, die vorher erwähnt wurde?


Delphi-Quellcode:

  function ScreenshotHidden1(wnd: HWND; const bmp: TBitmap): Boolean;
  var
    rec: TRect;   //zum Zwischenspeichern der Fenstergröße
    caption: string;


    Size             : Cardinal;
    MemLocal         : ^TBitmap;
    MemRemote        : ^TBitmap;
    Process          : THandle;
    ProcessId        : DWORD;
    NumBytes         : Cardinal;
    ListView         : HWND;
    b                : TBitmap;
    InfoHeaderSize   : Cardinal;
    ImageSize        : Cardinal;


   begin
    Result := False;                    //Rückgabewert initialisieren

    GetWindowRect(wnd, rec);          //Größe des Fensters auslesen...
    bmp.Width := rec.Right - rec.Left;  //...und die Bitmap-Größe anpassen
    bmp.Height := rec.Bottom - rec.Top;
    bmp.PixelFormat := pf24Bit; // Datenmassen in Zaum halten

    bmp.SetSize (bmp.Width, bmp.Height);

    {Graphics.}GetDIBSizes (bmp.Handle, InfoHeaderSize, ImageSize);


    bmp.Canvas.Lock;                //Zugriff auf den Canvas für andere Threads blockieren

    ShowMessage(Format('Breite "%d", Höhe "%d"',[bmp.Width,bmp.Height]));
    try

      b:=TBitmap.Create;
      b.Width:=bmp.Width;
      b.Height:=bmp.Height;
      b.PixelFormat:=bmp.PixelFormat;

     
      // Fensterhandle des Desktop-ListView ermitteln und Prozess oeffnen
      ListView := wnd;
      ProcessId := 0;
      GetWindowThreadProcessId(ListView, @ProcessId);

      Process := OpenProcess(PROCESS_QUERY_INFORMATION or PROCESS_VM_OPERATION or
                 PROCESS_VM_READ or PROCESS_VM_WRITE, False, ProcessId);
      if Process <> 0 then begin
        try

          Size := ImageSize;

          MemRemote := VirtualAllocEx(Process, nil, Size, MEM_COMMIT, PAGE_READWRITE);
          MemLocal := VirtualAlloc(nil, Size, MEM_COMMIT, PAGE_READWRITE);

          if Assigned(MemLocal) and Assigned(MemRemote) then begin
            SendMessage(wnd, WM_PRINT, Integer(MemRemote), PRF_ERASEBKGND or PRF_CLIENT or PRF_CHILDREN);
          end;

          if ReadProcessMemory(Process, MemRemote, bmp.Canvas, Size, NumBytes) then begin

          end;
        except
        end;
      end;


      finally
        bmp.Canvas.Unlock;            //Zugriff wieder erlauben
      end;
  end;

stoxx 14. Feb 2009 21:50

Re: Screenshot eines anderen Programmes mit wm_print
 
Zitat:

Zitat von zeras
Da ich einen Screenshot unter Win2000 brauche, hilft mir die Funktion "Printwindow" nicht weiter. Diese gibt es erst ab XP.
Nun habe ich versucht, mit LuckieDIPS mir was zusammenzubauen, aber die Windowsfunktionen sind nicht so mein Ding.
Ich habe mal meinen Versuch angehängt, aber er bringt nur immer eine weißes Canvas und sonst nichts.
Hat jemand eine fertige Funktion dafür, abgesehen von der DLL, die vorher erwähnt wurde?

also mit der Funktion: myBMPWindowSnap

im Link
http://www.delphipraxis.net/internal...32+printwindow


geht es eigentlich immer ganz gut.
Windows2000? .. fast 10 Jahre .. ich frage Dich, bist du wirklich so schlecht bezahlt?
Ich frag mal ironisch, für DOS muss es aber nicht mehr funktionieren, oder?

Wieviel Stunden sitzt Du schon an dem Problem?
Schenk Deinem Kunden eine Windows XP Lizenz zu Deinem Programm dazu ..

gibts für 50 Euro bei Amazon .. hmmm

http://www.amazon.de/Windows-Profess...4644512&sr=1-4

dann läuft das ...

sorry, wenn ich jetzt böse war ...

SirThornberry 15. Feb 2009 08:05

Re: Screenshot eines anderen Programmes mit wm_print
 
@stoxx: Windows2000 ist ein Betriebssystem was noch sehr häufig verwendet wird und das nicht wegen fehlendem Geld sondern weil es sehr stabil ist. Ich kenne einige welche Windows2000 bevorzugen.

Bernhard Geyer 15. Feb 2009 09:21

Re: Screenshot eines anderen Programmes mit wm_print
 
Es gibt sogar noch einige (größere Mrd. €) Firmen die auch ihre neuen PC generell mit W2K ausrüsten.

zeras 15. Feb 2009 10:00

Re: Screenshot eines anderen Programmes mit wm_print
 
@stoxx

Deine Info, ich soll myBMPWindowSnap nutzen, hatte ich in meinem Beitrag schon ausgeschlossen, da es nicht unter W2K läuft. Dies habe ich schon ausprobiert. Unter XP läuft dies.
Wie die Vorgänger schon sagen, gibt es viele Firmen, die noch W2K nutzen und die werden nicht wegen meinem Programm umsteigen.

Ziel ist eigentlich, die Screenschots aus einem VNC Viewer abzuspeichern. Das Programm war schon fertig und funktionierte, bis es auf W2K Rechnern installiert wurde. OK, man kann auch mit ALT-Druck eine Kopie machen und dann aus der Zwischenablage einlesen, beschneiden und abspeichern, aber die Funktion würde einiges noch vereinfachen und man kann eher mal was automatisieren.
Vielleicht gibt es ja auch einen VNC Viewer, der das direkt machen kann.
Möglicherweise kann Luckie nochmal drüberschauen. Er hat ja die Info gebracht, wie es zu machen wäre. Dies habe ich versucht, umzusetzen, aber offensichtlich gibt es da noch Verständnisprobleme mit Windows Funktionen.

EWeiss 15. Feb 2009 12:07

Re: Screenshot eines anderen Programmes mit wm_print
 
warum schickst du nicht einfach einen SendKey auf die Drucktaste.
und holst dir dann das bild aus der ablage.

Kannst dir dann den ganzen Code da oben sparen.

gruss Emil

zeras 15. Feb 2009 12:17

Re: Screenshot eines anderen Programmes mit wm_print
 
Danke für die Info.
Dann muss aber sichergestellt sein, dass der Viewer das aktive Fenster ist. Ansonsten hole ich mir irgendwas anderes in die Zwischenablage.
Vielleicht müßte ich dann erst den Viewer als aktives Fenster machen und dann könnte es klappen. Aber dazu fehlen mit wiederum die Windowsfunktionen. Jedenfalls kenne ich diese nicht.

EWeiss 15. Feb 2009 12:19

Re: Screenshot eines anderen Programmes mit wm_print
 
Zitat:

Zitat von zeras
Danke für die Info.
Dann muss aber sichergestellt sein, dass der Viewer das aktive Fenster ist. Ansonsten hole ich mir irgendwas anderes in die Zwischenablage.
Vielleicht müßte ich dann erst den Viewer als aktives Fenster machen und dann könnte es klappen. Aber dazu fehlen mit wiederum die Windowsfunktionen. Jedenfalls kenne ich diese nicht.

SetForegroundWindow könnte da helfen.
http://msdn.microsoft.com/de-de/library/bb979463.aspx

EDIT:
unter verwendung von SetActiveWindow.
Also erst Aktivieren.

Gruss Emil

SirThornberry 15. Feb 2009 12:24

Re: Screenshot eines anderen Programmes mit wm_print
 
Der Vorteil von von wm_print etc. ist ja das es nicht wirklich sichtbar sein muss etc. Es würde also theoretich auch mit Fenstern funktionieren die im Hintergrund sind.
Wenn man jetzt anfängt und meint die Drucktaste zu simulieren welche letztendlich nur das aktive Fenster als Bild in die Zwischenablage speichert ist es doch um einiges eleganter selbst den Screenshot mit GetDC, BitBlt etc. zu machen da man dadurch wenigstens die Zwischenablage in Ruhe lässt.
Aber wie bereits erwähnt ist Drucktaste etc. nicht das gleiche wie wm_print.

Garfield 15. Feb 2009 13:16

Re: Screenshot eines anderen Programmes mit wm_print
 
Wie man einen Screenshot von einem Window im Vordergrund macht sieht man zum Beispiel dort. Für ein inaktives Window benutzen sie allerdings auch PrintWindow, weil die davorliegenden Windows mit auf dem Bild sind.

stoxx 15. Feb 2009 13:50

Re: Screenshot eines anderen Programmes mit wm_print
 
okay .. dann gebe ich mich geschlagen, dass Windows2000 noch so oft benutzt wird, wusste ich nicht.

Ich hätte ja mal einen etwas frechen Vorschlag, kannst Du ihn mal probieren? Würde mich auch interesieren, ob das geht.
Hol Dir doch mal eine user32.dll von einem XP System. Benenne Sie kurz um und kopiere sie Dir in Dein eigenes Projektverzeichnis.
Und probiere dann, ob Du die Funktion aus der DLL einfach so nutzen kannst, oder ob diese wiederum andere DLL'S benötigt :-)

mkinzler 15. Feb 2009 13:52

Re: Screenshot eines anderen Programmes mit wm_print
 
Diese Abhängigkeiten könnte man auch mit dem DependenyWalker vorher abchecken

stoxx 15. Feb 2009 13:58

Re: Screenshot eines anderen Programmes mit wm_print
 
ich lerne immer gern dazu, aber ich kannte bisher nichtmal den Namen "Dependency Walker" :-)
kurz gegoogelt .. eine Frage stellt sich da, bekommmt man da nur raus, welche Abhängigkeiten für die gesamte DLL bestehen, oder auch für einzelne Funktionen in dieser DLL?

zeras 15. Feb 2009 14:03

Re: Screenshot eines anderen Programmes mit wm_print
 
Ich habe dies nun mit der angegebenen Funktion versucht, ein wenig umgebaut und zur Zeit funktioniert diese Funktion.
Ein Problem ist nur, dass, wenn ich die Zeit "sleep(5)" rausnehme, kommt nur ein schwarzes Fenster ohne Inhalt. Dies könnte Probleme bei anderen Rechnern bringen, denn es wird bestimmt nicht immer die Zeit reichen. Bei langsameren Rechner wird das wohl noch mehr sein.
Die Funktion holt erst die Info, welches Fenster genutzt werden soll und dann wird der Screenshot erzeugt. Man muss nur wissen, wie das Programm unter Windows heißt.
Jetzt fällt nur auf, dass das Fenster aktiv in den Vordergrund gebracht wird. Aber für meine Zwecke sollte es mehr oder weniger immer sichtbar sein.
Danke für die Tipps und eventuelle Verbesserungsmöglichkeiten.

Ergänzung: Wenn das Fenster teilweise über den rechten Rand hinausragt, wird es nicht mehr komplett kopiert, sondern es erscheint rechts ein weißer Streifen.

Delphi-Quellcode:
  procedure ScreenShot(wnd: HWND; destBitmap : TBitmap) ;
  var
    w,h : integer;
    DC : HDC;
    hWin : Cardinal;
    r : TRect;

  begin
    SetActiveWindow(wnd);
    SetForegroundWindow(wnd);
    sleep(5);
    hWin := GetForegroundWindow;
    dc := GetWindowDC(hWin) ;
    GetWindowRect(hWin,r) ;
    w := r.Right - r.Left;
    h := r.Bottom - r.Top;
    try
      destBitmap.Width := w;
      destBitmap.Height := h;
      BitBlt(destBitmap.Canvas.Handle,
               0,
               0,
               destBitmap.Width,
               destBitmap.Height,
               DC,
               0,
               0,
               SRCCOPY) ;
    finally
      ReleaseDC(hWin, DC) ;
    end;
  end;


//und Aufruf

  WinName:=Class4Screenshot; //hier den Namen des Programmes übnergeben

  wnd := FindWindow(PChar(WinName), nil); //Handle vom Viewer Fenster

  if wnd=0 then begin
    ShowMessage('Fenster nicht gefunden für Screenshot "'+WinName+'"');
  end
  else begin

    b := TBitmap.Create;
    try
      ScreenShot(wnd, b) ;
      Image1.Picture.Bitmap.Assign(b) ;
    finally
      b.FreeImage;
      FreeAndNil(b) ;
    end;

Garfield 15. Feb 2009 18:03

Re: Screenshot eines anderen Programmes mit wm_print
 
Zitat:

Zitat von zeras
Delphi-Quellcode:
    SetActiveWindow(wnd);
    SetForegroundWindow(wnd);
    sleep(5);
    hWin := GetForegroundWindow;

Zwei Variablen (wnd, hWin) für ein Handle?

Wenn das Fenster im Vorgergrund ist, braucht man nicht warten.
Delphi-Quellcode:
if GetForegroundWindow <> wnd
then begin
  SetForegroundWindow(wnd);
  sleep(5);
end;
Als Alternativen zum Fensterwechsel kannst Du Dir das mal ansehen: http://www.swissdelphicenter.ch/de/showcode.php?id=261
Zitat:

{
Manchmal funktioniert die SetForeGroundWindow Funktion
nicht so, wie sie sollte; besonders unter Windows 98/2000,
wenn ein anderes Fenster den Fokus hat.
ForceForegroundWindow ist eine "verbesserte" Version von
der SetForeGroundWindow API-Funktion, um ein Fenster in
den Vordergrund zu bringen.
}

zeras 15. Feb 2009 18:30

Re: Screenshot eines anderen Programmes mit wm_print
 
@Garfield

Ja, Du hast recht.
Ich hatte aus zwei verschiedenen Quellen was zusammengeschrieben. Nun habe ich optimiert und es klappt unter XP. Morgen kann ich unter W2K testen.
Deine Optimierung habe ich auch eingebaut.


Alle Zeitangaben in WEZ +1. Es ist jetzt 19:04 Uhr.

Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz