Autor |
Beitrag |
HenryHux
      
Beiträge: 542
Erhaltene Danke: 33
Windows 7 Premium
Delphi XE, Eclipse
|
Verfasst: Di 08.03.11 18:10
Hi, bei mir werden in mehreren Threads gleichzeitig Screenshots gemacht.
Da das in den Funktionen leider nicht ohne Synchronisation geht, ist halt alles synchronisiert.
Doch bringen da dann Threads nicht wirklich einen Geschwindigkeitsvorteil, denn es kann ja immer nur eine synchronisierte Funktion ausgeführt werden.
Jetzt wollte ich mal fragen, ob es dazu irgendwelche Alternativen gibt, damit nicht immer alle Threads warten müssen, bis der andere Thread die Synchronisation freigibt.
Lg
|
|
elundril
      
Beiträge: 3747
Erhaltene Danke: 123
Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
|
Verfasst: Di 08.03.11 18:18
Du könntest gleich mehrere Prozesse starten anstatt "nur" mehrere Threads. Jeder Prozess kann dann unabhängig arbeiten. Musst das ganze dann halt mit einer art kommunikation steuern.
lg elundril
_________________ This Signature-Space is intentionally left blank.
Bei Beschwerden, bitte den Beschwerdebutton (gekennzeichnet mit PN) verwenden.
|
|
HenryHux 
      
Beiträge: 542
Erhaltene Danke: 33
Windows 7 Premium
Delphi XE, Eclipse
|
Verfasst: Di 08.03.11 18:21
elundril hat folgendes geschrieben : | Du könntest gleich mehrere Prozesse starten anstatt "nur" mehrere Threads. Jeder Prozess kann dann unabhängig arbeiten. Musst das ganze dann halt mit einer art kommunikation steuern.
|
Ist ein neuer Prozess immer mit dem erneuten Öffnen der Executable verbunden, oder kann ich neue Prozesse wie Threads während der Laufzeit erzeugen?
lg
E: Auch jaenicke hat mir in nem älteren Thread schon geraten : Zitat: | Lagere die Prüfung nicht nur in einen eigenen Thread, sondern in einen anderen Prozess aus. Via IPC bekommst du dann das Ergebnis mitgeteilt und gut ist es. Dann sind die ganzen Synchronisierungsprobleme vom Tisch. |
Dann denke ich, werde ich das so mal realisieren. Doch wie gehe ich da am besten dran? Wäre es möglich nur die betroffenen Funktionen "auszulagern"?
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 08.03.11 21:02
HenryHux hat folgendes geschrieben : | Dann denke ich, werde ich das so mal realisieren. Doch wie gehe ich da am besten dran? Wäre es möglich nur die betroffenen Funktionen "auszulagern"? |
Theoretisch ja, praktisch würde ich das besser entkoppeln.
Die wohl beste Variante wären DataSnap oder ähnliche Sachen, aber das wird mit deiner Delphiversion kaum gehen.
Die billigste und einfachste Variante wären schlichte Windows Messages. Du kannst dem neuen Prozess einfach das Handle mitgeben, an die es die Antwort schicken soll, und damit hat sich das schon fast.
Und dann gibt es da noch eine Unmenge anderer Möglichkeiten: Pipes, MMFs, ...
Aber ich würde dir wirklich raten einfach einmal ein paar Windows Messages hin- und herzuschicken und zu schauen wie das so läuft, das wird denke ich die einfachste Lösung sein.
Der Datenaustausch kann dann zusätzlich über Shared Memory / MMFs passieren.
Für diesen Beitrag haben gedankt: HenryHux
|
|
HenryHux 
      
