2012-04-22 1 views
2

У меня есть довольно сложная проблема программирования на руках, так что несите меня в течение нескольких минут.Синхронизация между несколькими экземплярами одной и той же программы

Я решил создать медиа-плеер в WPF (C#), и я столкнулся с немного рассолом.

Я хочу, чтобы мое приложение было единым экземпляром, так что, когда пользователь дважды щелкает файлы сервера, программа запускается только один раз и ставит в очередь все файлы для воспроизведения.

Я пробовал несколько способов сделать это, включая реализацию одного экземпляра Microsoft, и ничего не работало, пока я не решил создать свой собственный, как в i хоть что-то и реализовал его (это, вероятно, было в Интернете где-то также, но это не отображалось)

В принципе, я использую именованный мьютекс, чтобы предотвратить открытие нескольких экземпляров, а также заставить другие экземпляры записывать свои аргументы в файл, а после этого , экземпляр, создавший мьютекс, будет читать файл. Излишне говорить, что это очень, очень неэффективно, поскольку производительность идет, но в любом случае, вот моя реализация функции Main(). Обратите внимание, что этот Main() также написан с нуля, так как мне не понравился тот, который автоматически генерируется VS2010.

static void Main(string[] args) 
    { 

      string[] arguments = new string[0]; 
      handler g = new handler(); 
      bool createdNew = false; 
      Mutex lolpaca = new Mutex(true, "lolpacamaximumtrolololololol", out createdNew); 
      if (createdNew) 
      { 

       if (args != null) 
       { 
        var MainWindow = new MainWindow(); 
        var app = new Application(); 
        app.Run(MainWindow); 
        lolpaca.ReleaseMutex(); 
        lolpaca.Dispose(); 
       } 
       else 
       { 
          Array.Resize(ref arguments, 1); 
          arguments[0] = args[0]; 
          string line; 
        //nu mai arunca exceptii nenorocitule 

          while ((line = g.ReadArgs()) != null) 
          { 
           int old_size = arguments.Length; 
           Array.Resize(ref arguments, arguments.Length + 1); 
           arguments[old_size] = line; 
          } 


        var MainWindow = new MainWindow(arguments, arguments.Length); 
        var app = new Application(); 
        app.Run(MainWindow); 
        lolpaca.ReleaseMutex(); 
        lolpaca.Dispose(); 

       } 
       if (File.Exists(path)) 
       { 
        File.Delete(path); 
       } 
      } 

      else 
      { 
       Thread writer = new Thread(new ParameterizedThreadStart(g.WriteArg)); 
       writer.Start(args); 
       writer.Join(); 

       try 
       { 
        g.WriteArg(args); 
       } 
       catch (IOException e) 
       { 
        MediaPlayerFinal_GUI_new.ExceptionCatcher exp = new MediaPlayerFinal_GUI_new.ExceptionCatcher(e.Source); 
        exp.Show(); 
       } 

      } 

    } 

Я также использую этот класс, чтобы попытаться синхронизировать между нитями

public class handler 
{ 
    static string path = @"D:\playlist.txt"; 
    static FileStream fs = new FileStream(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); 
    string line; 

    string arg; 
    bool readerFlag = false; 
    public string ReadArgs() 
    { 
     try 
     { 
      lock (fs) // Enter synchronization block 
      { 
       if (!readerFlag) 
       {   // Wait until writer finishes 
        try 
        { 
         // Waits for the Monitor.Pulse in WriteArg 
         Monitor.Wait(fs); 
        } 
        catch (SynchronizationLockException) 
        { 

        } 
        catch (ThreadInterruptedException) 
        { 

        } 
       } 


       TextReader tr = new StreamReader(fs); 
       while ((line = tr.ReadLine()) != null) 
       { 
        arg = line; 
       } 
       tr.Close(); 
       tr.Dispose(); 

      } 

      /* fs.Close(); 
      fs.Dispose();*/ 
      readerFlag = false; 
      Monitor.Pulse(fs); 
      return arg; 
     } 
     catch (IOException e) 
     { 
      MediaPlayerFinal_GUI_new.ExceptionCatcher exp = new MediaPlayerFinal_GUI_new.ExceptionCatcher(e.Source); 
      exp.Show(); 
      return null; 
     } 
    } 
    public void WriteArg(object args) 
    { 
     lock (fs) 
     { 
      try 
      { 
       if (readerFlag) 
       { 
        try 
        { 
         Monitor.Wait(fs); // Wait for the Monitor.Pulse in ReadArgs 
        } 
        catch (SynchronizationLockException) 
        { 

        } 
        catch (ThreadInterruptedException) 
        { 

        } 
       } 
       arg = Convert.ToString(args); 
       // FileStream fs = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.Read);     
       TextWriter tw = new StreamWriter(fs); 
       tw.WriteLine(args); 
       tw.Close(); 
       tw.Dispose(); 


      } 
      catch (IOException e) 
      { 
       MediaPlayerFinal_GUI_new.ExceptionCatcher exp = new MediaPlayerFinal_GUI_new.ExceptionCatcher(e.Source); 
       exp.Show(); 
      } 
     } 
     /* fs.Close(); 
     fs.Dispose();*/ 
     readerFlag = true; 
     Monitor.Pulse(fs); 
    } 

}

Сейчас, в основном, для каждого двойного щелкнутой файла, один экземпляр Main() функция создается Windows. Первый экземпляр имеет контроль над мьютексом и переходит к тому, что он хочет делать. Другие экземпляры должны записать свой аргумент в файл.

Теперь проблема: По-видимому, нити (все они) не синхронизируются должным образом, а иногда я получаю исключения IO. У меня нет подсказки, где именно эти исключения выбрасываются, потому что блоки try-catch, похоже, ничего не делают. На самом деле, я считаю, что это немного глубже, чем попытаться поймать.

Итак, как мне синхронизировать все потоки, которые появляются, когда пользователь дважды щелкает много файлов? Эта реализация работает нормально с 3 файлами с двойным щелчком, а иногда (обратите внимание, иногда это работает, иногда это не так) с более чем 3 файлами (проверено до 9). Ничего, что я нашел до сих пор в Интернете, вызывает несколько экземпляров одного и того же приложения, работающих независимо.

Было бы здорово, если бы вы дать мне пример :)

