2010-08-28 15 views
0

J'ai une console que je veux utiliser pour invoquer des commandes sur un WinForm dans un autre ordinateur (je le teste cependant via localhost). Lorsque le formulaire démarre, il instancie CommandListener pour recevoir des commandes via TCP. Chaque fois que je tente de l'instancier sans un thread séparé, la winform ne s'affiche pas du tout, j'ai donc utilisé "Initialize" pour l'exécuter sur un thread séparé.C# Recevoir des commandes à distance sur TCP et les appeler sur WinForm (Multithread)

public CommandListener(Form client) 
    { 
     this.ClientControl = client; 

     Socket CommandSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); 
     IPAddress ipa = IPAddress.Loopback; 
     IPEndPoint ipe = new IPEndPoint(ipa, 23120); 

     CommandSocket.Bind(ipe); 
     CommandSocket.Listen(1); 

     Thread RemoteCommandListener = new Thread(new ParameterizedThreadStart(Initialize)); 
     RemoteCommandListener.Start(CommandSocket); 

    } 

    private void Initialize(object obj) 
    { 
     Socket CommandSocket = (Socket)obj; 

     while (true) 
     { 
      allDone.Reset(); 
      CommandSocket.BeginAccept(new AsyncCallback(AcceptCallback), CommandSocket); 
      allDone.WaitOne(); 
     } 
    } 

Malheureusement, si j'utilise un thread séparé, je reçois « opération de fil croix non valide » comme une erreur lors d'une tentative d'invoquer la commande sur le WinForm.

  int bytesRead = Master.EndReceive(ar); 
      if (bytesRead > 0) 
      { 
       state.sb.Append(Encoding.ASCII.GetString(state.Buffer, 0, bytesRead)); 

       command = state.sb.ToString(); 
       if (command.IndexOf("Write") > -1) 
       { 
        try 
        { 
         MethodInfo method = typeof(Multiboxxy).GetMethod(command); 
         method.Invoke(ClientControl, new object[] { "Success!" }); 
        } 
        catch (Exception e) 
        { 
         MessageBox.Show(e.InnerException.Message); 
        } 
       } 
       else 
       { 
        Master.BeginReceive(state.Buffer, 0, StateObject.BufferSize, 0, 
         new AsyncCallback(ReadCallback), state); 
       } 
      } 

Répondre

0

Au lieu de MethodInfo.Invoke, utilisez:

// somewhere, define a delegate type for the invoked method (e.g. 'InvokerDelegate') 

if (ClientControl.InvokeRequired) 
    ClientControl.Invoke(Delegate.CreateDelegate(typeof(InvokerDelegate), ClientControl, method), "Success!"); 
else 
    method.Invoke(ClientControl, new object[] { "Success!" }); 

La méthode de classe Invoke()Control est, au mieux de ma connaissance, la seule façon d'effectuer une bonne synchronisation des threads lors de l'appel des méthodes sur les contrôles.

+0

Merci pour la réponse, mais pourriez-vous donner un exemple pour le "InvokerDelegate"? –

+0

Bien dans l'exemple que vous avez posté, le délégué serait défini comme: 'déléguer void InvokerDelegate (chaîne arg);' - puisque vous ne faites rien avec la valeur de retour et que vous ne passez qu'une chaîne comme paramètre ... –

+0

pour référence future, 'ISynchronizeInvoke' (qui inclut' Control.Invoke') est un mécanisme de synchronisation obsolète. Il limite le code pour fonctionner uniquement avec Windows Forms. Son remplacement est 'SynchronizationContext' (introduit dans .NET 2.0), qui fonctionne avec Windows Forms, WPF, Silverlight, ASP.NET, les applications de la console, les services Win32, etc. –

1

Je recommande d'utiliser WCF à la place; il y a une option dans WCF pour synchroniser automatiquement au SynchronizationContext de l'hôte.

La meilleure solution consiste à utiliser des objets socket synchronisés automatiquement, comme ceux du Nito.Async.

Une troisième option est de garder la classe Socket .NET mais quand vous devez faire des mises à jour de l'interface utilisateur, utilisez une Task prévue pour le thread d'interface utilisateur (TaskScheduler.FromCurrentSynchronizationContext). Task et TaskScheduler sont intégrés dans .NET 4.0 et sont available in a library pour .NET 3.5.

Une quatrième option consiste à conserver la classe .NET Socket et à utiliser SynchronizationContext directement pour la mise à jour de l'interface utilisateur.