Beiträge: 542
Erhaltene Danke: 33
Windows 7 Premium
Delphi XE, Eclipse
|
Verfasst: Di 08.03.11 21:06
Ok, danke so werde ich da mal drangehen.
Also habe ich das richtig verstanden, dass ich für einen neuen Prozess eine eigene Executable erstellen muss?
Und die rufe ich immer wieder/oder einmal pro Thread auf und übergebe ihr beim Start den ThreadHandle.
Was ich mich schon öfter gefragt habe, wie man solche StartParameter realisiert...
Vielen Dank,
Lg
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 08.03.11 21:36
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Di 08.03.11 21:56
An deiner Stelle würde ich versuchen, die Funktionen Thread-Safe zu machen.
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Di 08.03.11 21:59
Wenn das so einfach wäre auf visuelle Objekte threadsicher zuzugreifen...
Keine Ahnung ob das anders ist, wenn man nur mit Handles, also DCs, arbeitet, aber ich glaube nicht.
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Di 08.03.11 22:09
So ungefähr in dieser Reihenfolge: GetDC(GetDesktopWindow), CreateCompatibleDC, CreateCompatibleBitmap, SelectObject, BitBlt
Wenn du Handles nicht über Threadgrenzen hinweg verwendest, solltest du keine Schwierigkeiten bekommen.
Alternativ kann man auch über DirectX oder Windows Media Screenshots erstellen.
Ich frage mich allerdings, was du denn damit anstellen willst. Wieso in mehreren Threads gleichzeitig Screenshots? Wenn man die gleichzeitig macht, kriegt man auch das gleiche Ergebnis.
|
|
HenryHux 
      
Beiträge: 542
Erhaltene Danke: 33
Windows 7 Premium
Delphi XE, Eclipse
|
Verfasst: Di 08.03.11 22:37
Die Positionen von den Screenshots sind verschieden und werden auch anders verarbeitet und das bekomme ich nicht in einen Thread.
Ich habe im Prinzip nur 2 Funktionen die synchronisiert werden. Einmal aus einer ImgList die Methode GetBitmap, die stört mich aber nicht groß, weil die nicht viel Zeit in Anspruch nimmt.
Außerdem könnte ich da leicht auf alternative Methoden umspringen. Anders siehts da bei der Prozedur für den Screenshot aus. Wäre natürlich ideal, wenn ich die erst garnicht synchronisieren bräuchte.
Wie könnte es denn klappen mit den Methoden die du angesprochen hast?
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:
| procedure TMain.Screenshot; var DesktopHandle: HDC; ShotTargetBitmap : TBitmap; begin if not Assigned(SynchP.TargetBmp) then Exit; Shottargetbitmap := TBitmap.Create; try ShotTargetbitmap.Width := SynchP.Bounds.Right - SynchP.Bounds.Left; ShotTargetbitmap.Height := SynchP.Bounds.Bottom - SynchP.Bounds.Top; SynchP.TargetBmp.Width := SynchP.Rechts - SynchP.Links; SynchP.TargetBmp.Height := SynchP.Unten - SynchP.Oben; DesktopHandle := GetDC(0); try BitBlt(ShotTargetBitmap.Canvas.Handle, 0 ,0, SynchP.bounds.Right-SynchP.bounds.left, SynchP.bounds.Bottom - SynchP.bounds.top, DesktopHandle, SynchP.bounds.Left, SynchP.bounds.Top,SRCCOPY); BitBlt(SynchP.TargetBmp.Canvas.Handle, 0, 0, SynchP.Rechts, SynchP.Unten, ShotTargetBitmap.Canvas.Handle, SynchP.Links, SynchP.Oben, SRCCOPY); finally ReleaseDC(0, DesktopHandle); end; finally FreeAndNil(ShotTargetBitmap); end; end;
procedure TMain.ScreenshotSynched(Bounds2:TRect; Links2, Rechts2, Oben2, Unten2 : Integer; var Bmp : TBitmap); begin with SynchP do begin Bounds := Bounds2; Links := Links2; Rechts := Rechts2; Oben := Oben2; Unten := Unten2; end; SYNCHRONIZE(ScreenShot); Bmp.Assign(SynchP.TargetBmp); end; |
Screenshots werden nur im jeweiligen Thread benötigt, der auch die Funktion aufruft.
Ich bin für jeden Vorschlag offen =)
Lg
|
|
HenryHux 
      
