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

我有一个应用程序,在随后的开始检测是否有一个进程已经运行相同的名称,如果是这样,激活正在运行的应用程序的窗口,然后退出。

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

在启动时,以前的实例的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; } } } } } } 

Solutions Collecting From Web of "发送消息到Windows进程(而不是它的主窗口)"

以下是我如何做到这一点:

 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); } } } 

我希望我已经正确地转录了这个。 我不得不抛弃许多其他的东西,但我认为我有什么必要的。 我对我的作品没有失败。 如果你有问题,让我知道,我会看到我错过了什么。

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

在Program.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; } } } } } } 

在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(); } 

命名管道可以用于此。 它可能是.net的更可接受的方法。您可以在主应用程序中定义一个接受来自调用应用程序的消息的服务。 这是一个服务的例子,在VB中。 它调用主应用程序,并传递一个字符串,在这种情况下,一个文件名。 它也返回一个字符串,但是这里可以使用任何参数。

 Public Class PicLoadService : Implements IMainAppPicLoad Public Function LoadPic(ByVal fName As String) As String Implements IMainAppPicLoad.LoadPic ' do some stuff here. LoadPic = "return string" End Function End Class 

调用应用程序中的设置有一点涉及。 调用和主应用程序可以是同一个应用程序。

 Imports System.Diagnostics Imports System.ServiceModel Imports System.IO Imports vb = Microsoft.VisualBasic modulee MainAppLoader Sub Main() Dim epAddress As EndpointAddress Dim Client As picClient Dim s As String Dim loadFile As String Dim procs() As Process Dim processName As String = "MainApp" loadFile = "" ' filename to load procs = Process.GetProcessesByName(processName) If UBound(procs) >= 0 Then epAddress = New EndpointAddress("net.pipe://localhost/MainAppPicLoad") Client = New picClient(New NetNamedPipeBinding, epAddress) s = Client.LoadPic(loadFile) End If End Sub <System.Diagnostics.DebuggerStepThroughAttribute(), _ System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")> _ Partial Public Class picClient Inherits System.ServiceModel.ClientBase(Of IMainAppPicLoad) Implements IMainAppPicLoad Public Sub New(ByVal binding As System.ServiceModel.Channels.Binding, ByVal remoteAddress As System.ServiceModel.EndpointAddress) MyBase.New(binding, remoteAddress) End Sub Public Function LoadPic(ByVal fName As String) As String Implements IMainAppPicLoad.LoadPic Return MyBase.Channel.LoadPic(fName) End Function End Class ' from here down was auto generated by svcutil. ' svcutil.exe /language:vb /out:generatedProxy.vb /config:app.config http://localhost:8000/MainAppPicLoad ' Some has been simplified after auto code generation. <System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0"), _ System.ServiceModel.ServiceContractAttribute(ConfigurationName:="IMainAppPicLoad")> _ Public Interface IMainAppPicLoad <System.ServiceModel.OperationContractAttribute(Action:="http://tempuri.org/IMainAppPicLoad/LoadPic", ReplyAction:="http://tempuri.org/IMainAppPicLoad/LoadPicResponse")> _ Function LoadPic(ByVal fName As String) As String End Interface <System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")> _ Public Interface IMainAppPicLoadChannel Inherits IMainAppPicLoad, System.ServiceModel.IClientChannel End Interface <System.Diagnostics.DebuggerStepThroughAttribute(), _ System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "3.0.0.0")> _ Partial Public Class IMainAppPicLoadClient Inherits System.ServiceModel.ClientBase(Of IMainAppPicLoad) Implements IMainAppPicLoad Public Sub New(ByVal binding As System.ServiceModel.Channels.Binding, ByVal remoteAddress As System.ServiceModel.EndpointAddress) MyBase.New(binding, remoteAddress) End Sub Public Function LoadPic(ByVal fName As String) As String Implements IMainAppPicLoad.LoadPic Return MyBase.Channel.LoadPic(fName) End Function End Class End modulee <ServiceContract()> Public Interface IMainAppPicLoad <OperationContract()> Function LoadPic(ByVal fName As String) As String End Interface