none
Threadübergreifender Zugriff schon vor Application.Run RRS feed

  • Frage

  • Hallo

    Also erst mal der Vollständigkeit halber möchte ich darauf hinweisen, dass dieser Thread ein Resultat eines anderen ist,
    wo mir empfohlen wurde, mein Problem anders zu lösen.

    Also hier mal der Verweis/Link für die, die es gerne mal nachlesen wollen. SynchronizingObject ...


    So und nun das Problem.
    Meine Anwendung soll nur ein mal gestartet werden können, was schon funktioniert.
    Allerdings soll nun die Zweite Instanz dem laufenden Programm mitteilen: "Achtung du wurdest grad noch mal aufgerufen",
    damit das Laufende Programm dann reagieren kann und z.B. das Hauptformular generiert, initialisiert und Aufruft, oder aber es auch einfach nur in
    den Vordergrund holt.

    Das Problem ist, das alles passiert noch vor Application.Run und es existieren noch keinerlei Controls.
    Auf Empfehlung hin, aus dem anderen Thread, habe ich das ganze nun versucht mit einem NamedPipe zu lösen.
    Denn ich brauche das Ganze nur für ein einziges Signal und sonst gar nicht, weshalb mir halt so was wie Sockets zu mächtig erschienen.

    Ja und da die Sachen nun nicht alle auf einem gemeinsamen Formular laufen, habe ich das Problem, das meine Lösungen immer automatisch in einem
    anderen Thread ablaufen, wie der in dem "später" die Formulare aufgerufen werden.

    So auch bei meinem Versuch das ganze mit einen Named Pipe zu lösen.
    Immer wenn ich eine Message von der Zweiten Instanz bekomme löst meine Klasse die ich extra für das Handling angelegt habe, ein selbst generiertes event aus,
    worauf dann mein Hauptprogramm reagiert und dann halt z.B: das Hauptformular aufruft.
    Genau hier kommt dann der Fehler "Threadübergreifender Zugriff" bzw. wenn ich mein Event mit Invoke auslöse, dann kommt kein Fehler, aber das aufgerufene Formular reagiert dann nicht (wie in einer Endlosschleife).


    Da die Klasse nicht so umfangreich ist, poste ich die hiermit gleich mal mit.


    internal class NamedPipeCommunication : IDisposable
      {
        public delegate void MessageReceivedEventHandler(string Message);
        public event MessageReceivedEventHandler MessageReceived;
    
        private System.IO.Pipes.NamedPipeServerStream ServerPipe;
        private Guid g = Guid.NewGuid();
    
        public NamedPipeCommunication()
        {
          ServerPipe = new NamedPipeServerStream(System.Windows.Forms.Application.ProductName + "Pipe", PipeDirection.InOut, 1, PipeTransmissionMode.Message, PipeOptions.Asynchronous);
          ServerPipe.BeginWaitForConnection(new AsyncCallback(Connected), g);
        }
    
        private void Connected(IAsyncResult ar)
        {
          //System.Windows.Forms.MessageBox.Show("Connected Server");
    
          ServerPipe.WaitForConnection();
          System.IO.StreamReader sr = new System.IO.StreamReader(ServerPipe);
    
          string Message = sr.ReadToEnd();
    
          sr.Close();
          sr.Dispose();
          
          if (MessageReceived != null)
            MessageReceived.Invoke(Message);
    
          //System.Windows.Forms.MessageBox.Show("Server Readed --> " + Message);
        }
    
        public static void SendToServer()
        {
          System.IO.Pipes.NamedPipeClientStream ClientPipe = new NamedPipeClientStream(".",System.Windows.Forms.Application.ProductName + "Pipe", PipeDirection.InOut, PipeOptions.Asynchronous);
          ClientPipe.Connect();
    
          System.IO.StreamWriter sw = new System.IO.StreamWriter(ClientPipe);
    
          //System.Windows.Forms.MessageBox.Show("Connected Client");
    
          sw.AutoFlush = true;
          sw.Write("Hallo");
          sw.Close();
          sw.Dispose();
    
          //ClientPipe.WaitForPipeDrain();
    
          ClientPipe.Close();
          ClientPipe.Dispose();
    
          //System.Windows.Forms.MessageBox.Show("Client sended");
        }
    
        #region IDisposable Member
    
        public void Dispose()
        {
          if (ServerPipe != null)
          {
            ServerPipe.Close();
            ServerPipe.Dispose();
          }
        }
    
        #endregion
      }
    


    Hat vielleicht jemand eine Idee, wie ich das Problem einfach lösen kann!?
    Ich arbeite noch mit .Net 3.5.
    Das Laufende Programm startet dann halt die Klasse als Server und wartet dass sich ein Client meldet.
    Wird eine Zweite Instanz gestartet erkennt diese das und sendet über diese Klasse eine Nachricht an den Server.
    Mehr gibts da eigentlich erst mal nicht zu sagen.

    Bin wie immer für jede Hilfe dankbar!

    Gruß
    Christian

    Donnerstag, 18. November 2010 18:22

