2012-04-22 125 views
2

我手上有一個相當複雜的編程問題,請耐心等待幾分鐘。在同一程序的多個實例之間同步

我決定我想在WPF(C#)中創建一個媒體播放器,並且我遇到了一個泡菜。

我希望我的應用程序是單實例,這樣當用戶雙擊服務器文件時,程序將只運行一次並排隊所有文件進行播放。我試過了幾種方式來做到這一點,包括微軟的單實例實現,直到我決定創建我自己的,就像我在做某事並實現它(這可能是在互聯網上某處,但它沒有出現)

基本上,我使用一個命名互斥體來防止打開多個實例,並強制其他實例將它們的參數寫入一個文件,之後,創建互斥體的實例將讀取該文件。不用說,就性能而言,這是非常非常無效的,但無論如何,這裏是我對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); 
    } 

}

現在,基本上每個雙次點擊文件,一個實例主的()函數是由Windows創建的。 第一個實例可以控制互斥鎖並繼續做它想做的任何事情。 其他實例必須將它們的參數寫入文件。

現在,問題: 顯然,線程(所有這些線程)沒有正確同步,有時我得到IO異常。 我不知道究竟在哪裏引發了這些異常,因爲try-catch塊似乎什麼都不做。事實上,我認爲這比嘗試捕捉會更有力。

那麼,我如何同步用戶雙擊大量文件時產生的所有線程呢?這個實現可以和最多3個雙擊文件一起工作,有時(注意,有時可以工作,其他時間則不可以)有3個以上的文件(最多測試9個)。 目前爲止,我沒有發現互聯網帳戶上的多個獨立運行的應用程序的實例。

這將是巨大的,如果你能給我一個例子:)

謝謝。

+0

請勿將文件用於進程間通信。將filespec發送到WM_COPYDATA消息中的已經運行的實例,使用管道,使用TCP,幾乎除磁盤文件之外的任何東西。 – 2012-04-22 22:49:21

+0

我如何與WPF中的贏取消息進行交互?只要我知道,WPF是否最好阻止你這樣做。 – 2012-04-23 08:13:17

+0

不要在意:P – 2012-04-23 08:38:19

回答

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 
} 

然後在你的主類,你可以創建類的實例,將留到aplication運行。您可以檢查其他實例是否由FirstInstance屬性創建,並獲取由OtherInstanceCreated事件創建的其他實例的通知。

+0

謝謝你的例子。我會嘗試這個和WM:P – 2012-04-23 15:57:29

+0

我一直在某處發生ArgumentException(使用上面的SingleInstance實現)。 – 2012-04-23 19:08:51

相關問題