[C#] Invoke naar UI Thread

Pagina: 1
Acties:
  • 1.493 views sinds 30-01-2008
  • Reageer

Acties:
  • 0 Henk 'm!

  • codeneos
  • Registratie: Augustus 2004
  • Laatst online: 11-09 00:04
Ik ben helemaal nieuw met betrekking tot threading en ben begonnen met het schrijven van een simpel C#.net applicatie in windows. Gewone invokes gaan prima maar nu wil ik vanuit Thread A, welke ik net gemaakt heb, een object in de UI Thread updaten.

Tijdens het starten van de applicatie maak ik dus 2 nieuwe threads, één thread waar de UI in draait en één thread waar van ik de UI moet kunnen updaten. (ThreadTest is de naam van de UI class)

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// main Threads             
ThreadTest UIproc  = new ThreadTest();
status CKproc      = new status("localhost",UIproc);

UIproc.args        = args;

Thread UI          = new Thread(new ThreadStart(UIproc.UIProc));
UI.Name            = "User Interface Thread";
UI.ApartmentState  = ApartmentState.STA;

Thread CK          = new Thread(new ThreadStart(CKproc.check));
CK.Name            = "Server status Thread";
CK.ApartmentState  = ApartmentState.STA;

// starting threads
UI.Start();
CK.Start();


Ik geef de UI class dus door aan de status class. De status class checkt of een bepaalde server online is en van uit daar moet de tray icon in de UI thread worden geupdate.

Om het invoken wat makkelijker te maken gebruik ik deze helper class die alle invokes zou moeten afhandelen

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
using System;
using System.Collections;
using System.Reflection;
using System.Reflection.Emit;

namespace ThreadingTest
{
    public class SafeInvokeHelper
    {
        static readonly ModuleBuilder builder;
        static readonly AssemblyBuilder myAsmBuilder;
        static readonly Hashtable methodLookup;

        static SafeInvokeHelper()
        {
            AssemblyName name = new AssemblyName();
            name.Name = "temp";
            myAsmBuilder = AppDomain.CurrentDomain.DefineDynamicAssembly(name, AssemblyBuilderAccess.Run);
            builder = myAsmBuilder.DefineDynamicModule("TempModule");
            methodLookup = new Hashtable();
        }

        public static object Invoke(System.Windows.Forms.Control obj, string methodName, params object[] paramValues)
        {
            Delegate del = null;
            string key = obj.GetType().Name + "." + methodName;
            Type tp;
            if (methodLookup.Contains(key)) 
                tp = (Type)methodLookup[key];
            else
            {
                Type[] paramList = new Type[obj.GetType().GetMethod(methodName).GetParameters().Length];
                int n = 0;
                foreach (ParameterInfo pi in obj.GetType().GetMethod(methodName).GetParameters()) paramList[n++] = pi.ParameterType;
                TypeBuilder typeB = builder.DefineType("Del_" +  obj.GetType().Name + "_" + methodName, TypeAttributes.Class | TypeAttributes.AutoLayout | TypeAttributes.Public |  TypeAttributes.Sealed, typeof(MulticastDelegate), PackingSize.Unspecified);
                ConstructorBuilder conB = typeB.DefineConstructor(MethodAttributes.HideBySig | MethodAttributes.SpecialName |            MethodAttributes.RTSpecialName, CallingConventions.Standard, new Type[] { typeof(object), typeof(IntPtr) });
                conB.SetImplementationFlags(MethodImplAttributes.Runtime);
                MethodBuilder mb = typeB.DefineMethod( "Invoke", MethodAttributes.Public | MethodAttributes.Virtual | MethodAttributes.HideBySig, obj.GetType().GetMethod(methodName).ReturnType, paramList );
                mb.SetImplementationFlags( MethodImplAttributes.Runtime ); 
                tp = typeB.CreateType();
                methodLookup.Add(key, tp);
            }

            del = MulticastDelegate.CreateDelegate(tp, obj, methodName);
            return obj.Invoke(del, paramValues);

        }
    }
}


In de UI class zit ook de functie die wordt aangeroepen door de invoke:

C#:
1
2
3
4
5
6
7
8
9
#region Thread Safe interactions

public void TrayStatus(bool active)
{
    this.notifyIcon1.Visible = active;
    this.notifyIcon2.Visible = active ? false : true;
}

#endregion


In de status class, check function wordt deze code gedraait, die de UIthread dus zou moeten updaten (als ik status niet in een eigen thread uitvoer gaat het goed en zijn er geen invoke problemen).

C#:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
while(true)
{
    try
    {
        this.Connector.Connect(this.Address, 80);
        SafeInvokeHelper.Invoke(this.UIThread, "TrayStatus", true);
        System.Windows.Forms.MessageBox.Show("online ("+Address+")");
        this.Connector.Close();
    }
    catch
    {
        SafeInvokeHelper.Invoke(this.UIThread, "TrayStatus", false);
        System.Windows.Forms.MessageBox.Show("offline");
    }
            
    Thread.Sleep(this.TimeOut * 1000);
}


Connector, Address en UIThread worden in status() vast gesteld. UIThread is de UI class.

De invoke werkt alleen niet op deze manier. Ik heb al wat ge-googled maar heb geen oplossing kunnen vinden voor mijn probleem. Ik heb wel gedacht aan MarsheledInvoke, alleen dit is een private function die ik niet kan aan roepen.

[ Voor 9% gewijzigd door codeneos op 25-05-2006 11:07 ]

http://www.tweakers.net/gallery/119170/sys


Acties:
  • 0 Henk 'm!

  • whoami
  • Registratie: December 2000
  • Laatst online: 01:56
Als je gebruik maakt van .NET 2.0 kan je imho beter de BackGroundWorker class gebruiken.
Indien je geen gebruik maakt van .NET 2.0, maar met .NET 1.x werkt, kan je zelf een 'Task' class maken:
[rml]whoami in "[ C#] button.Show() vanuit anderen Thread"[/rml]

https://fgheysels.github.io/


Acties:
  • 0 Henk 'm!

  • codeneos
  • Registratie: Augustus 2004
  • Laatst online: 11-09 00:04
Maar ik start 2 threads achter elkaar vanuit de main thread. Ik kan van uit de main thread wel de invoke doen met de SafeInvokeHelper maar als ik nog een thread maak en dan vanuit deze thread een invoke probeer werkt het niet. Als zou ik een task class maken die de taks uitvoert (in dit geval om de 30 seconden checken of de server nog online is) moet ik nog steeds van uit deze task thread een update op de main doen en dat werkt niet.

Volgens mij is een kleine aanpassing aan de SafeInvokeHelper genoeg alleen heb in geen idee wat er anders moet. Voor mijn gevoel zou hij nu perfect de invoke moeten kunnen doen, alleen gebeurd dat niet.

http://www.tweakers.net/gallery/119170/sys


Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

hoe kan je nou een UI Thread gaan aanmaken ?

ik dacht dat UI-threads zowat 'impliciet' waren aan controls. Er bestaat afaik nl ook geen mogelijkheid om een UI-thread toe te wijzen...

Je hele opzet lijkt nogal bij het haar gegrepen dus.
De normale manier van werken is met de InvokeRequired en Invoke van de Control. Ook een tray-icon zal wel op de een of andere manier daaraan voldoen.

ASSUME makes an ASS out of U and ME


Acties:
  • 0 Henk 'm!

  • codeneos
  • Registratie: Augustus 2004
  • Laatst online: 11-09 00:04
Ik heb het net zonder invoke geprobeerd en dat werkt gewoon :S. Ik ben het nu even helemaal kwijt. Als ik de functie StatusTray() aanroep vanuit de andere thread wordt de tray icon gewoon geupdate.

http://www.tweakers.net/gallery/119170/sys


Acties:
  • 0 Henk 'm!

  • H!GHGuY
  • Registratie: December 2002
  • Niet online

H!GHGuY

Try and take over the world...

over het al dan niet werken spreekt niemand zicht uit.
het threadsafe zijn is een andere zaak.

in welke klasse zit die StatusTray en is dit afgeleid van Control ?

bovendien vraag ik me af wat:
this.notifyIcon2.Visible = active ? false : true;

in godsnaam mag betekenen.

wil je
this.notifyIcon2.Visible = !active; ??

ASSUME makes an ASS out of U and ME

Pagina: 1