Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - BitBlt streikt


HenryHux - Fr 04.02.11 21:16
Titel: BitBlt streikt
Hi,

ich wende mich mal wieder an euch, da ich absolut nicht mehr weiter weiß.
Und zwar habe ich eine ältere Procedur, welche einen Screenshot macht, und diesen schneidet.
So sieht sie aus:


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:
procedure Screenshot(Bounds:TRect; Links, Rechts, Oben, Unten : Integer; var TBmp : TBitmap);
var DeskCanvas: TCanvas;
    ShotTargetBitmap : TBitmap;
    Cut : TRect;
begin
  try
    try
      Shottargetbitmap := TBitmap.create;  //Nur für den eigentlichen Screenshot
      ShotTargetbitmap.Width := (Bounds.Right - Bounds.left);
      ShotTargetbitmap.Height := (Bounds.Right - Bounds.Left);

      TBmp:=TBitmap.Create;                //Soll fertiges, geschnittenes Bmp enthalten
      TBmp.Width := Rechts - Links;
      TBmp.Height := Unten - Oben;

      DeskCanvas := TCanvas.Create;
      DeskCanvas.Handle := GetDC(0);

      Cut.Left := Links;
      Cut.Top := Oben;
      Cut.Right := Rechts;
      Cut.Bottom := Unten;

      BitBlt(ShotTargetBitmap.Canvas.Handle, 0 ,0, bounds.Right-bounds.left,
             bounds.Bottom-bounds.top, DeskCanvas.handle, bounds.Left,
             bounds.Top,SRCCOPY);
      BitBlt(TBmp.Canvas.Handle, 00, Cut.Right, Cut.Bottom,
             ShotTargetBitmap.Canvas.Handle, Cut.Left, Cut.Top, SRCCOPY);


      TBmp.savetofile('C:\TBmp2 '+inttostr(Random(50000))+'.bmp');                //Testweise speichern
      ShotTargetBitmap.savetofile('C:\TBmp1 '+inttostr(Random(50000))+'.bmp');


    finally
      ReleaseDC(0,DeskCanvas.Handle);
      FreeAndNil(deskcanvas);
      FreeAndNil(ShotTargetBitmap);
    end;
  except
    on E : Exception do
    begin
      showmessage('Exception in Screenshot : ' + E.Message);
      FreeAndNil(TBmp);
    end;
  end;
end;


Das Problem, welches ich erst kürzlich bemerkt habe ist, dass das fertig geschnittene Bitmap, einfach nur weiß ist.
Zwar richtig geschnitten, aber 0 Inhalt.
Bei ca jedem 6ten bis 7ten Bmp, hat er es aber genau so gemacht wie gewollt, sprich mir Inhalt.
Meiner Meinung nach, müsste es an dem letzten BitBlt liegen, denn alle! abgespeicherten ShotTargetBmps sind richtig.
Zu sagen bleibt, dass keine einzige Exception ausgelöst wurde.
Habe noch ein paar mal sleep() eingebaut, was aber das gleiche, ist also nicht weil die Prozedur zu schnell läuft.
Vielleicht wisst ihr ja was.

Lg


BenBE - Fr 04.02.11 21:25

Was sagt GetLastError?


HenryHux - Fr 04.02.11 21:39

user profile iconBenBE hat folgendes geschrieben Zum zitierten Posting springen:
Was sagt GetLastError?


Der sagt bei jedem mal wo das Bild weiß bleibt 87.
Nur kann ich damit nichts anfangen, laut http://msdn.microsoft.com/en-us/library/ms681382 ist es ein falcher Parameter.
Doch ich rufe immer die gleichen auf, also wodran könnte es liegen?

lg


jaenicke - Fr 04.02.11 21:43

Naja, fällt dir hier nicht etwas auf... ;-)
user profile iconHenryHux hat folgendes geschrieben Zum zitierten Posting springen:

Delphi-Quelltext
1:
2:
      ShotTargetbitmap.Width := (Bounds.Right - Bounds.left);
      ShotTargetbitmap.Height := (Bounds.Right - Bounds.Left);


HenryHux - Fr 04.02.11 21:55