Antworten

Alle Antworten

  • Hmm. Evtl. habe ich die eigentliche Problematik nicht verstanden. Was passiert vor dem Application.Run?

    Wenn Deine Applikation eine Windows Message Queue unterhält (und ein Fenster hat - auch wenn es vesteckt ist oder so), dann würde ich einfach ein individuelles Event schicken. Es gibt klar definierte Nachrichten und aus der "guten alten c Zeit" kenne ich halt noch das WM_USER ab dem man eigene Nachrichten definiert hat...

    Dann würde die Applikation einfach nach dem bestehenden Prozess schauen und dann dem entsprechenden Fenster die entsprechende Nachricht senden. Diese würde dann darauf reagieren. (Control.WndProc müsste dazu dann überschrieben werden)

    Das aber nur so als Idee - Da ich die genauen Anforderungen gerade nicht 100% verstanden habe, ist das auch nur eine kleine Anregung - vielleicht ist dies ja eine brauchbare Idee.

    Ein möglicher Link (auf englisch), der das mit den Events etwas erklärt, ist z.B.
    http://www.ww.functionx.com/csharp1/winprog/Lesson06.htm

    Vielleicht hilft diese Idee ja ein bisschen weiter.

    Mit den besten Grüßen,

    Konrad

    Donnerstag, 18. November 2010 20:47
  • Hmm. Evtl. habe ich die eigentliche Problematik nicht verstanden. Was passiert vor dem Application.Run?

    Hallo Konrad

    Also erst mal alles was ich habe. ;-)
    Wie ich ja geschrieben hatte, resultiert diese Problem aus einer Frage von mir in einem anderen Thread.
    Irgend wie hab ich mich schwer getan, das Problem zu schildern, ohne das Alte Problem jetzt wieder mit einzubauen,
    obwohl sie nahe beieinander liegen.

    Zusammengefasst:

    Bevor Application.Run aufgerufen wird (MainLoop), wird geprüft ob das Programm schon mal läuft und wenn ja wird darauf reagiert,
    indem eine Nachricht an die laufende Instanz geschickt wird.

    Das funktioniert im Prinzip mit dieser Klasse, nur das Problem ist, das die Klasse / die darin Verwendeten Funktionen scheinbar in einem
    anderen Thread ausgeführt werden und wenn ich auf das Ereignis der Klasse reagiere und in einem der Fälle halt ein neues Formular aufrufen muss,
    gibt das den Fehler "Threadübergreifender Zugriff".

    LG
    Christian

    Donnerstag, 18. November 2010 21:02
  • Hallo Christian,

    • Du schriebst:
      "Genau hier kommt dann der Fehler "Threadübergreifender Zugriff" bzw. wenn ich mein Event mit Invoke auslöse, ..."

    Nun, dann schätze ich, hast Du das Invoke über Delegate.Invoke aus einem anderen Thread als dem MainThread aufgerufen. Dann ist der Fehler "by design". Mach das Invoke (o. BeginInvoke) zum Beispiel über ein Control, das im MainThread erstellt wurde. Dann wird der Handler auch im MainThread ausgeführt:

    [Bearbeiten von Steuerelementen aus Threads]
    http://dzaebel.net/ControlInvoke.htm

    Ein Beispiel:

    using System.Diagnostics;
    using System.Threading;
    using System.Windows.Forms;
    
    namespace WinDelegateInvoke
    {
     public partial class Form1 : Form
     {
     TestDelegate td = new TestDelegate(Display);
     public delegate void TestDelegate(object meldung);
    
     public Form1()
     {
     InitializeComponent();
     Debug.WriteLine("\n===========");
     AnzeigeThreadID("Am Anfang (MainThread)");
     new Thread(new ThreadStart(InThreadAusführen)).Start(); 
     //t("Demo");
     }
    
     public void InThreadAusführen()
     {
     AnzeigeThreadID("InThreadAusführen");
     td.Invoke("Demo über Delegate.Invoke");
     this.Invoke(td, "Demo über Control.Invoke");
     new Thread(new ParameterizedThreadStart(Display)).Start("Demo über Start"); 
     }
    
     public static void Display(object meldung)
     {
     AnzeigeThreadID("\nIn Display");
     Debug.WriteLine("Die Meldung war : " + meldung);
     }
    
     private static void AnzeigeThreadID(string info)
     {
     Debug.WriteLine(info + ": " + Thread.CurrentThread.ManagedThreadId);
     }
     }
    }

    ____________________

    Ausgabe des Programmes:

    • ===========
      Am Anfang (MainThread): 10
      InThreadAusführen: 11

      In Display: 11
      Die Meldung war : Demo über Delegate.Invoke

      In Display: 10
      Die Meldung war : Demo über Control.Invoke

      In Display: 12
      Die Meldung war : Demo über Start

    -----------------------------

    Da hier auch SingleInstance Verhalten angesprochen wurde, hier noch ein Thread-Link diesbezüglich:
    http://social.msdn.microsoft.com/Forums/de-DE/visualcsharpde/thread/500ab44e-290e-471a-bd36-a77240a09206
    auch, wenn der Teil ja schon fertig implementiert ist.
    Insbesondere der die Ghengis-InitialInstanceActivator-Klasse dort (die mit Remoting arbeitet), könnte mit Deiner Lösung zusammen benutzt werden.


    ciao Frank

    Freitag, 19. November 2010 08:01
  • Nun, dann schätze ich, hast Du das Invoke über Delegate.Invoke aus einem anderen Thread als dem MainThread aufgerufen. Dann ist der Fehler "by design". Mach das Invoke (o. BeginInvoke) zum Beispiel über ein Control , das im MainThread erstellt wurde. Dann wird der Handler auch im MainThread ausgeführt:

    Hallo Frank

    Aber genau das ist doch mein Problem!
    Ich habe noch kein Control um mich drauf zu beziehen!
    (Zumindest keines was schon mal sichtbar war oder ist, wenn ich das Kontextmenü für mein Systrayicon mit einbeziehe.)

     

    LG
    Christian

    Freitag, 19. November 2010 15:27
  • Christian,

         > Ich habe noch kein Control

    ich hatte Dir schon dazu schon im alten Thread einen Hinweis gegeben, wie man das machen kann. Wenn man keins hat, kann man sich zum Beispiel ein neues erstellen:

    http://social.msdn.microsoft.com/Forums/de-DE/visualcsharpde/thread/8141c7a5-b677-4954-ab21-8ee1d50dc573#fd60b002-c5a5-4148-af90-0447e722c45a

    Vielleicht hattest Du es übersehen, da Du dort nicht darauf geantwortet hast.
    Dieser obige Hinweis war speziell für eine kurze Lösung gemacht.

    Hier auch kurz die Untermauerung meiner Aussage, das es ggf. die
    einfachere Variante ist, über Control zu gehen, als über ISynchronizeInvoke-Wrapper:

          The problem is that using ISynchronizeInvoke complicates the programming
          model significantly, and as a result it is often better to encapsulate the
          interaction with the ISynchronizeInvoke interface in controls and forms
          that will automatically use ISynchronizeInvoke as required.
    [Quelle]

    Weitere Standards (aber halt etwas aufwändiger) waren auch bereits im alten Thread von mir verlinkt. Grob etwas wie:

    [Invoke a delegate on a specific thread C# - Stack Overflow]
    http://stackoverflow.com/questions/3481075/invoke-a-delegate-on-a-specific-thread-c

    Als Info dazu vielleicht noch: Microsoft wird in diesem Bereich "BackgroundThreading und schnelles MainThread-Marshalling" in den nächsten Versionen Verbesserungen und Vereinfachungen bringen. Mehr darf ich da nicht sagen.

    Zum TaskScheduler noch mal vertieft. Folgende Lösung funktioniert normal für Windows Forms und für WPF in meinem Beispiel aus dem ersten Posting:

     void StartComputation()
     {
     var uiTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
     Task.Factory.StartNew(() => "Starte...")
     .ContinueWith(result => { td.Invoke("Demo Neu"); },
     uiTaskScheduler);
     }
    wobei man dann den 'uiTaskScheduler' eben als globales Objekt hält.

    [-> ParallelProgramsinNET4_CodingGuidelines.pdf]
    http://www.microsoft.com/downloads/en/details.aspx?FamilyID=c3ea8fb5-650d-434b-a216-7e54c53965d1&displaylang=en

    [TaskScheduler.FromCurrentSynchronizationContext - Parallel Programming with .NET - Site Home - MSDN Blogs]
    http://blogs.msdn.com/b/pfxteam/archive/2009/09/22/9898090.aspx

    Bei .NET 3.5 ggf. noch über Reactive Extensions Libs machbar.


    ciao Frank

    Freitag, 19. November 2010 16:33