Запустите текущее приложение как единичный экземпляр и покажите предыдущий экземпляр - PullRequest
0 голосов
/ 27 мая 2018

Я только что реализовал этот код, который защищает единый экземпляр приложения, чтобы не запускать приложение дважды.

Теперь мне интересно, как я могу показать исходный процесс приложения, которыйуже запущен.

Вот мой код в классе программы:

static class Program
{
    [STAThread]
    static void Main()
    {
        const string appName = "MyappName";
        bool createdNew;
        mutex = new Mutex(true, appName, out createdNew);

        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Form form = new Form1();

        if (!createdNew)
        {
            form.Show();  <<=========================== NOT WORKING
            form.Visible = true; <<===================== None
            form.TopMost = true; <<===================== of
            form.BringToFront(); <<===================== these working!
            form.WindowState = FormWindowState.Maximized;
            return;
        }
        Application.Run(form);
    }        private static Mutex mutex = null;
}

Ответы [ 2 ]

0 голосов
/ 27 мая 2018

Я предлагаю вам другой метод, использующий комбинацию класса System.Threading.Mutex и UIAutomation AutomationElement класса.

A Mutex может быть, как вы уже знаете, простой строкой.Вы можете назначить приложению Mutex в форме GUID, но это может быть что угодно еще.
Предположим, что это текущее приложение Mutex:

string ApplicationMutex = "BcFFcd23-3456-6543-Fc44abcd1234";
//Or
string ApplicationMutex = "Global\BcFFcd23-3456-6543-Fc44abcd1234";

Примечание :
Используйте префикс "Global\", чтобы определить область действия Mutex.Если префикс не указан, вместо него используется префикс "Local\".Это предотвратит один экземпляр процесса, когда активны несколько рабочих столов или на сервере запущены службы терминалов.

Если мы хотим проверить, зарегистрировал ли другой работающий Процесс тот же самый Mutex, мы попытаемся зарегистрировать наш Mutex, и в случае сбоя другой экземпляр нашего Приложения уже запущен.
МыСообщите пользователю, что приложение поддерживает только один экземпляр, затем переключитесь на запущенный процесс, показывая его интерфейс и, наконец, выйдите из дубликата приложения, удалив Mutex.

Способ активации предыдущего экземпляра Приложения может различаться в зависимости от типа Приложения, но меняются только некоторые детали.
Мы можем использовать Process..GetProcesses () чтобы получить список запущенных процессов и проверить, имеет ли один из них те же данные, что и у нас.

Здесь у вас есть оконное приложение (оно имеет пользовательский интерфейс), поэтому уже можно отфильтровать список, исключая те процессы, у которых нет MainWindowHandle .

Process[] windowedProcesses = 
    Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero).ToArray();

Чтобы определить правильный, мы могли бы проверить, совпадает ли Process.ProcessName .
Но это имя связано с исполняемым файломназвание.Если имя файла изменяется (кто-то меняет его по какой-то причине), мы никогда не будем идентифицировать Процесс таким образом.

Один из возможных способов определения правильного процесса - проверить Process.MainModule.FileVersionInfo.ProductName и проверить, совпадает ли он.

При обнаружении можно вывести оригинальное приложение на передний план с помощью UIAutomation AutomationElement, созданного с использованием MainWindowHandle идентифицированного процесса.
AutomationElement может автоматизировать различные Patterns (вид элементов управления, обеспечивающих функциональные возможности автоматизации для элементов пользовательского интерфейса).
A WindowPattern позволяет управлять элементом управления на основе окна (Платформа не имеет значения, может быть формой WinForms илиокно WPF).

AutomationElement element = AutomationElement.FromHandle(process.MainWindowHandle);
WindowPattern wPattern = element.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
wPattern.SetWindowVisualState(WindowVisualState.Normal);

Чтобы использовать функции UIAutomation, вы должны добавить эти ссылки в свой проект:
- UIAutomationClient
- UIAutomationTypes

ОБНОВЛЕНИЕ:
Поскольку форма приложения может быть скрыта, Process.GetProcesses() не найдет свой дескриптор окна, поэтому AutomationElement.FromHandle() использовать нельзяопределить окно Form.

Возможный обходной путь, без отклонения «шаблона» UIAutomation, заключается в регистрации события автоматизации с использованием Automation.AddAutomationEventHandler , который позволяет получать уведомление, когда происходят события автоматизации пользовательского интерфейса, напримеркак только должно появиться новое окно (программа запущена).

Событие регистрируется только в том случае, если приложение должно работать как единая копия.При возникновении события новое имя процесса AutomationElement (текст заголовка Windows) сравнивается с текущим, и, если оно совпадает, скрытая форма не будет скрыта и отобразится в нормальном состоянии.
КакБезаварийная мера, представляем информацию MessageBox.Заголовок MessageBox имеет тот же заголовок, что и приложение MainForm.
( Протестировано с формой, для которой WindowsState установлено на Minimized и его свойство Visible установлено на false).


После того, как оригинальный процесс был выведен на передний план, нам просто нужно закрыть текущий поток и освободить созданные нами ресурсы (в основном, Mutex, в данном случае).

