Autor Beitrag
HenryHux
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 542
Erhaltene Danke: 33

Windows 7 Premium
Delphi XE, Eclipse
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3747
Erhaltene Danke: 123

Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 542
Erhaltene Danke: 33

Windows 7 Premium
Delphi XE, Eclipse
BeitragVerfasst: Di 08.03.11 18:21 
user profile iconelundril hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 08.03.11 21:02 
user profile iconHenryHux hat folgendes geschrieben Zum zitierten Posting springen:
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 542
Erhaltene Danke: 33

Windows 7 Premium
Delphi XE, Eclipse
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Di 08.03.11 21:36 
user profile iconHenryHux hat folgendes geschrieben Zum zitierten Posting springen:
Also habe ich das richtig verstanden, dass ich für einen neuen Prozess eine eigene Executable erstellen muss?
Du kannst auch die eigene Exe wieder aufrufen. ;-)

user profile iconHenryHux hat folgendes geschrieben Zum zitierten Posting springen:
Was ich mich schon öfter gefragt habe, wie man solche StartParameter realisiert...
Schau dir z.B. die Parameter von ShellExecute an. Einer davon heißt Parameters und da gehören die auch rein. ;-)

Abfragen geht mit ParamCount und ParamStr. ;-)
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: Di 08.03.11 21:56 
An deiner Stelle würde ich versuchen, die Funktionen Thread-Safe zu machen.
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 542
Erhaltene Danke: 33

Windows 7 Premium
Delphi XE, Eclipse
BeitragVerfasst: 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?
ausblenden volle Höhe 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:
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, 00, 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 542
Erhaltene Danke: 33

Windows 7 Premium
Delphi XE, Eclipse
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Mi 09.03.11 17:31 
Schmeiß einfach TBitmap komplett heraus. Nach dem was user profile icondelfiphan geschrieben hat, sollte das klappen, wenn du nur mit den Handles arbeitest.

user profile iconHenryHux hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: 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.
ausblenden volle Höhe 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:
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, 00, Width, Height, SourceDC, Rect.Left, Rect.Top, SRCCOPY or CAPTUREBLT);
  ReleaseDC(DesktopWindow, SourceDC);
  DeleteDC(TargetDC);
end;

Für diesen Beitrag haben gedankt: HenryHux
HenryHux Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 542
Erhaltene Danke: 33

Windows 7 Premium
Delphi XE, Eclipse
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 542
Erhaltene Danke: 33

Windows 7 Premium
Delphi XE, Eclipse
BeitragVerfasst: 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 :
ausblenden volle Höhe 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:
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, 00, 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, 00, rechts, unten, SourceBitmap.Canvas.Handle, links, oben, SRCCOPY);
  randomize;
  TargetBmp.SaveToFile('L:\Tests\'+inttostr(random(99999))+'.bmp');
end;


und so der Aufruf :
ausblenden 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 :idea: :?:
Ich vermute mal stark, dass es hieran liegt : DesktopWindow := GetDesktopWindow;.
Wie ist denn die Bezeichnung für den ganzen Screen?
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3747
Erhaltene Danke: 123

Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
BeitragVerfasst: Mi 09.03.11 23:23 
user profile icondelfiphan hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 632
Erhaltene Danke: 121

Win 7 32-bit
Delphi 2006/XE
BeitragVerfasst: Do 10.03.11 08:18 
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Adressraum, den jeder Prozess und auch der Kernel zur Verfügung haben, denn das sind 2 GiB. ;-)
Wenn ich u. a. die XE-Hilfe zu den Compiler-Direktiven richtig interpretiere, kann man das aber ändern. Zitat: "IMAGE_FILE_LARGE_ADDRESS_AWARE Die Anwendung kann Adressen verarbeiten, die größer als 2 GB sind."
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19315
Erhaltene Danke: 1747

W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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.