2009-07-31 6 views
1

J'ai une application WinForms, où s'il y a déjà une instance exécutant & l'utilisateur essaye d'en créer une autre, je l'arrête en vérifiant par rapport à un Mutex avant d'appeler Application.Run(). Cette partie fonctionne très bien. Ce que je voudrais faire est de passer un message de la nouvelle instance de l'application (avec un morceau de données sous forme de chaîne) à l'instance existante avant de tuer le nouveau processus. J'ai essayé d'appeler PostMessage, et je reçois le message sur l'application en cours d'exécution, mais la chaîne que je passe dans le lparam est défaillante (oui, j'ai vérifié que je passe dans une bonne chaîne pour commencer). Comment puis-je faire mieux?Comment envoyer un message d'une instance d'une application gérée à une autre?

static class Program 
{ 
    [DllImport("user32.dll", CharSet = CharSet.Auto)] 
    public static extern bool PostMessage(int hhwnd, uint msg, IntPtr wparam, IntPtr lParam); 

    [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)] 
    static extern uint RegisterWindowMessage(string lpString); 

    private const int HWND_BROADCAST = 0xffff; 
    static uint _wmJLPC = unchecked((uint)-1); 

    /// <summary> 
    /// The main entry point for the application. 
    /// </summary> 
    [STAThread] 
    static void Main() 
    { 
     _wmJLPC = RegisterWindowMessage("JumpListProjectClicked"); 
     if (_wmJLPC == 0) 
     { 
      throw new Exception(string.Format("Error registering window message: \"{0}\"", Marshal.GetLastWin32Error().ToString())); 
     } 

     bool onlyInstance = false; 
     Mutex mutex = new Mutex(true, "b73fd756-ac15-49c4-8a9a-45e1c2488599_ProjectTracker", out onlyInstance); 

     if (!onlyInstance) { 
      ProcessArguments(); 
      return; 
     } 

     Application.EnableVisualStyles(); 
     Application.SetCompatibleTextRenderingDefault(false); 
     Application.Run(new MainForm()); 

     GC.KeepAlive(mutex); 
    } 

    internal static void ProcessArguments() 
    { 
     if (Environment.GetCommandLineArgs().Length > 1) 
     { 
      IntPtr param = Marshal.StringToHGlobalAuto(Environment.GetCommandLineArgs()[1]); 
      PostMessage(HWND_BROADCAST, _wmJLPC, IntPtr.Zero, param); 
     } 
    } 
} 

Par ailleurs, dans ma forme ...

protected override void WndProc(ref Message m) 
{ 
    try 
    { 
     if (m.Msg == _wmJLPC) 
     { 
      // always returns an empty string 
      string param = Marshal.PtrToStringAnsi(m.LParam); 

      // UI code omitted 
     } 
    } 
    catch (Exception ex) 
    { 
     HandleException(ex); 
    } 

    base.WndProc(ref m); 
} 

Répondre

3

Greg,

Le pointeur non géré créé par StringToHGlobalAuto est uniquement valable dans l'espace de processus qui l'a créé. La mémoire à laquelle il fait référence n'est pas accessible depuis l'autre processus.

Pour transmettre des données d'une application à une autre, utilisez SendMessage() avec le message WM_COPYDATA.

Scott

0

Vérifiez votre nouveau code. Vous utilisez StringToHGlobalAuto pour créer la chaîne (se terminant probablement avec Unicode). Ensuite, vous appelez PtrToStringAnsi, qui n'utilise pas unicode.

Si cette solution ne fonctionne pas, plusieurs options s'offrent à vous. Vous pouvez lire à leur sujet en cherchant IPC (InterProcess Communication.)

Une méthode que j'ai utilisée, car il était rapide et facile, est de créer un fichier bien connu avec le contenu nécessaire. Vous verrouillez l'utilisation de ce fichier avec des événements nommés et dites à l'application "owner" que le fichier a été modifié en définissant un autre événement nommé. Le "propriétaire" vérifie occasionnellement l'événement ou lance un thread de travail pour le surveiller.

Encore une fois, IPC a beaucoup de saveurs, si ces idées ne fonctionnent pas, continuer à chercher.

0

Il existe des méthodes beaucoup plus simples et modernes pour faire de la communication inter-processus de nos jours. en particulier, consultez WCF. Bien que je vais admettre qu'il y a une petite partie d'une courbe d'apprentissage. Une fois que vous l'avez compris, c'est vraiment très facile. Vous pouvez même le faire tous par programmation afin de ne pas avoir à vous soucier des confusions de configuration.

0

Voici une manière simple. Je n'ai pas exécuter le code, mais vous obtenez l'idée

class Program 
{ 
    static Thread listenThread; 
    static void Main(string[] args) 
    { 
     try 
     { 
      using (Mutex mutex = new Mutex(true, "my mutex")) 
      { 
       listenThread = new Thread(Listen); 
       listenThread.IsBackground = true; 
       listenThread.Start(); 
      } 
     } 
     catch (ApplicationException) 
     { 
      using (Mutex mutex = Mutex.OpenExisting("my mutex")) 
      { 
       mutex.WaitOne(); 
       try 
       { 
        using (NamedPipeClientStream client = new NamedPipeClientStream("some pipe")) 
        { 
         using (StreamWriter writer = new StreamWriter(client)) 
         { 
          writer.WriteLine("SomeMessage"); 
         } 
        } 
       } 
       finally 
       { 
        mutex.ReleaseMutex(); 
       } 
      } 
     } 
    } 
    static void Listen() 
    { 
     using (NamedPipeServerStream server = new NamedPipeServerStream("some pipe")) 
     { 
      using (StreamReader reader = new StreamReader(server)) 
      { 
       for (; ;) 
       { 
        server.WaitForConnection(); 
        string message = reader.ReadLine(); 
        //Dispatch the message, probably onto the thread your form 
        // was contructed on with Form.BeginInvoke 

       } 
      } 
     } 
    } 
Questions connexes