Hi Zhen, willkommen zurück...
Am Besten du packst mal deine Solution so wie sie ist zusammen und schiebst sie hier in den Thread. Ist schwer zu sagen, welche Methode du nun verwendest und wie der Code ausschaut. Immerhin haben lynxx und ich so an den verschiedensten Sachen experimentiert und kräftig gepostet, sodaß es doch recht verwirrend sein kann. Am Ende waren wir uns, glaube ich, einig, dass es schlecht ist, das Rechteck der Änderungen zu ermitteln, weil es zum einen sehr aufwändig, kompliziert und fehleranfällig ist und es viele Situationen geben wird, wo du gezwungenermaßen den kompletten oder fast kompletten Screen schicken musst. Ein Beispiel: Oben links in der Ecke ändert sich ein Pixel, unten Rechts (z.B. die Uhr) ändert mind. ein Pixel, dann sendest du den vollen Screen. Und das wird sehr häufig passieren.
Aber man kann sich auch andere Lösungen überlegen, z.B. jeden Screen in viele kleine Rechtecke (ala 32x32 Pixel) aufteilen, diese prüfen und wenn es Unterschiede gibt, dann senden. Ergebnis wäre, dass auch minimale Änderungen in weit entfernten Bereichen des Screens das zu sendende Datenvolumen relativ klein belassen, was letztendlich der Übertragung und Darstellung auf dem Remoteclient zu Gute kommt. Ein anderer Ansatz wäre, dass man das Bild als gesamtes Array betrachtet, dieses in 256 Pixel lange Ketten zerlegt, diese betrachtet und bei Änderungen nur die geänderten Pixelketten mit dem Index sendet. Beim Prüfen des Screens bin ich da auf meinem Rechner (1280x1024, 32 Bit, 3.4GHz) selbst bei vielen Änderungen im Screen mit ca 6- 10ms dabei. Das Zusammensetzen geht ebenfalls sehr schnell, einfach an dem Index die Pixeldaten drüber bügeln. Hier entscheidet nur die Übertragungsgeschwindigkeit im Netz und das .Net interne Zeichnen des Screens in die Picturebox.
Zum GC: Als ich mit der .Net Sache anfing, hielt ich damals deinen heutigen Vorschlag für eine superdupper Idee. Mittlerweile bin ich persönlich strikt dagegen, weil (a) man besser den GC seine Sachen selbst regeln lässt (Ich kenne nicht den GC in seiner Tiefe und kann somit nicht abschätzen ob es ok ist!), (b) der Programmierfehler, das Speicherleck, weiterhin im Programm ist und somit weiter unabschätzbare Probleme verursachen kann und (c) auf Faulheit / Schlampigkeit des Programmierers hindeutet! Das ist nur das Konsumieren einer Schmerztablette bei Kopfschmerzen, ohne die Ursache zu kennen.
Aber das sind halt sehr subjektive Dinge, die mich von Sachen wie GC.Collect() etc. abhalten. Ausserdem würde es mich verrückt machen, wenn ich da ne Sache programmiert habe, aber nicht genau weiß, wieso ein Problem entsteht.
Das eigentliche Problem ist jedoch sehr leicht zu finden. Mit jedem Screenshot wird durch die CaptureScreen.dll ein neues mehrere MB großes Bitmap im Speicher erzeugt, ohne dass das vorhergehende mit Bitmap.Dispose() für den GC zur Bereinigung freigegeben wird. D.h. mehrere MB große Bitmap liegen im Speicher bis irgendwann der GC selber sagt, nun wech mit dem alten Scheiß. Angenommen der Timer Tick wird alle 100ms ausgelöst (so hatte ich ihn bei mir eingestellt) dann kommen pro Sekunde ca 50 MB nutzloser Speicher dazu. Dann noch das Problem, dass der GC bei diesen "Langzeitobjekten" von Haus aus lange wartet, bis er sie tatsächlich bereinigt. Alles zusammen werden dann sinnlos mehrere 100 MB verballert ohne Sinn und Verstand. Die Lösung: Es werden genau 2 Bitmaps erzeugt, bei jedem Durchgang getauscht und mit der Graphics.CopyScreen Methode neu befüllt.
Siehe lynxx Lösung (Post #70):
Aber hey, das einfachste von allem klappt natürlich ..
in der GetScreen()
if (newScreen == null || prevScreen == null || (newScreen != null & prevScreen != null && ((newScreen.Width * newScreen.Height) != (prevScreen.Width * prevScreen.Height))))
newScreen = new Bitmap(Screen.PrimaryScreen.Bounds.Width, Screen.PrimaryScreen.Bounds.Height, PixelFormat.Format32bppArgb);
Und da wo nur
prevScreen = newScreen;
steht:
Bitmap tmpBmp = prevScreen;
prevScreen = newScreen;
newScreen = tmpBmp;
Wenn du vorher noch den Speicher beider Bitmaps mit Marshal.AllocHGlobal selbst erzeugst und diesen IntPtr dann an einen der Overloads des Bitmapkonstruktors übergibst, kannst du dir auch das LockBits und UnlockBits beim Vergleich bzw. Zugriff auf die Bilddaten sparen. Im übrigen bleibt bei dieser Variante mein Rechner bei "schlanken" 40MB Speicher und der GC hat so gut wie nichts mehr zu tun. Aber vergiss nicht auch diesen Speicher an entsprechender Stelle (z.B. bei Änderung der Auflösung / Neuerstellen der Bitmaps) mit Marshal.Release wieder freizugeben, sonst hast du das gleiche Problem wie vorher nur mit dem Unterschied dass der GC nichts mehr freigibt und dein Rechner irgendwann swapped und dann das Programm später völlig den Geist aufgibt.
Also, Anregungen gibts genug, lass Taten folgen ...
Grüße
Rossibaer