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.

    phil schrieb:

    aufgerufen?
    Ggf. im Finalizer / Dispose des Programms.
    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!

    ~blaze~ schrieb:

    VB.NET-Quellcode

    1. Implements IDisposable
    In dem Moment, wo Du Deiner Klasse dies gibst, füllt die IDE folgendes aus:
    Spoiler anzeigen

    VB.NET-Quellcode

    1. #Region "IDisposable Support"
    2. Private disposedValue As Boolean ' To detect redundant calls
    3. ' IDisposable
    4. Protected Overridable Sub Dispose(disposing As Boolean)
    5. If Not Me.disposedValue Then
    6. If disposing Then
    7. ' TODO: dispose managed state (managed objects).
    8. End If
    9. ' TODO: free unmanaged resources (unmanaged objects) and override Finalize() below.
    10. ' TODO: set large fields to null.
    11. End If
    12. Me.disposedValue = True
    13. End Sub
    14. ' TODO: override Finalize() only if Dispose(ByVal disposing As Boolean) above has code to free unmanaged resources.
    15. 'Protected Overrides Sub Finalize()
    16. ' ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
    17. ' Dispose(False)
    18. ' MyBase.Finalize()
    19. 'End Sub
    20. ' This code added by Visual Basic to correctly implement the disposable pattern.
    21. Public Sub Dispose() Implements IDisposable.Dispose
    22. ' Do not change this code. Put cleanup code in Dispose(ByVal disposing As Boolean) above.
    23. Dispose(True)
    24. GC.SuppressFinalize(Me)
    25. End Sub
    26. #End Region


    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!

    phil schrieb:

    Was ich verstanden habe:
    - DestroyHandle ... muss am Schluss immer aufgerufen werden.
    Da hast du die 2. Zeile unterschlagen, und die ist viel wichtiger, denn deren systemweite Auswirkung ist viel gravierender:

    VB.NET-Quellcode

    1. MyBase.DestroyHandle()
    2. Dim H As IntPtr = SetClipboardViewer(_hview)
    Würde die ursprüngliche Handle _hview nicht wieder dort eingesetzt, wos zuvor vom CW-Handle verdrängt wurde, so würde nun die Clipboard-Messages die _hview nicht mehr erreichen.

    Noch einmal: Der CW macht sowas wie den "Man-in-the-Middle", also beim Start weist der Windows an, die Clipboard-Messages an seine eigene Handle zu senden - die kommen dann in WndProc() raus.
    Dort werden die Messages gecheckt, und ggfs. ein Event geraist.
    Anschließend - wichtig!! - wird die Message aber weitergesendet, nämlich an ihren ursprünglichen Adressaten: _hview.

    Wenn nun der CW aufgeräumt wird, dann musser selbstverständlich alles wieder in den vorherigen Zustand versetzen, also dass auch wenner weg ist, die Messages noch immer die _hview-Handle erreichen.

    die anneren Dinge hast du richtig verstanden, auch das letzte.
    Prinzipien-Sauber wäre, den CW per Dispose explizit aufzuräumen, aber hier ist das Aufräumen so wichtig, dass ich es zusätzlich vom Finalizer aus ausführe, falls der .Dispose-Aufruf versäumt wurde.
    Im Endeffekt ja - sagte ich ja schon: da hätte man hier ausnahmsweise (weils ein Singleton ist) den .Dispose-Pattern auch ganz weglassen können.

    ErfinderDesRades schrieb:

    die 2. Zeile unterschlagen
    Das war in den drei Punkten drin :D

    Wenn ein IDisposable-Objekt benutzt wird muss es also nicht aufgeräumt werden, weil es der GarbageCollector macht.
    Aber in der IDisposable-Klasse muss die Disposable-Sub überschrieben werden (um diese zwei Zeilen auszuführen)

    Um nochmal ganz sicher zu gehen: meintest du mit dem .Dispose-Pattern folgenden Code: ?

    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
    , den man weglassen kann.

    @RodFromGermany
    Ah, also ist das von alleine reingerutscht.

    @~blaze~
    Verstanden. Also das, was reinrutscht. ^^

    phil schrieb:

    Wenn ein IDisposable-Objekt benutzt wird muss es also nicht aufgeräumt werden, weil es der GarbageCollector macht.
    Aber in der IDisposable-Klasse muss die Disposable-Sub überschrieben werden (um diese zwei Zeilen auszuführen)
    ojeh - hätte ich man nichts erklärt - vorher wars richtiger.

    hat vermutlich auch gar keinen Sinn, weil vlt. weißt du garnet, was ein Interface ist, was ein Pattern, was ein Event, und kannst von daher gar nicht Begriffe wie IDisposable, Dispose-Pattern oder gar das Disposed-Event der Control-Klasse auseinander halten.
    Stattdessen erfindest du weitere Begriffe wie IDisposable-Klasse oder Disposable-Sub - die nicht wirklich einen Sinn ergeben, und die Verwirrung nur noch steigern.

    Wo fängt man da an zu erklären? GarbageCollection? Pattern? Interface? Event?
    dieses Buch lesen (hingegen das Galileio-Openbook ist Mist) - da wird das alles richtig und systematisch abgehandelt.

    Also hier die ClipboardWatcher-Klasse ohne den Dispose-Quatsch:

    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices
    2. Public Class ClipboardWatcher : Inherits NativeWindow
    3. <DllImport("user32", EntryPoint:="SetClipboardViewer")> _
    4. Private Shared Function SetClipboardViewer(ByVal hWnd As IntPtr) As IntPtr
    5. End Function
    6. Public Event Changed As EventHandler
    7. Public Shared ReadOnly Singleton As New ClipboardWatcher
    8. Private Shared ReadOnly _NoPtr As IntPtr = New IntPtr(-1)
    9. Private _PreviousHandle As IntPtr
    10. Private Sub New()
    11. MyBase.CreateHandle(New CreateParams)
    12. _PreviousHandle = SetClipboardViewer(MyBase.Handle)
    13. End Sub
    14. Protected Overrides Sub WndProc(ByRef m As Message)
    15. Const WM_DRAWCLIPBOARD As Integer = &H308
    16. Select Case m.Msg
    17. Case WM_DRAWCLIPBOARD
    18. RaiseEvent Changed(Me, EventArgs.Empty)
    19. End Select
    20. MyBase.WndProc(m)
    21. End Sub
    22. Protected Overrides Sub Finalize()
    23. If _PreviousHandle = _NoPtr Then Return
    24. MyBase.DestroyHandle()
    25. SetClipboardViewer(_PreviousHandle)
    26. _PreviousHandle = _NoPtr
    27. End Sub
    28. End Class
    Wie man malwieder sieht: am schwierigsten zu verstehen ist immer der Code, der nicht wirklich Sinn ergibt - auch wenner ansonsten völlig fehlerfrei ist.

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „ErfinderDesRades“ ()

    GarbageCollection löscht die Bereiche des Speichers, auf die es keine Verweise gibt.
    Manuell kann man es mit Dispose machen.
    ein Interface erweitert die Klasse um ein paar Subs, Functions, ...
    ein Event löst eine externe Methode aus, wenn sie mit Handles DiesesEvent definiert ist
    IDisposable bewirkt, dass man Disposable überschreiben kann/muss
    Ist das so richtig?

    Was ein Pattern ist, weiß ich nicht. Gibt es eine Seite, auf der es erklärt wird?
    Bücher zu Visual Basic halte ich nicht aus. Ich habe einfach nicht die Geduld, mir alles durchzulesen. Nach der 20. Seite fange ich an, nur noch grob drüberzulesen, weil ich schon weiß, was Variablen,... sind. Dann werden die Sprünge größer und ich scroll irgendwann nur noch durch. Während einer solchen Phase überlese ich dann Dinge, wie Pattern. :(
    Aber ich verlange nicht, dass du mich verstehst. :S

    Meine eigene Frage war, ob ich das frmClipboardWatch_Disposed weglassen kann?

    EDIT:
    Wow, das hab ich gerade gar nicht gesehen:
    Man kann das ganze IDisposable weglassen, weil die 2 Zeilen im Finalize stehen?

    phil schrieb:

    GarbageCollection löscht die Bereiche des Speichers, auf die es keine Verweise gibt.
    Manuell kann man es mit Dispose machen.
    ein Interface erweitert die Klasse um ein paar Subs, Functions, ...
    ein Event löst eine externe Methode aus, wenn sie mit Handles DiesesEvent definiert ist
    IDisposable bewirkt, dass man Disposable überschreiben kann/muss
    Ist das so richtig?
    Leider alles falsch.
    naja, immerhin hat GarbageCollection was mit Speicherbereichen zu tun, auf dies keine Verweise mehr gibt. Aber es löscht diese nicht - was immer du damit gemeint haben magst.

    Und das mit den Events ist auch fast richtig, nur kann man Methoden nicht "auslösen", sondern sie werden aufgerufen.
    Aber das mit Dispose und Interface geht gar nicht.
    Auch gibt es neben der Handles-Klausel eine weitere Möglichkeit, Events zu abonnieren und darüberhinaus auch wieder abzubestellen.
    Da bin ich ja beruhigt, bein den Events war ich mir nämlich ziemlich sicher.
    Interfaces kenne ich nur so halb aus Java. Das werde ich mir mal genauer anschauen.
    Was macht GarbageCollection dann, wenn es nicht Speicherbereiche freigibt zu denen es keine Verweise mehr gibt?

    Meine Frage ist immer noch, ob ich ein IDisposable-Objekt explizit Disposen muss, also ob ich folgenden Code brauche:

    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
    (steht im Hauptprogramm, das den ClipBoardWatcher benutzt)
    ja - freigeben tut er, nicht löschen.

    ja, und ein IDisposables Objekt sollte man immer explizit disposen, wenn mans nicht mehr braucht.
    Das ist der Sinn des IDisposable-Interfaces: belegte Resourcen von Objekten freigeben, wo der GarbageCollector nicht weiß, wie er das machen soll.

    Also Speicherplatz freigeben weiß er, aber ein Window-Handle freizugeben weiß er nicht.

    Aber ich empfehle wirklich Buch, denn auch beim Disposen gibts eine oftmals günstigere Alternative, nämlich die Nutzung des Using-Schlüsselwortes.
    Könnte ich jetzt auch erklären, und so würde es immer weiter gehen, und es würde immer doch nur Halbwissen bleiben, so wie dein halbes Wissen über Events (ohne das Addhandler-Schlüsselwort zu kennen), und dein neues halbes Wissen über IDisposable (ohne das Using-Schlüsselwort zu kennen).

    Using kenne ich.

    Ok, wenn ich den Code brauche, wo schreibe ich ihn dann rein, wenn es kein Me.Disposed ohne das IDisposable gibt?
    Muss ich dann das IDisposable auch in der Hauptklasse implementieren?
    In deinem WindowsForms-Beispiel hat das irgendwie ohne IDisposable in der Hauptklasse funktioniert.

    EDIT:
    Interface und implementieren gehört schon zusammen, oder verstehe ich da etwas ganz falsch?

    phil schrieb:

    Using kenne ich.
    Kann nicht sein. Wer das IDisposable-Interface nicht kennt, kann das Using-Schlüsselwort erst recht nicht kennen. Es hat in VB eine annere Bedeutung als zB in Java (und in c# hats 2 Bedeutungen).

    ErfinderDesRades schrieb:

    Könnte ich jetzt auch erklären, und so würde es immer und immer weiter gehen, und es würde immer doch nur Halbwissen bleiben
    dieses Buch lesen (hingegen das Galileio-Openbook ist Mist)

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

    VB.NET-Quellcode

    1. Public Class frmClipboardWatch
    2. Private WithEvents _Watcher As ClipboardWatcher = ClipboardWatcher.Singleton
    3. Private Sub frmClipboardWatch_Disposed(ByVal sender As Object, _
    4. ByVal e As EventArgs) Handles Me.Disposed
    5. _Watcher.Dispose()
    6. End Sub
    7. Private Sub _Watcher_Changed(ByVal sender As Object, _
    8. ByVal e As EventArgs) Handles _Watcher.Changed
    9. Dim Data As IDataObject = Clipboard.GetDataObject
    10. With Me.TextBox1
    11. .AppendText(String.Concat("Available Formats:", CrLf))
    12. .AppendText(String.Join(CrLf, Data.GetFormats))
    13. .AppendText(String.Concat(CrLf, CrLf, "Text = '", _
    14. Clipboard.GetText, "'", CrLf, CrLf))
    15. End With
    16. End Sub
    17. End Class
    Zeile 5.

    Irgendwie funktioniert das bei dir, weil es mit WindowsForms zusammenhängt. Oder gibt es dieses Me.Disposed-Event auch bei einer normalen Klasse, die nur von Object erbt?
    Bei mir wird kein Me.Disposed gefunden. (Ich verwende WPF)