Invia un messaggio a un processo di Windows (non la sua finestra principale)

Ho un’applicazione che all’avvio successivo rileva se esiste già un processo con lo stesso nome e, in caso affermativo, triggers la finestra dell’app in esecuzione e quindi esce.

Il problema è che la finestra principale potrebbe essere nascosta (visibile solo l’icona dell’area di notifica), lasciandomi quindi senza maniglia della finestra.

All’avvio, la proprietà MainWindowHandle dell’istanza precedente è 0, quindi non è ansible inviare ShowWindow o PostMessage .

C’è un modo in cui posso inviare un messaggio che può essere intercettato dall’app in esecuzione, permettendo così di visualizzare la sua finestra principale?

L’applicazione è scritta in C #, il codice che sto usando per ottenere questo sotto.

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

Ecco come ho fatto questo:

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

Spero di averlo trascritto correttamente. Ho dovuto lasciare fuori molte altre cose, ma penso di aver ottenuto ciò che è necessario. Quello che ho funziona per me senza fallo. Se hai un problema, fammelo sapere, e vedrò cosa mi sono perso.

Per altre persone che vogliono raggiungere questo objective, sto postando sotto la mia implementazione, usando la soluzione di Matt Davis.

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

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

Per questo si possono usare named pipe. Potrebbe essere il metodo più accettabile con .net. È ansible definire un servizio nell’applicazione principale che accetta un messaggio dall’applicazione chiamante. Ecco un esempio del servizio, in vb. Chiama l’app principale e passa una stringa ad esso, in questo caso, un nome di file. Restituisce anche una stringa, ma qui è ansible utilizzare qualsiasi parametro.

 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 

L’installazione nell’applicazione chiamante è un po ‘più coinvolta. La chiamata e l’applicazione principale possono essere la stessa applicazione.

 Imports System.Diagnostics Imports System.ServiceModel Imports System.IO Imports vb = Microsoft.VisualBasic Module 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  _ 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.  _ Public Interface IMainAppPicLoad  _ Function LoadPic(ByVal fName As String) As String End Interface  _ Public Interface IMainAppPicLoadChannel Inherits IMainAppPicLoad, System.ServiceModel.IClientChannel End Interface  _ 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 Module  Public Interface IMainAppPicLoad  Function LoadPic(ByVal fName As String) As String End Interface