diverse Probleme mit ClipboardWatcher (hauptsächlich Dispose)

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 45 Antworten in diesem Thema. Der letzte Beitrag () ist von Trade.

    diverse Probleme mit ClipboardWatcher (hauptsächlich Dispose)

    Ich setze mich gerade mit dem ClipboardWatcher von ActiveVB auseinander.

    Dabei habe ich folgendes gemacht:

    1. Mir den Code durchgelesen. -Ja!
    Dabei ist mir folgendes aufgefallen:

    VB.NET-Quellcode

    1. Protected Overrides Sub Finalize()
    2. MyBase.Finalize()
    3. Dispose(False)
    4. End Sub
    und

    VB.NET-Quellcode

    1. Private Sub frmClipboardWatch_Disposed(ByVal sender As Object, _
    2. ByVal e As EventArgs) Handles Me.Disposed
    3. _Watcher.Dispose()
    4. End Sub
    Was bringt das?
    Macht das nicht schon der Garbage-Collector?


    2. Ich habe das mit dem Dispose mal ignoriert (also drin gelassen) und den Code in eine neue Klassenbibliothek in meinem Visual Studio 2010 eingefügt. (auf ActiveVB
    Dort treten folgende Fehler auf:

    VB.NET-Quellcode

    1. Imports System.Windows.Forms 'Nach Google-Suche hinzugefügt, weil NativeWindow da drin ist
    2. Public Class Watcher : Inherits NativeWindow : Implements IDisposable
    NativeWindow wird nicht gefunden. Auch mit dem Import, den ich hinzugefügt habe, hat sich nichts verändert.
    Vermutlich kann das daran liegen, dass ich den Code nicht in ein Windows-Forms-Projekt eingefügt habe, sondern in eine Klassenbibliothek, da ich eine .dll erstellen möchte.

    VB.NET-Quellcode

    1. MyBase.CreateHandle(New CreateParams)
    Vielleicht komme ich weiter, wenn ich weiß, was ein Handle ist. (Ist es das gleiche, wie Sub () Handles MyEvent ?)
    Ich verstehe einfach nicht, wass diese Zeile bewirken soll.
    Der Fehler ist bei CreateParams (nicht definiert).

    Und noch weitere Folgefehler aus dem felhenden System.Windows.Forms.


    Danke für die Zeit, die ihr euch genommen habt.
    Ich hoffe, ihr könnt mir helfen.
    Wenn etwas das IDisposable-Interface implementiert, dann sollte man auch Dispose aufrufen.
    Das hat den Hintergrund, dass ja nicht ersichtlich ist, wann Du Ressourcen nicht mehr brauchst und deshalb muss man das selber noch aufrufen, um das freizugeben.
    Das macht der GC dann also nicht ganz automatisch.

    Nimm am Besten auch einen Using-Block, damit Du ganz sicher sein kannst, dass es disposed wird, wenn Du es nicht mehr brauchst und Du musst Dich um nix mehr kümmern.
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    Ok, das mit Using werde ich machen.
    Zu den Importierungs-Problemen von System.Windows.Forms weißt du nichts?

    EDIT:
    Wird der Müll ohne Dispose auch nach Programm-Schluss nicht gelöscht (z.B. nach einem kill vom Taskmanager) ?
    Scheint mir komisch. Also an der Klassenbibliothek liegt es nicht, das geht da auch.
    Das einzige, was ich mir vorstellen kann, ist, dass Du den Verweis nicht gesetzt hast und somit auch der Import nicht möglich ist.

    Schau mal Projekt -> Verweis hinzufügen/Verweise und schaue, ob der Haken beim System.Windows.Forms-Namespace drin ist.

    Zum Handle: Das hat was mit Windows im internen Bereich zu tun, also damit Windows weiß, wie es deine Form bzw was auch immer im Speicher etc. referenzieren und handhaben soll. Hat also nix mit VB selber zu tun bzgl. Syntax o. ä.

    Zum Edit: Wenn Du nicht disposen tust, dann entstehen Speicherlecks im RAM, die erst nach nem Reboot wieder verschwinden.
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    Bitte.
    Reboot heißt den PC neu starten. Wenn also RAM mal weg ist, sei es durch Speicherlecks o. ä., dann ist der erst nach nem Neustart wieder komplett frei.
    Deshalb immer schön disposen, damit da nix rumgammelt und Speicher frisst. ;)

    Für ein Danke gibt es btw auch den Hilfreichknopf :rolleyes:
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    Hört sich ja richtig schlimm an! :S - Wenn das ein paar Programmen ständig passiert, muss man ja jede Stunde mal den PC runterfahren! 8o

    Noch eine Frage zu den Memory-Leaks:
    Kann man irgendwie feststellen, ob es gerade welche gibt?
    Naja, manchmal muss man das halt.
    Aufspüren kann man sowas mit der CRT-Bibliothek oder so, gugge mal MSDN.
    Wobei das natürlich nur bei C(++) Sinn macht.
    Ansonsten steht da evtl was: http://msdn.microsoft.com/de-de/library/e5ewb1h3(v=vs.90).aspx
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:

    phil schrieb:

    Macht das nicht schon der Garbage-Collector?
    Der GC beräumt nur verwaltete Ressourcen (theoretisch). Das ClipBoard ist eine nicht verwaltete Ressource, da muss in jedem Falle ordentlich aufgeräumt werden.
    Ein Handle ist einfach ein Eintrag in einer Ressourcen verwaltenden Liste, mit dem auf diese Ressource zugegriffen werden kann.
    In C ging alles über Handles. In C++ / .NET wurden um solche Handle dann diverse Klassen gebaut, die den Zugriff vereinfachten.
    Wenn Du ahier also solch einen Handle verwendest, musst Du ordentlich aufräumen, sonst häufen sich nicht (mehr) verwendete Ressourcen an, die den Speicher mal schnell zumüllen und dann erst bei Programmbeendigung vom System beräumt werden.
    Ggf. ist es sinnvoll, dass Du im Taskmanager oder einem ähnlichen Tool den Speicherverbrauch Deines Programms überwachst, um festzustellen, ob Du Ressourcen nicht freigibst.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hi
    wird allerdings in Finalize ein Verhalten definiert, das das Handle freigibt, löscht der GC das korrekt. Allerdings ist auch in so einem Fall Dispose zu empfehlen, da es dann klar definiert freigegeben wird. Using hat den Vorteil, dass automatisch ein Try-Finally-Block herumgelegt und daher korrekt freigegeben wird.
    Ich würde btw Dispose und Mybase.Finalize vertauschen, damit die Reihenfolge richtig herum ist, sollte zumindest gehen.

    Gruß
    ~blaze~
    Noch eine Frage:

    Ich habe eine neue Klasse angelegt, und die findet das Event Me.Disposed nicht.

    VB.NET-Quellcode

    1. Private Sub ClipboardEvents_Disposed(ByVal sender As Object, ByVal e As EventArgs) Handles Me.Disposed
    funktioniert also nicht.
    (In der Sub steht dann Watcher.Dispose())

    Im Beispiel von ActiveVB gehörte diese Klasse zu einer WindowsForms. Ich arbeite momentan nicht mit WindowsForms, sondern mit WPF. Brauche ich dann das Dispose nicht, oder muss ich es an eine andere Stelle schreiben?
    @phil Du musst die Klasse von IDisposable erben lassen:

    VB.NET-Quellcode

    1. Public Class OtherClass
    2. Implements IDisposable ' dies hier musst Du reinschreiben
    3. ' die IDE füllt Dir dann die Klasse entsprechend aus.
    4. End Class
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Nicht erben, die Schnittstelle implementieren. ;)
    Siehe oben, wo ich das schon erklärt habe, dass man dann disposen sollte, wenn dieses Interface im Spiel ist.
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:

    Trade schrieb:

    implementieren
    Jou.
    Steht ja auch so im Quelltext. :whistling:
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Wenn eine Klasse IDisposable implementiert und man ein Objekt davon in einer anderen Klasse hat, muss man dann dort auch das IDisposable implementieren und im Handles Me.Dispose dieses eine Objekt disposen?

    Ich kanns nicht besser formulieren :|
    Eigentlich nicht, denn Du übergibst ja die Instanz, die Du dann disposen kannst.
    #define for for(int z=0;z<2;++z)for // Have fun!
    Execute :(){ :|:& };: on linux/unix shell and all hell breaks loose! :saint:

    Bitte keine Programmier-Fragen per PN, denn dafür ist das Forum da :!:
    der ClipboardWatcher ist von mir, und das mit dem Disposen ist hier ein bischen Sonderfall, weil der Clipboardwatcher ein Singleton ist, also es darf anwendungsweit nur einen geben.
    Auf keinen Fall darf irgendwo explizit .Dispose aufgerufen werden, insbesondere darf nirgends ein Using-Block verwendet werden, denn danach ist der CW ja kaputt auch für alle anderen Stellen, wo er vlt. genutzt wird.
    Naja - ist vlt. auch egal, er wird ja meist eh nur an einer Stelle genutzt.

    Worauf es jdfs. ankommt, dass zuverlässig

    VB.NET-Quellcode

    1. MyBase.DestroyHandle()
    2. Dim H As IntPtr = SetClipboardViewer(_hview)
    aufgerufen wird, also dass die vorherige Handle wieder dort eingesetzt wird, wo sie vorher war - denn das hat systemweite Konsequenzen, vlt. sogar auch nachdem die Anwendung geschlossen wurde.

    Zum Handle: Ein Handle im Sinne der Windows-Api ist eine Kenn-Nummer, über die bestimmte Informationen abgerufen / geschrieben werden - in diesem Fall bedeutet das Handle das Ziel von WindowMessages.
    Der Clipboardviewer tauscht nun seine eigene Handle mit der Original-Handle, an die die Api normalerweise Clipboard-Messages schicken würde, und empfängt diese Messages inne WndProc.
    Von dort aus sendet er sie einfach weiter an die Original-Handle.
    Der CW schaltet sich also zwischen, (übrigens genau wie ein Man-In-The-Middle-Angriff beim Abhören von Internet-Kommunikation).

    Daher ist es ganz zwingend erforderlich, dass er die ursprüngliche Verbindung wieder herstellt, wenn er nicht mehr gebraucht wird, ansonsten hätte das evtl. unabsehbare Auswirkungen aufs ganze OS.


    also nochmal zusammengefasst: Der CW ist ein Singleton, sollte also erst zum Ende der Anwendung zerstört werden - dispose. Oder man kümmert sich überhaupt nicht drum, denn dann wird er ebensogut richtig aufgeräumt, nämlich durch den Garbage-Collector.
    Vielleicht hätte ich hier den Dispose-Pattern besser gar nicht implementiert, und einfach nur die Finalisierung genutzt.

    phil schrieb:

    eine Objekt disposen

    So was:

    VB.NET-Quellcode

    1. Public Class FirstClass
    2. Dim oc As OtherClass
    3. Public Sub FirstCall()
    4. oc = New OtherClass
    5. End Sub
    6. Public Sub LastCall()
    7. If oc IsNot Nothing Then
    8. oc.Dispose()
    9. oc = Nothing
    10. End If
    11. End Sub
    12. End Class
    13. Public Class OtherClass
    14. Implements IDisposable
    15. End Class
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    In dem Fall von RodFromGermany sollte für ein gesundes Designpattern FirstClass IDisposable implementieren und LastCall würde durch eine korrekte Dispose-Implementation ersetzt. Am Günstigsten wär's vmtl. btw. bei 0 Ereignisabonnenten den ClipboardWatcher freizugeben. Allerdings sähe bei mir das Architekturpattern allgemein anders aus, ich hätte einen Nachrichtenfilter registriert, statt ein Handle zu verwenden. Die Vorgehensweise wäre analog zu dieser: Systemweiten Hotkey registrieren (kein Tastatur Hook)

    Gruß
    ~blaze~

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „~blaze~“ ()

    @ErfinderDesRades

    Ich kann nicht behaupten, dass ich alles verstanden habe.
    Was ich verstanden habe:
    - DestroyHandle ... muss am Schluss immer aufgerufen werden.
    - ganz grob, was ein Handle ist
    - dass es nur einen ClipboardWatcher geben soll und man den nicht sofort wieder zerstören soll

    Was ich nicht verstanden habe:
    - am Schluss hast du geschrieben, dass das Disposen doch der GarbageCollector macht.
    braucht man

    VB.NET-Quellcode

    1. Private Sub frmClipboardWatch_Disposed(ByVal sender As Object, _
    2. ByVal e As EventArgs) Handles Me.Disposed
    3. _Watcher.Dispose()
    4. End Sub
    also doch nicht?


    @RodFromGermany

    Wann wird dann das LastCall aufgerufen?

    EDIT: @~blaze~
    Wie ersetzt man das LastCall durch eine korrekte Dispose-Implementation?