Beiträge: 542
Erhaltene Danke: 33
Windows 7 Premium
Delphi XE, Eclipse
|
Verfasst: Mi 09.03.11 17:00
Wollte nochmal kurz nachfragen.
Das einzige was in der Prozedur oben synchronisiert werden muss ist doch das GetDC(0);, BitBlt muss soweit ich weiß doch nicht synchronisiert werden, oder? (ausgenommen das Erstellen der Bitmaps, die könnte man ja auch anders erzeugen)
Zumindest, wenn ich mit der Annahme richtig liege dürfte es doch genügen aus dem DesktopHandle := GetDC(0); ein CreateCompatibleDC(GetDesktopWindow); zu machen?
Lg
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Mi 09.03.11 17:31
Schmeiß einfach TBitmap komplett heraus. Nach dem was delfiphan geschrieben hat, sollte das klappen, wenn du nur mit den Handles arbeitest.
HenryHux hat folgendes geschrieben : | Das einzige was in der Prozedur oben synchronisiert werden muss ist doch das GetDC(0);, BitBlt muss soweit ich weiß doch nicht synchronisiert werden, oder? |
Genau das wird vermutlich knallen, denn dann erzeugst du das Handle in einem anderen Kontext als dein Thread. Aber ich habe mich damit nie so genau beschäftigt, nur eben gemerkt, dass TBitmap im Thread keine gute Idee ist.
Für diesen Beitrag haben gedankt: HenryHux
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Mi 09.03.11 20:10
Fehlerbehandlung darfst du noch selbst implementieren. Du bekommst hier einfach ein HBITMAP. Ob du damit arbeiten kannst, ist eine andere Frage. Mit GetDIBits kommst du z.B. an die Rohdaten.
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:
| type TScreenShot = class private FBitmap: HBITMAP; procedure Clear; public property Bitmap: HBITMAP read FBitmap; procedure Execute(Rect: TRect); destructor Destroy; override; end;
procedure TScreenShot.Clear; begin DeleteObject(FBitmap); FBitmap := 0; end;
destructor TScreenShot.Destroy; begin Clear; inherited; end;
procedure TScreenShot.Execute(Rect: TRect); const CAPTUREBLT = $40000000; var Width, Height: Integer; DesktopWindow: HWND; SourceDC, TargetDC: HDC; begin if FBitmap <> 0 then Clear;
Width := Rect.Right - Rect.Left; Height := Rect.Bottom - Rect.Top;
DesktopWindow := GetDesktopWindow; SourceDC := GetDC(DesktopWindow); TargetDC := CreateCompatibleDC(SourceDC); FBitmap := CreateCompatibleBitmap(SourceDC, Width, Height); SelectObject(TargetDC, FBitmap); BitBlt(TargetDC, 0, 0, Width, Height, SourceDC, Rect.Left, Rect.Top, SRCCOPY or CAPTUREBLT); ReleaseDC(DesktopWindow, SourceDC); DeleteDC(TargetDC); end; |
Für diesen Beitrag haben gedankt: HenryHux
|
|
HenryHux 
      
Beiträge: 542
Erhaltene Danke: 33
Windows 7 Premium
Delphi XE, Eclipse
|
Verfasst: Mi 09.03.11 20:37
Vielen Dank, erste Versuche sahen schonmal vielversprechend aus.
Mit einem HBITMAP kann ich direkt nichts anfangen, aber www.delphipraxis.net...hbitmap-tbitmap.html hat geholfen =)
Wenn alles geklappt hat, oder auch nicht, melde ich mich nochmal.
Lg
|
|
HenryHux 
      
