将消息发送到Windows进程(而不是其主窗口)

chi*_*tza 9 .net c# windows interop

我有一个应用程序,在后续启动时检测是否已经运行了同名的进程,如果是,则激活正在运行的应用程序窗口,然后退出.

问题是主窗口可能被隐藏(只有通知区域图标可见),因此没有窗口句柄.

在启动时,前一个实例的MainWindowHandle属性为0,所以我无法发送ShowWindowPostMessage.

有没有办法可以发送一条可以被正在运行的应用程序截获的消息,从而允许它显示其主窗口?

该应用程序是用C#编写的,我正在使用以下代码实现此目的.

[STAThread]
static void Main()
{
    bool createdNew = true;
    using (Mutex mutex = new Mutex(true, "MyMutexName", out createdNew))
    {
        if (createdNew)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }
        else
        {
            Process current = Process.GetCurrentProcess();
            foreach (Process process in Process.GetProcessesByName(current.ProcessName))
            {
                if (process.Id != current.Id)
                {
                    Interop.WINDOWINFO pwi = new Interop.WINDOWINFO();
                    IntPtr handle = process.MainWindowHandle;
                    var isVisible = Interop.GetWindowInfo(handle, ref pwi);
                    if (!isVisible)
                    {
                        MessageBox.Show(Constants.APP_NAME + " is already running, check the notification area (near the clock).", 
                                        Constants.APP_NAME, MessageBoxButtons.OK, MessageBoxIcon.Information);//temporary message, until I find the solution
                        //Interop.ShowWindow(handle, Interop.WindowShowStyle.ShowNormal);
                        //Interop.PostMessage(handle, Interop.WM_CUSTOM_ACTIVATEAPP, IntPtr.Zero, IntPtr.Zero);
                    }
                    else
                        Interop.SetForegroundWindow(handle);//this works when the window is visible
                        break;
                    }
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Mat*_*vis 9

这是我如何做到这一点:

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;
public partial class MainForm : Form
{
    #region Dll Imports
    private const int HWND_BROADCAST = 0xFFFF;

    private static readonly int WM_MY_MSG = RegisterWindowMessage( "WM_MY_MSG" );

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

    [DllImport( "user32" )]
    private static extern int RegisterWindowMessage(string message);
    #endregion Dll Imports
    static Mutex _single = new Mutex(true, "{4EABFF23-A35E-F0AB-3189-C81203BCAFF1}");
    [STAThread]
    static void Main()
    {
        // See if an instance is already running...
        if (_single.WaitOne(TimeSpan.Zero, true)) {
            // No...start up normally.
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            try {
                Application.Run(new MainForm());
            } catch (Exception ex) {
                // handle exception accordingly
            } finally {
                _single.ReleaseMutex();
            }
        } else {
            // Yes...Bring existing instance to top and activate it.
            PostMessage(
                (IntPtr) HWND_BROADCAST,
                WM_MY_MSG,
                new IntPtr(0xCDCD),
                new IntPtr(0xEFEF));
        }
    }

    protected override void WndProc(ref Message m)
    {
        if (m.Msg == WM_MY_MSG) {
            if ((m.WParam.ToInt32() == 0xCDCD) && (m.LParam.ToInt32() == 0xEFEF)) {
                if (WindowState == FormWindowState.Minimized) {
                    WindowState = FormWindowState.Normal;
                }
                // Bring window to front.
                bool temp = TopMost;
                TopMost = true;
                TopMost = temp;
                // Set focus to the window.
                Activate();
            }
        } else {
            base.WndProc(ref m);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我希望我已经正确地转录了这个.我不得不遗漏很多其他东西,但我认为我得到了必要的东西.我对我的作品一定会给你带来好处.如果您有问题,请告诉我,我会看到我错过了什么.


chi*_*tza 5

对于其他想要实现这一目标的人,我使用马特·戴维斯的解决方案在我的实现下方发布。

在程序.cs中

static class Program
{
    #region Dll Imports
    public const int HWND_BROADCAST = 0xFFFF;

    [DllImport("user32.dll")]
    public static extern bool SetForegroundWindow(IntPtr hWnd);

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

    [DllImport("user32")]
    public static extern int RegisterWindowMessage(string message);
    #endregion Dll Imports

    public static readonly int WM_ACTIVATEAPP = RegisterWindowMessage("WM_ACTIVATEAPP");

    [STAThread]
    static void Main()
    {
        bool createdNew = true;
        //by creating a mutex, the next application instance will detect it
        //and the code will flow through the "else" branch 
        using (Mutex mutex = new Mutex(true, "MyMutexName", out createdNew))//make sure it's an unique identifier (a GUID would be better)
        {
            if (createdNew)
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.Run(new MainForm());
            }
            else
            {
                //we tried to create a mutex, but there's already one (createdNew = false - another app created it before)
                //so there's another instance of this application running
                Process currentProcess = Process.GetCurrentProcess();

                //get the process that has the same name as the current one but a different ID
                foreach (Process process in Process.GetProcessesByName(currentProcess.ProcessName))
                {
                    if (process.Id != currentProcess.Id)
                    {
                        IntPtr handle = process.MainWindowHandle;

                        //if the handle is non-zero then the main window is visible (but maybe somewhere in the background, that's the reason the user started a new instance)
                        //so just bring the window to front
                        if (handle != IntPtr.Zero)
                            SetForegroundWindow(handle);
                        else
                            //tough luck, can't activate the window, it's not visible and we can't get its handle
                            //so instead notify the process that it has to show it's window
                            PostMessage((IntPtr)HWND_BROADCAST, WM_ACTIVATEAPP, IntPtr.Zero, IntPtr.Zero);//this message will be sent to MainForm

                        break;
                    }
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

在MainForm.cs中

protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);
            //someone (another process) said that we should show the window (WM_ACTIVATEAPP)
    if (m.Msg == Program.WM_ACTIVATEAPP)
        this.Show();
}
Run Code Online (Sandbox Code Playgroud)