Hallo ich hab meine Anwendung als "ClipboardWatcher" registriert. Wenn ich jetzt aber über die Funktion SetData() selber eine z.B. FileDroplist setze dann wird die WM_DRAWCLIPBOARD Message danach 2mal gesendet! Hat da jemand nen grund für?
public void MessageProc(ref Message m)
{
if (m.Msg == WM_CHANGECBCHAIN)
{
if (m.WParam == _nextCBWatcher)
{
_nextCBWatcher = m.LParam;
}
else
{
SendMessage(_nextCBWatcher, m.Msg, m.WParam, m.LParam);
}
}
else if (m.Msg == WM_DRAWCLIPBOARD)
{
OnClipboardChange();
}
else if (m.Msg == WM_CLOSE)
{
ChangeClipboardChain(_ownerWnd, _nextCBWatcher);
}
}
public static void SetData(CBDataType type, object data)
{
HoldOnce = true;
object dat;
switch (type)
{
case CBDataType.Files:
dat = data as StringCollection;
if (dat == null) throw new Exception("Invalid data.");
Clipboard.SetFileDropList((StringCollection)dat);
break;
case CBDataType.Audio:
dat = data as Stream;
if (dat == null) throw new Exception("Invalid data.");
Clipboard.SetAudio((Stream)dat);
break;
case CBDataType.Image:
dat = data as Image;
if (dat == null) throw new Exception("Invalid data.");
Clipboard.SetImage((Image)dat);
break;
case CBDataType.Text:
dat = data as string;
if (dat == null) throw new Exception("Invalid data.");
Clipboard.SetText((string)dat);
break;
default: throw new Exception("Unsupported Clipboardformat.");
}
}
erster Verdächtiger bei sowas ist immer eine doppelte Registrierung des Events.
Dann: Was macht
SendMessage(_nextCBWatcher, m.Msg, m.WParam, m.LParam);
?
oder auch die anderen methoden?
kannstejamal auskommentieren, obs dann immer noch doppelt kommt.
Der frühe Apfel fängt den Wurm.
Die SendMessage(_nextCBWatcher, m.Msg, m.WParam, m.LParam) Funktion reicht das event an den nächsten registrierten ClipboardWatcher in der Kette weiter (siehe MSDN). Mein Watcher ist definitiv nur 1 mal registriert ansonsten würden ja alle events 2mal ankommen und nicht nur dann wenn ich daten ins Clipboard schreibe...
ChangeClipboardChain(_ownerWnd, _nextCBWatcher) entfernt meine Registrierung in der Kette.
OnClipboardChange() ist mein Event das getriggert wird...
Also kanns daran nicht liegen...
Jo, habich jetzt auch gefunden. Immer, wenn die eigene Anwendung was ins Clipboard setzt isses doppelt.
Sieht mir Bug aus.
Ich hab in VB getestet, auf Basis von dem hier: Clipboard überwachen
Als Workaround habich ein Delay eingebaut, sodaß der ClipboardWatcher erst feuert, wenn der MessageLoop abgearbeitet ist (Application.Idle verarbeitet)
Imports System.Runtime.InteropServices
Public Class ClipboardWatcher : Inherits NativeWindow : Implements IDisposable
<DllImport("user32", EntryPoint:="SetClipboardViewer")> _
Private Shared Function SetClipboardViewer(ByVal hWnd As IntPtr) As IntPtr
End Function
Public Event Changed As EventHandler
Private _IsNew As Boolean = True
Private _hview As IntPtr
'nur eine globale Instanz zulassen
Public Shared Singleton As New ClipboardWatcher
Private Sub New()
MyBase.CreateHandle(New CreateParams)
_hview = SetClipboardViewer(MyBase.Handle)
End Sub
Protected Overrides Sub WndProc(ByRef m As Message)
Const WM_DRAWCLIPBOARD As Integer = &H308
Select Case m.Msg
Case WM_DRAWCLIPBOARD
If _IsNew Then
AddHandler Application.Idle, AddressOf App_Idle
_IsNew = False
End If
End Select
MyBase.WndProc(m)
End Sub
Private Sub App_Idle(ByVal sender As Object, ByVal e As EventArgs)
RemoveHandler Application.Idle, AddressOf App_Idle
RaiseEvent Changed(Me, EventArgs.Empty)
_IsNew = True
End Sub
#Region " IDisposable Support "
' Für diese Klasse ist korrekte Ressourcenbereinigung besonders wichtig, da mit
' systemübergreifenden Ressourcen gearbeitet wird
Private disposedValue As Boolean = False ' So ermitteln Sie überflüssige Aufrufe
Protected Overridable Sub Dispose(ByVal disposing As Boolean)
If Not Me.disposedValue Then
If disposing Then
' TODO: Verwaltete Ressourcen freigeben, wenn sie explizit aufgerufen werden
End If
MyBase.DestroyHandle()
Dim H As IntPtr = SetClipboardViewer(_hview)
End If
Me.disposedValue = True
End Sub
' Dieser Code wird von Visual Basic hinzugefügt, um das Dispose-Muster richtig zu implementieren.
Public Sub Dispose() Implements IDisposable.Dispose
' Ändern Sie diesen Code nicht. Fügen Sie oben in Dispose(ByVal disposing As Boolean) Bereinigungscode ein.
Dispose(True)
GC.SuppressFinalize(Me)
End Sub
Protected Overrides Sub Finalize()
MyBase.Finalize()
Dispose(False)
End Sub
#End Region
End Class
Der frühe Apfel fängt den Wurm.
Hm, Visual Basic 😕. Werd das mal morgen auf der Arbeit entschlüsseln 😄. Danke für deinen Lösungsansatz weitere wären trozdem erwünscht vorallem welche die sich schon bewährt haben!