using System;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Windows.Automation;
using System.Windows.Forms;

static class Program
{
    static Mutex mutex = null;

    [STAThread]
    static void Main()
    {
        Application.ThreadExit += ThreadOnExit;
        string applicationMutex = @"Global\BcFFcd23-3456-6543-Fc44abcd1234";
        mutex = new Mutex(true, applicationMutex);
        bool singleInstance = mutex.WaitOne(0, false);
        if (!singleInstance)
        {
            string appProductName = Process.GetCurrentProcess().MainModule.FileVersionInfo.ProductName;
            Process[] windowedProcesses = 
                Process.GetProcesses().Where(p => p.MainWindowHandle != IntPtr.Zero).ToArray();

            foreach (Process process in windowedProcesses.Where(p => p.MainModule.FileVersionInfo.ProductName == appProductName))
            {
                if (process.Id != Process.GetCurrentProcess().Id)
                {
                    AutomationElement wElement = AutomationElement.FromHandle(process.MainWindowHandle);
                    if (wElement.Current.IsOffscreen)
                    {
                        WindowPattern wPattern = wElement.GetCurrentPattern(WindowPattern.Pattern) as WindowPattern;
                        #if DEBUG
                        WindowInteractionState state = wPattern.Current.WindowInteractionState;
                        Debug.Assert(!(state == WindowInteractionState.NotResponding), "The application is not responding");
                        Debug.Assert(!(state == WindowInteractionState.BlockedByModalWindow), "Main Window blocked by a Modal Window");
                        #endif
                        wPattern.SetWindowVisualState(WindowVisualState.Normal);
                        break;
                    }
                }
            }
            Thread.Sleep(200);
            MessageBox.Show("Application already running", "MyApplicationName",
                            MessageBoxButtons.OK, MessageBoxIcon.Information, 
                            MessageBoxDefaultButton.Button1, MessageBoxOptions.ServiceNotification);
        }

        if (SingleInstance) {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MyAppMainForm());
        }
        else {
            Application.ExitThread();
        }
    }
    private static void ThreadOnExit(object s, EventArgs e)
    {
        mutex.Close();
        mutex.Dispose();
        Application.ThreadExit -= ThreadOnExit;
        Application.Exit();
    }
}

В приложении MainForm конструктор:
(используется, если главное окно приложения скрыто при запуске нового экземпляра, поэтому процедура в Program.cs не может найти свой дескриптор)

public partial class MyAppMainForm : Form
{
    public MyAppMainForm()
    {
        InitializeComponent();
        Automation.AddAutomationEventHandler(WindowPattern.WindowOpenedEvent, 
                                             AutomationElement.RootElement, 
                                             TreeScope.Subtree, (uiElm, evt) =>
        {
            AutomationElement element = uiElm as AutomationElement;
            string windowText = element.Current.Name;
            if (element.Current.ProcessId != Process.GetCurrentProcess().Id && windowText == this.Text)
            {
                this.BeginInvoke(new MethodInvoker(() =>
                {
                    this.WindowState = FormWindowState.Normal;
                    this.Show();
                }));
            }
        });
    }    
}
0 голосов
/ 27 мая 2018

Запуск только один раз:

static class Program
{    
    [STAThread]
    static void Main()
    {
        bool createdNew = true;
        using (Mutex mutex = new Mutex(true, "samplename", out createdNew))
        {
            if (createdNew)
            {
                Application.EnableVisualStyles();
                Application.SetCompatibleTextRenderingDefault(false);
                Application.ThreadException += new ThreadExceptionEventHandler(Application_ThreadException);
                AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
                Application.Run(new Form1());
            }
            else
            {
                ProcessUtils.SetFocusToPreviousInstance("samplename");
            }
        }
    }

    private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
    {
    }

    private static void Application_ThreadException(object sender, ThreadExceptionEventArgs e)
    {
    }
}

ProcessUtils:

   public static class ProcessUtils
    {
        [DllImport("user32.dll", SetLastError = true)]
        static extern IntPtr FindWindow(string lpClassName, string lpWindowName);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetForegroundWindow(IntPtr hWnd);

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

        [DllImport("user32.dll")]
        static extern bool ShowWindow(IntPtr hWnd, int nCmdShow);

        const int SW_RESTORE = 9;

        [DllImport("user32.dll")]
        static extern IntPtr GetLastActivePopup(IntPtr hWnd);

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


        public static void SetFocusToPreviousInstance(string windowCaption)
        {

            IntPtr hWnd = FindWindow(null, windowCaption);


            if (hWnd != null)
            {

                IntPtr hPopupWnd = GetLastActivePopup(hWnd);



                if (hPopupWnd != null && IsWindowEnabled(hPopupWnd))
                {
                    hWnd = hPopupWnd;
                }

                SetForegroundWindow(hWnd);


                if (IsIconic(hWnd))
                {
                    ShowWindow(hWnd, SW_RESTORE);
                }
            }
        }
    }

Нормальный запуск:

static class Program
{
    [STAThread]
    static void Main()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(false);
        Application.Run(new Form1());
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...