Ok, danke wahr denke auch noch ein Fehler, aber nicht der, den ich suche..

Delphi-Quelltext
1:
2:
      ShotTargetbitmap.Width := (Bounds.Right - Bounds.left);
      ShotTargetbitmap.Height := (Bounds.Bottom - Bounds.Top);

Damit ist es genau das gleiche.
Der Aufruf sieht so aus :

Delphi-Quelltext
1:
Screenshot(Rect(Links,Oben,Links+100,Oben+100),32,43,64,90, TBmp);                    


Wobei TBmp dort wo es aufgerufne wird nur lokal deklariert ist und noch nicht mit create erzeugt ist.
Es wird lediglich mit .free am Ende beendet.

Sonst ne Idee wodran es liegen könnte?

Lg


jaenicke - Fr 04.02.11 22:00

Kann es sein, dass du das fälschlicherweise in einem Thread aufrufst?

Und wozu brauchst du überhaupt DeskCanvas? :gruebel:


HenryHux - Fr 04.02.11 22:06

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Kann es sein, dass du das fälschlicherweise in einem Thread aufrufst?

Und wozu brauchst du überhaupt DeskCanvas? :gruebel:


Gehts auch ohne? o.O

Aber vermutung ist richtig, ist eine Klasse, deren Instanz in einem Thread aufgerufen wird.
Ist dasn Problem?


jaenicke - Fr 04.02.11 22:20

user profile iconHenryHux hat folgendes geschrieben Zum zitierten Posting springen:
Wobei TBmp dort wo es aufgerufne wird nur lokal deklariert ist und noch nicht mit create erzeugt ist.
Es wird lediglich mit .free am Ende beendet.
Äußerst schlechter Stil. Ressourcen sollten immer dort freigegeben werden, wo sie reserviert werden. Und schon gar nicht innerhalb einer Prozedur erzeugt und nach außen gegeben werden.
Und auch die Fehlerbehandlung gehört nach draußen, nicht in die Prozedur.
Außerdem hast du die Ressourcenschutzblöcke falsch angewendet.
Außerdem brauchst du kein var, da eine Bitmap ohnehin schon eine Objektreferenz ist, wenn du es außen erzeugst.
Und zu deinem Cut-Rect: Variablen so im Grunde nur durch andersnamige zu ersetzen, führt höchstens zu Verwirrung...

user profile iconHenryHux hat folgendes geschrieben Zum zitierten Posting springen:
user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Und wozu brauchst du überhaupt DeskCanvas? :gruebel:
Gehts auch ohne? o.O
Ja, denn du benutzt ja nur den HDC, nicht das Canvas Objekt...

Korrigiert also so ca.:

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:
procedure Screenshot(Bounds:TRect; Links, Rechts, Oben, Unten : Integer; TargetBmp : TBitmap);
var
  DesktopHandle: HDC;
  ShotTargetBitmap: TBitmap;
begin
  if not Assigned(TargetBmp) then
    Exit;
  Shottargetbitmap := TBitmap.Create;  //Nur für den eigentlichen Screenshot
  try
    ShotTargetbitmap.Width := Bounds.Right - Bounds.Left;
    ShotTargetbitmap.Height := Bounds.Bottom - Bounds.Top;
    TargetBmp.Width := Rechts - Links;
    TargetBmp.Height := Unten - Oben;

    DesktopHandle := GetDC(0);
    try
      BitBlt(ShotTargetBitmap.Canvas.Handle, 0 ,0, bounds.Right-bounds.left,
        bounds.Bottom - bounds.top, DesktopHandle, bounds.Left,
        bounds.Top,SRCCOPY);
      BitBlt(TargetBmp.Canvas.Handle, 00, Rechts, Unten,
        ShotTargetBitmap.Canvas.Handle, Links, Oben, SRCCOPY);

      TargetBmp.savetofile('K:\TBmp2 '+inttostr(Random(50000))+'.bmp');                //Testweise speichern
      ShotTargetBitmap.savetofile('K:\TBmp1 '+inttostr(Random(50000))+'.bmp');
    finally
      ReleaseDC(0, DesktopHandle);
    end;
  finally
    FreeAndNil(ShotTargetBitmap);
  end;