Спасибо.

+0

Не используйте файлы для обмена между процессами. Отправьте файл спецификаций на уже запущенный экземпляр в сообщении WM_COPYDATA, используйте каналы, используйте TCP, почти все, кроме файлов на диске. –

+0

И как именно я взаимодействую с win-сообщениями в WPF? пока я знаю, WPF делает все возможное, чтобы вы этого не делали. –

+0

Не обращайте на это внимания: P –

ответ

0

Лучшим способом поговорить между двумя экземплярами одного и того же приложения является использование IPC. Пыльник см пример класса, который может быть использован, чтобы помочь с одним экземпляром:

/// <summary> 
     /// Enforces single instance for an application. 
     /// </summary> 
     public class SingleInstance : IDisposable 
     { 
      #region Fields 

      /// <summary> 
      /// The synchronization context. 
      /// </summary> 
      private readonly SynchronizationContext synchronizationContext; 

      /// <summary> 
      /// The disposed. 
      /// </summary> 
      private bool disposed; 

      /// <summary> 
      /// The identifier. 
      /// </summary> 
      private Guid identifier = Guid.Empty; 

      /// <summary> 
      /// The mutex. 
      /// </summary> 
      private Mutex mutex; 

      #endregion 

      #region Constructors and Destructors 

      /// <summary> 
      /// Initializes a new instance of the <see cref="SingleInstance"/> class. 
      /// </summary> 
      /// <param name="identifier"> 
      /// An identifier unique to this application. 
      /// </param> 
      /// <param name="args"> 
      /// The command line arguments. 
      /// </param> 
      public SingleInstance(Guid identifier, IEnumerable<string> args) 
      { 
       this.identifier = identifier; 

       bool ownsMutex; 
       this.mutex = new Mutex(true, identifier.ToString(), out ownsMutex); 

       this.synchronizationContext = SynchronizationContext.Current; 

       this.FirstInstance = ownsMutex; 

       if (this.FirstInstance) 
       { 
        this.ListenAsync(); 
       } 
       else 
       { 
        this.NotifyFirstInstance(args); 
       } 
      } 

      /// <summary> 
      /// Initializes a new instance of the <see cref="SingleInstance"/> class. 
      /// </summary> 
      /// <param name="identifier"> 
      /// An identifier unique to this application. 
      /// </param> 
      public SingleInstance(Guid identifier) 
       : this(identifier, null) 
      { 
      } 

      #endregion 

      #region Public Events 

      /// <summary> 
      /// Event raised when arguments are received from successive instances. 
      /// </summary> 
      public event EventHandler<OtherInstanceCreatedEventArgs> OtherInstanceCreated; 

      #endregion 

      #region Public Properties 

      /// <summary> 
      /// Gets a value indicating whether this is the first instance of this application. 
      /// </summary> 
      public bool FirstInstance { get; private set; } 

      #endregion 

      #region Implemented Interfaces 

      #region IDisposable 

      /// <summary> 
      /// The dispose. 
      /// </summary> 
      public void Dispose() 
      { 
       this.Dispose(true); 
       GC.SuppressFinalize(this); 
      } 

      #endregion 

      #endregion 

      #region Methods 

      /// <summary> 
      /// Clean up any resources being used. 
      /// </summary> 
      /// <param name="disposing"> 
      /// True if managed resources should be disposed; otherwise, false. 
      /// </param> 
      protected virtual void Dispose(bool disposing) 
      { 
       if (this.disposed) 
       { 
        return; 
       } 

       if (disposing) 
       { 
        if (this.mutex != null && this.FirstInstance) 
        { 
         this.mutex.WaitOne(); 
         this.mutex.ReleaseMutex(); 
         this.mutex = null; 
        } 
       } 

       this.disposed = true; 
      } 

      /// <summary> 
      /// Fires the OtherInstanceCreated event. 
      /// </summary> 
      /// <param name="arguments"> 
      /// The arguments to pass with the <see cref="OtherInstanceCreatedEventArgs"/> class. 
      /// </param> 
      protected virtual void OnOtherInstanceCreated(OtherInstanceCreatedEventArgs arguments) 
      { 
       EventHandler<OtherInstanceCreatedEventArgs> handler = this.OtherInstanceCreated; 

       if (handler != null) 
       { 
        handler(this, arguments); 
       } 
      } 

      /// <summary> 
      /// Listens for arguments on a named pipe. 
      /// </summary> 
      private void Listen() 
      { 
       try 
       { 
        using (var server = new NamedPipeServerStream(this.identifier.ToString())) 
        { 
         using (var reader = new StreamReader(server)) 
         { 
          server.WaitForConnection(); 
          var arguments = new List<string>(); 

          while (server.IsConnected) 
          { 
           arguments.Add(reader.ReadLine()); 
          } 

          this.synchronizationContext.Post(o => this.OnOtherInstanceCreated(new OtherInstanceCreatedEventArgs(arguments)), null);       
         } 
        } 

        // start listening again. 
        this.Listen(); 
       } 
       catch (IOException) 
       { 
        // Pipe was broken, listen again. 
        this.Listen(); 
       }   
      } 

      /// <summary> 
      /// Listens for arguments being passed from successive instances of the applicaiton. 
      /// </summary> 
      private void ListenAsync() 
      { 
       Task.Factory.StartNew(this.Listen, TaskCreationOptions.LongRunning); 
      } 

      /// <summary> 
      /// Passes the given arguments to the first running instance of the application. 
      /// </summary> 
      /// <param name="arguments"> 
      /// The arguments to pass. 
      /// </param> 
      private void NotifyFirstInstance(IEnumerable<string> arguments) 
      { 
       try 
       { 
        using (var client = new NamedPipeClientStream(this.identifier.ToString())) 
        { 
         using (var writer = new StreamWriter(client)) 
         { 
          client.Connect(200); 

          if (arguments != null) 
          { 
           foreach (string argument in arguments) 
           { 
            writer.WriteLine(argument); 
           } 
          } 
         } 
        } 
       } 
       catch (TimeoutException) 
       { 
        // Couldn't connect to server 
       } 
       catch (IOException) 
       { 
        // Pipe was broken 
       } 
      } 



#endregion 
    } 

/// <summary> 
/// Holds a list of arguments given to an application at startup. 
/// </summary> 
public class OtherInstanceCreatedEventArgs : EventArgs 
{ 
    #region Constructors and Destructors 

    /// <summary> 
    /// Initializes a new instance of the <see cref="OtherInstanceCreatedEventArgs"/> class. 
    /// </summary> 
    /// <param name="args"> 
    /// The command line arguments. 
    /// </param> 
    public OtherInstanceCreatedEventArgs(IEnumerable<string> args) 
    { 
     this.Args = args; 
    } 

    #endregion 

    #region Public Properties 

    /// <summary> 
    /// Gets the startup arguments. 
    /// </summary> 
    public IEnumerable<string> Args { get; private set; } 

    #endregion 
} 

Затем в главном классе можно создать экземпляр класса, который будет оставаться до тех пор, пока будет ПРИМЕНЕНИЕ работает. Вы можете проверить, создан ли другой экземпляр с помощью свойства FirstInstance, и получить уведомление о другом экземпляре, созданном событием OtherInstanceCreated.

+0

Спасибо за ваши примеры. Я попробую это и WM: P –

+0

Я все время получаю исключение ArgumentException (используя реализацию SingleInstance выше). –