Beiträge: 542
Erhaltene Danke: 33
Windows 7 Premium
Delphi XE, Eclipse
|
Verfasst: Mi 09.03.11 22:52
So..
Erstmal nochmal vielen Dank, es klappt im Ansatz.
Doch habe ich noch ein Problem.
Wenn ich mehrere Threads jetzt hiermit Screenshots machen lasse :
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:
| procedure TScreenShot.Execute(Bounds: TRect); const CAPTUREBLT = $40000000; var Width, Height: Integer; DesktopWindow: HWND; SourceDC, TargetDC: HDC; begin if FBitmap <> 0 then Clear;
Width := Bounds.Right - Bounds.Left; Height := Bounds.Bottom - Bounds.Top;
DesktopWindow := GetDesktopWindow; SourceDC := GetDC(DesktopWindow); TargetDC := CreateCompatibleDC(SourceDC); FBitmap := CreateCompatibleBitmap(SourceDC, Width, Height); SelectObject(TargetDC, FBitmap); BitBlt(TargetDC, 0, 0, Width, Height, SourceDC, Bounds.Left, Bounds.Top, SRCCOPY or CAPTUREBLT);
ReleaseDC(DesktopWindow, SourceDC); DeleteDC(TargetDC); end;
procedure TScreenshot.Cut(Links, Rechts, Oben, Unten : Integer; SourceBitmap : TBitmap; var TargetBmp : TBitmap); begin TargetBmp.Width := Rechts - Links; TargetBmp.Height := Unten - Oben; Windows.BitBlt(TargetBmp.Canvas.Handle, 0, 0, rechts, unten, SourceBitmap.Canvas.Handle, links, oben, SRCCOPY); randomize; TargetBmp.SaveToFile('L:\Tests\'+inttostr(random(99999))+'.bmp'); end; |
und so der Aufruf :
Delphi-Quelltext 1: 2: 3: 4:
| TS.Execute(Rect(Links,Oben,Links+100,Oben+100)); SourceBmp.Handle := TS.Bitmap;
TS.Cut(50,100,90,100, SourceBmp, TargetBmp); |
(Bitmaps sind nicht im Kontext des Threads erzeugt).
, dann bleiben von 100 Screenshots vlt 5-10 einfach weiß. Auch wenns die ganze Zeit die gleiche Stelle ist, springt eins einfach aus der Reihe...
Ist aber auch, wenn ich nur einen Thread laufen lasse, verstärkt sich nicht bei mehreren Threads.
Kommt jemand spontan auf ne Idee?
Lg
Edit: Ich habe jetzt mal nicht die kleinen geschnittenen Bitmaps abgespeichert, sondern die nicht geschnittenen.
Und dort ist zu erkennen, dass nicht einfach nichts auf dem Bild ist, sondern das Interface von Delphi, was jedoch während der Zeit minimiert war
Ich vermute mal stark, dass es hieran liegt : DesktopWindow := GetDesktopWindow;.
Wie ist denn die Bezeichnung für den ganzen Screen?
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: Mi 09.03.11 23:13
Bitmaps sind Kernelobjekte und der Kernel hat bekanntlich nur max. 2 GB auf einem 32-bit System (unabhängig von den Prozessen). Da nützt es auch nichts, 4 GB RAM zu haben. Ausserdem kann Speicherfragmentierung dazu führen, dass der Kernel keinen grossen, zusammenhängenden Block mehr finden kann; auch wenn insgesamt noch genügend Speicher bzw. Virtueller Address Space da wäre. Gibst du die Objekte auch immer schön frei? Hast du eine Fehlerbehandlung reinprogrammiert? Allenfalls gibt eine GDI-Funktion 0 zurück, dann solltest du mal schauen, was die Fehlermeldung war.
Ausserdem verwendest du ja immer noch TBitmap um das ganze rauszulesen. Ich bin mir nicht sicher, ob TBitmap thread-safe ist oder es sicher ist, einem TBitmap ein Handle aus einem anderen Thread zuzueweisen. Da müsste man sich mal den Code anschauen. Ausserdem bin ich mir nicht sicher, ob du Handles jetzt doch threadübergreifend verwendest (evtl. indirekt über TBitmap). Da muss man etwas aufpassen.
|
|
elundril
      
Beiträge: 3747
Erhaltene Danke: 123
Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
|
Verfasst: Mi 09.03.11 23:23
delfiphan hat folgendes geschrieben : | Bitmaps sind Kernelobjekte und der Kernel hat bekanntlich nur max. 2 GB auf einem 32-bit System (unabhängig von den Prozessen). |
Ich dachte 3,4 GB oder so wären möglich beim 32-bit system.
_________________ This Signature-Space is intentionally left blank.
Bei Beschwerden, bitte den Beschwerdebutton (gekennzeichnet mit PN) verwenden.
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 10.03.11 06:41
Du verwechselst gerade den Speicher, den Windows zur Verfügung hat mit dem virtuellen Adressraum, den jeder Prozess und auch der Kernel zur Verfügung haben, denn das sind 2 GiB. 
|
|
Gerd Kayser
      
Beiträge: 632
Erhaltene Danke: 121
Win 7 32-bit
Delphi 2006/XE
|
Verfasst: Do 10.03.11 08:18
|
|
jaenicke
      
Beiträge: 19315
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Do 10.03.11 09:18
Auch dann gehen aber nur 3 GiB und das auch nur, wenn Windows entsprechend konfiguriert ist, zumindest unter 32-Bit Systemen.
|
|
|