end;


user profile iconHenryHux hat folgendes geschrieben Zum zitierten Posting springen:
Aber vermutung ist richtig, ist eine Klasse, deren Instanz in einem Thread aufgerufen wird.
Ist dasn Problem?
Ja, denn du benutzt eine Leinwand zum Zeichnen. Das geht nicht in einem Thread, sondern nur synchronisiert mit dem Hauptthread.


HenryHux - Fr 04.02.11 23:22

Ok, danke habe jetzt einiges verändert.
Doch habe ich das mit dem Synchronisieren noch nicht ganz hinbekommen.
Ich kenne nur Synchronize und TCriticalSection, muss aber zugeben, dass ich noch nie damit gearbeitet habe.
Mit Synchronize sollte ich die Prozedur ja aufrufen können, aber er macht es nur, wenn ich keine Parameter übergebe.
Muss ich aber. Zumindest kenne ich keinen anderen Weg.

Delphi-Quelltext
1:
SYNCHRONIZE(Screenshot(Rect(Links,Oben,Links+100,Oben+100),32,43,64,90, TBmp));                    

Das klappt nicht, wäre auch zu einfach gewesen :D
Wie synchronisiere ich sowas am Besten?
Vielleicht ein paar gute Tipps? =)


jaenicke - Fr 04.02.11 23:25

Du musst die Daten unter private als Feld speichern. Dann kannst du eine Methode ohne Parameter synchronisiert aufrufen und darin diese Daten wiederum verwenden um die Screenshot-Prozedur aufzurufen.


HenryHux - Fr 04.02.11 23:41

user profile iconjaenicke hat folgendes geschrieben Zum zitierten Posting springen:
Du musst die Daten unter private als Feld speichern. Dann kannst du eine Methode ohne Parameter synchronisiert aufrufen und darin diese Daten wiederum verwenden um die Screenshot-Prozedur aufzurufen.

:autsch: :autsch:

Da hätte ich selber drauf kommen müssten... Naja
Habe aber noch ein Problem:

Delphi-Quelltext
1:
Synchronize(Screenshot);                    

Wird zu [DCC Fehler] UnCardScanning.pas(158): E2066 Operator oder Semikolon fehlt .
Ich denke, dass ich noch die Instanz oder Ähnliches angeben muss.
Doch TThread.Synchronize(Screenshot)
[DCC Fehler] UnCardScanning.pas(159): E2389 Auf Protected-Element 'TThread.Synchronize' kann hier nicht zugegriffen werden


jaenicke - Fr 04.02.11 23:58

Eigentlich ist das so schon richtig, schließlich bist du ja schon im Thread. Mich irritiert, dass du die Methode Screenshot synchronisieren willst, obwohl so ja schon die Prozedur eben hieß. Bist du da durcheinander gekommen?

Ich vermute jedenfalls, dass da irgendetwas anderes noch Screenshot heißt als deine Methode...

Sonst: Wie sieht denn der Quelltext dort sonst aus?


HenryHux - Sa 05.02.11 00:06

Ich bin immoment viel am basteln.
Die Procedure Screenshot ist lokal in einer Funktion, die ReadIn heißt.
Und diese ist Bestand von einer Klasse, welche dann in einem Thread aufgerufen wird.
Ist das vlt ein Problem?


jaenicke - Sa 05.02.11 00:11

Das Synchronize gehört in das Execute des Threads.

Theoretisch kannst du die Instanz des Threads mitgeben, aber das wäre nicht gerade sauber...

In neueren Delphiversionen kann man auch außerhalb eines Threads synchronisieren. Aber am besten pack das ins Execute, da gehört es hin. Mit dem private meinte ich das des Threads.


HenryHux - Sa 05.02.11 00:52

Ok, habe es jetzt hinbekommen.
Zwar etwas hässlich, aber es klappt.
Habe einfach die ganze Klasse in den Thread gesteckt, wusste einfach nicht wie ich es sonst machen sollte.
Werde mich dann in nächster Zeit öfter damit beschäftigen müssen, mit Zeit kommt Rat =)

Vielen Dank, jaenicke!

Lg