2

这是场景,我打开我的应用程序然后显示托盘图标,如果我双击托盘图标,将显示主界面。如果我再次打开我的应用程序,主界面应该获得焦点,或者如果它尚未显示,那么应该显示它而不是打开我的应用程序的另一个实例。

这是我的代码的样子:

//Program.cs
.....

if(myAppIsNotRunningYet) //the program has not been open yet
{
MyTray = new MyTray();
Application.Run();
}

else //the program is already on the tray
{
//the code to give focus to the mainForm or open it up if not yet open
}

//MyTray.cs
.....
public MyTray()
{
notifyIcon = new NotifyIcon();
....
notifyIcon.Visible = true;
}

private void notifyIcon_DoubleClick(object sender, EventArgs e)
{
MainForm mainForm = new MainForm();
mainForm.ShowDialog();
}
4

3 回答 3

5

编辑:好的,看来要正确覆盖 WndProc,您必须使用/已经可见的表单。所以下面是使用MessageFilter的不同解决方案。这确实有效,所以希望你能从这里开始!

 internal sealed class Program
 { 
  /// <summary>
  /// Program entry point.
  /// </summary>
  [STAThread]
  public static void Main(string[] args)
  {
    bool newMutex;
    System.Threading.Mutex mutex = new System.Threading.Mutex(true, "{9F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}", out newMutex);

     // Attempt aquire the mutex
     if(newMutex)
     {
      // If we are able to aquire the mutex, it means the application is not running.
         Application.EnableVisualStyles();
         Application.SetCompatibleTextRenderingDefault(false);

         // Create the new tray icon
         MyTray myTray = new MyTray();

         Application.AddMessageFilter(myTray);
         Application.Run();

         // Release the mutex on exit
         mutex.ReleaseMutex();
     }
     else
     {
        // If the aquire attempt fails, the application is already running
        // so we broadcast a windows message to tell it to wake up.
         NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_SHOWME, IntPtr.Zero, IntPtr.Zero);
     }
     }
 }
}

internal class NativeMethods
 {
     public const int HWND_BROADCAST = 0xffff;
     public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");

     [DllImport("user32")]
     public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);

     [DllImport("user32")]     
     public static extern int RegisterWindowMessage(string message);
 }

 public class MyTray : IMessageFilter
 {
  private NotifyIcon notifyIcon = new NotifyIcon();
  private Form myForm = new Form();

  public MyTray()
  {
     this.notifyIcon.Icon = System.Drawing.Icon.FromHandle(new System.Drawing.Bitmap(16,16).GetHicon());
     this.notifyIcon.Visible = true;
     this.notifyIcon.DoubleClick += delegate(object sender, EventArgs e) { ShowForm(); };
  }

     void ShowForm()
     {
        this.notifyIcon.Visible = false;            
        this.myForm.ShowDialog();   
        this.notifyIcon.Visible = true;
      }

    public bool PreFilterMessage(ref Message m)
    {
       // If the message is the 'show me' message, then hide the icon and show the form.
       if(m.Msg == NativeMethods.WM_SHOWME)
       {
            if (!this.myForm.Visible)
            {
                ShowForm();
                return true; // Filter the message
            }
       }

       return false; // Forward the message
    }
 }

编辑:我整理了一个更接近您的场景的示例:

 internal sealed class Program
 {
  static System.Threading.Mutex mutex = new System.Threading.Mutex(true, "{8F6F0AC4-B9A1-45fd-A8CF-72F04E6BDE8F}");

  /// <summary>
  /// Program entry point.
  /// </summary>
  [STAThread]
  private static void Main(string[] args)
  {
   // Attempt aquire the mutex
         if(mutex.WaitOne(TimeSpan.Zero, true))
         {
          // If we are able to aquire the mutex, it means the application is not running.
             Application.EnableVisualStyles();
             Application.SetCompatibleTextRenderingDefault(false);

             // Create the new tray icon
             MyTray myTray = new MyTray();

             Application.Run();

             // Release the mutex on exit
             mutex.ReleaseMutex();
         }
         else
         {
            // If the aquire attempt fails, the application is already running
            // so we broadcast a windows message to tell it to wake up.
             NativeMethods.PostMessage((IntPtr)NativeMethods.HWND_BROADCAST, NativeMethods.WM_SHOWME, IntPtr.Zero, IntPtr.Zero);
         }
     }
 }

 internal class NativeMethods
 {
     public const int HWND_BROADCAST = 0xffff;
     public static readonly int WM_SHOWME = RegisterWindowMessage("WM_SHOWME");

     [DllImport("user32")]
     public static extern bool PostMessage(IntPtr hwnd, int msg, IntPtr wparam, IntPtr lparam);

     [DllImport("user32")]     
     public static extern int RegisterWindowMessage(string message);
 }

 public class MyTray : Control
 {
  private NotifyIcon notifyIcon = new NotifyIcon();

  public MyTray()
  {
   this.notifyIcon.Visible = true;
  }

  /// <summary>
  /// This method listens to all windows messages either broadcast or sent to this control
  /// </summary>
  protected override void WndProc(ref Message m)
  {
   // If the message is the 'show me' message, then hide the icon and show the form.
   if(m.Msg == NativeMethods.WM_SHOWME)
   {
    this.notifyIcon.Visible = false;
    using (Form mainForm = new Form())
    {
     mainForm.ShowDialog();
     this.notifyIcon.Visible = true;
    }  
   }
   else
   {
    base.WndProc(ref m);    
   }   
  }
 }

编辑: 我在 C# 中找到了一个结合互斥锁和 windows 消息的示例:

C# .NET 单实例应用程序



互斥锁可能是最好的方法。将此与您的应用程序侦听的自定义窗口消息相结合,以使自己成为焦点(请参阅通过窗口消息传递的 VB.NET、VB6 和 C# 进程间通信)。

查看此示例: C# - 单个应用程序实例

于 2009-11-17T18:35:39.297 回答
0

我认为您不能在另一个应用程序的窗口上引发事件(即使它们是同一个可执行文件)。

不过,我解决它的方法是使用一些IPC 机制来告诉正在运行的实例打开主窗口。相同的 IPC 机制也可用于确定另一个实例是否正在运行。

于 2009-11-17T18:39:40.743 回答
0

You can also take a look at the SwitchToThisWindow function.

When a second instance of the application starts you can just call that function with the main window handle of the first instance. You can get the main window handle with the Process.MainWindowHandle property.

于 2009-11-18T15:07:22.897 回答