Laden...

Asynchrone Verarbeitung , damit das GUI nicht hängt, führt zu merkwürdigen Effekten

Erstellt von DMW07 vor 13 Jahren Letzter Beitrag vor 13 Jahren 2.051 Views
D
DMW07 Themenstarter:in
3 Beiträge seit 2010
vor 13 Jahren
Asynchrone Verarbeitung , damit das GUI nicht hängt, führt zu merkwürdigen Effekten

Hi!

Ich code gerade an einem Programm, dass die Zwischenablage überwacht, und Bilder aus der Zwischenablage automatisch auf einen ausgewählten Imagehoster läd und anschließend die Links ausgibt.

So kann man dieses Programm sehr praktisch zum erstellen von Screens, als auch zum mirrorn von Grafiken im Web verwenden 🙂

Das Programm wird etwas komplexer und umfangreicher als bisherige Tools, Beispielsweise sind die Imagehoster nicht fest im Programm integriert sondern werden als Plugins verwaltet.

Im Moment plagt mich ein Problem, für das ich seit Stunden eine Lösung suche, und das ich überhaupt nicht verstehe weil es so paradox ist :S

Wie bereits oben erwähnt, überwacht mein Programm die Zwischenablage auf Bilder.
Dies hatte ich Anfangs mit einem Timer gelößt, der etwa jede Sekunde die Zwischenablage auf Bilder überprüft hat. Das war natürlich nicht gerade sehr performanceschonend und hat auch ab und an ClipboardFehler verursacht, daher habe ich eine Klasse gebastelt die ein Event aufruft, wenn sich der Inhalt der Zwischenablage verändert.

Dieses Event fange ich in meinem Programm nun ab, prüfe ob es sich in der Zwischenablage um ein Bild handelt (Könnte ja auch sein dass es was anderes ist wie eine Datei oder Text 😄) und rufe dann die Upload-Methode auf, welche das Bild weiterverarbeitet (Je nach Auswahl des Imagehosters wird das jeweilige Plugin geladen, das Bild ans Plugin übermittelt und anschließend die Antwort vom Plugin, welche die Links enthält, ausgewertet).

Das funktioniert soweit auch alles prima, das Programm funktioniert perfekt.
Nur leider sorgt das Multithreading hier sehr für Chaos und Verwirrung X(
Die Upload-Methode soll in einem seperaten Thread aufgerufen werden, damit meine GUI während und nach dem Uplopad nicht hängt.
Gesagt getan, doch wie sieht es mit der Funktion aus?

Sehr paradox.

Bis zum ersten Screen konnte man das Programm perfekt benutzen, hat alles wunderbar funktioniert.

Wenn man das Programm allerdings ca 10 Minuten lang nachdem man den 1. Screen damit hochgeladen hat unbenutzt hat laufen lassen, tat sich NICHTS mehr wenn man ein Bild in die Zwischenablage kopiert. 0 Reaktion.

Ich bin sehr ratlos wie MultiThreading ein derartiges Chaos entstehen lassen kann.

Ich habe schon einiges probiert.
An der Klasse welche die Zwischenablage überwacht scheint es nicht zu liegen, testweise habe ich mal im Event der Klasse eine Messagebox platziert, das Programm gestartet, Event ausgelößt, 15 Minuten gewartet und erneut gestestet, das Event hat jedesmal reagiert.

Die Klasse, welche das Event auslößt wenn der Inhalt der Zwischenablage verändert wird:


[DefaultEvent("OnClipboardChange")]
    public class ClipboardWatcher:Control{
        IntPtr nextClipboardViewer;

        public ClipboardWatcher () {
            this.BackColor = Color.Red;
            this.Visible = false;

            nextClipboardViewer = (IntPtr)SetClipboardViewer((int)this.Handle);
        }

        public event EventHandler<ClipboardChangedEventArgs> ClipboardChanged;

        protected override void Dispose (bool disposing) {
            try {
                IntPtr handle = this.Handle;
                ChangeClipboardChain(handle, nextClipboardViewer);
            } catch (Exception) { throw; }
        }

        [DllImport("User32.dll")]
        protected static extern int SetClipboardViewer (int hWndNewViewer);

        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool ChangeClipboardChain (IntPtr hWndRemove, IntPtr hWndNewNext);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        public static extern int SendMessage (IntPtr hwnd, int wMsg, IntPtr wParam, IntPtr lParam);

        protected override void WndProc (ref System.Windows.Forms.Message m) {
            const int WM_DRAWCLIPBOARD = 0x308;
            const int WM_CHANGECBCHAIN = 0x030D;

            switch (m.Msg) {
                case WM_DRAWCLIPBOARD:
                    OnClipboardChanged();
                    SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                    break;

                case WM_CHANGECBCHAIN:
                    if (m.WParam == nextClipboardViewer)
                        nextClipboardViewer = m.LParam;
                    else
                        SendMessage(nextClipboardViewer, m.Msg, m.WParam, m.LParam);
                    break;

                default: base.WndProc(ref m); break;
            }
        }

        void OnClipboardChanged () {
            IDataObject iData = Clipboard.GetDataObject();
            if (ClipboardChanged != null) {
                ClipboardChanged(this, new ClipboardChangedEventArgs(iData));
            }
        }
    }

    public class ClipboardChangedEventArgs : EventArgs {
        public readonly IDataObject DataObject;
        public ClipboardChangedEventArgs (IDataObject dataObject) {
            DataObject = dataObject;
        }
    }

Das Event, welches ich im Programm abfange


private void OnClipboardChangedEvent (object sender, ClipboardChangedEventArgs e) {
            if (Clipboard.ContainsImage()) {
                Bitmap image = new Bitmap(Clipboard.GetImage());
                Thread thread = new Thread((ThreadStart)delegate { UploadScreen(image); });
                thread.Start();
            }
        }

UploadScreen() ist die Methode, welche den Screen, also das Bitmap dann weiterverwarbeitet.

Ich kann mir nur sehr schwer vorstellen dass es daran liegt, da wie gesagt ohne MultiThreading alles (mit Ausnahme dass eben die GUI hängt) wunderbar funktioniert.

Eines noch was ich vielleicht dazu erwähnen sollte:

In der Methode Dispose() in der obrigen Klasse erhalte ich unregelmäßig, meist beim 2. Screenen, eine Exception über einen ungültigen Threadübergreifenden Vorgang - auch hier kann ich mir nicht erklären wie diese zustande kommt, da die Instanz der Klasse und das Abfangen des Events im Hauptthread des Programms stattfinden.
Es wird eben lediglich im Event ein neuer Thread erzeugt.

Ich hoffe ihr könnt mir einigermaßen folgen, ich bin jedenfalls ratlos X(

Blödes Multithreading :evil:

916 Beiträge seit 2008
vor 13 Jahren

Hallo DMW07,

ersteinmal noch ein paar Fragen zu deinem Problem. Von wo aus wird die Dispose ausgerufen und an welcher obigen Klasse? Die Exception deutet eindeutig darauf hin das du das Dispose aus einem Thread aufrufst der das besagte Object nicht erstellt hat!

Wenn du nun also nach dem ersten Upload erneut ein Screen addest, dann sagst du ja das Event kommt, hast du mal debugged ob dann auch ein neuer Thread erstellt wird und dieser dann auch gestartet wird?

Bau doch mal ein Event ein das gefeuert wird wenn der Thread beendet ist, dann siehst du auch ob die Updoad Methode ordnungsgemäß terminiert. Bezüglich des Multithreadings, kann es passieren das 2 Upload Methoden gleichzeitig ausgeführt werden?

Again what learned...