none
Handle окна RRS feed

  • Вопрос

  • Всем доброго времени суток.

    Есть приложение, в котором создан класс. В классе есть форма, иконка в трее и контекстное меню для иконки. Есть необходимость при запуске ещё одной копии приложения открывать уже запущенную, а не запускать новую.

    В качестве решения выбрал апи ShowWindow.

    Частично все работает хорошо, но возникает следующая проблема:

    Если запустить приложение и не открывать  меню, то все нормально - при запуске ехешника открывается форма, а не запускается новый процесс (определяем запущен ли такой процесс, если запущен, то ищем Handle окна процесса, потом передаём его в ShowWindow). Но если открыть меню через иконку в трее, то при запуске ехешника теперь показывается меню, а не форма. Т.е. получаем, что мы находим Handleменю, вместо Handle окна.

    Подскажите как можно это исправить и находить только Handle окна.

    Заранее спасибо!

    • Перемещено Tagore Bandlamudi 1 октября 2010 г. 21:58 MSDN Forums consolidation (От:Visual C#)

Ответы

  • Замени mutex на именованный EventWaitHandle. В одном из конструкторов есть out bool createdNew.

    Если получил true - то это первый экземпляр. Создавай отдельный поток на ожидание этого эвента, c this.Show() после завершения ожидания.

    Если получил false - то сигналь евент и выходи.

    Тогда весь замут с поиском окон и ShowWindow станет не нужен.

    • Помечено в качестве ответа I.Vorontsov 17 мая 2010 г. 5:07

Все ответы

  • Вот здесь неплохой пример.
     
    http://stackoverflow.com/questions/1207105/restrict-multiple-instances-of-an-application
     
    • Предложено в качестве ответа I.Vorontsov 13 мая 2010 г. 5:19
    Модератор
  • Также можно попробовать всё сделать на API. Примерно так:

     

    static void Main( )
     {
     Application.EnableVisualStyles( );
     Application.SetCompatibleTextRenderingDefault(false);
     IntPtr hWnd = FindWindow(null, "Form1");
     if ((Int32)hWnd > 27)
     {
     if (MessageBox.Show("Приложение уже запущено.\r\nЗапустить ещё одну копию?", "CheckWindow", MessageBoxButtons.YesNo, MessageBoxIcon.Information) == DialogResult.No)
     {
      SetForegroundWindow(hWnd);
      return;
     }
     }
     Application.Run(new Form1( ));
     }
     [DllImport("user32.dll", CharSet = CharSet.Auto)]
     private static extern IntPtr FindWindow(String ClassName, String WindowName);
    
     [return: MarshalAs(UnmanagedType.Bool)]
     [DllImport("user32.dll", CharSet = CharSet.Ansi, SetLastError = true, ExactSpelling = true)]
     private static extern Boolean SetForegroundWindow(IntPtr hWnd);

     

    Но в таком случае есть вероятность того, что, если в системе существует окно с таким же заголовком, как и у вас, то, возможно, FindWindow найдет именно его. Поэтому здесь лучше перестраховаться и ещё найти какой-нибудь контрол на форме (кнопку, чек-бокс) .

     

     

     

     

     

  • 13.05.2010 5:45, SkiF-1 пишет:
    [skip]
    > IntPtr hWnd = FindWindow(null,"Form1");
    > if ((Int32)hWnd> 27)
    [skip]
    Откуда волшебная цифра 27?

    И чем мьютексы не устраивают?
    Модератор
  • Из предложенных вариантов к сожалению ничего не помогло.

    Через мьютекс стоит проверять запущен ли наш процесс или нет, эту проверку я использую. Но если окно спрятано, то вариант

    IntPtr hWnd = runningProcess.MainWindowHandle
    
    
    

    для определения Handle не работает - он просто равен 0.

    Если использовать АПИ FindWindow, то проблема остаётся той же: пока не открыть меню иконки все отлично, как только меню было открыто окно по имени уже находится не хочет. Не знаю почему, может я что-то не так делаю.

    Нашёл вариант решения проблемы, но не знаю насколько он стоющий. Вариант следующий: создать в свойствах проекта параметр, в котором сохранять Handle окна. Работает так:

    • запускаем процесс
    • проверяем используя мьютекс не запущен ли у нас уже один такой
    • если запущен то считываем Handle из параметров: hWnd = (IntPtr)Properties.Settings.Default.WindowHandle и вызываем ShowWindow
    • если не запущен, то создаём форму, записываем в параметр её Handle и сохраняем его, запускаем приложение
  • Не догоняю. Не хочет искать окно - ищи процесс. Ему, вроде точно прятаться не получится.

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool SetForegroundWindow(IntPtr hWnd);
    
        /// <summary>
        /// The main entry point for the application.
        /// </summary>
        [STAThread]
        static void Main()
        {
          bool createdNew = true;
          using (Mutex mutex = new Mutex(true, "WindowsFormApplication3", out createdNew))
          {
            if (createdNew)
            {
              Application.EnableVisualStyles();
              Application.SetCompatibleTextRenderingDefault(false);
              Application.Run(new Form1());
            }
            else
            {
              Process current = Process.GetCurrentProcess();
              foreach (Process process in Process.GetProcessesByName(current.ProcessName))
              {
                if (process.Id != current.Id)
                {
                  MessageBox.Show("process.MainWindowHandle, это то что нам нужно?");
                  //SetForegroundWindow(process.MainWindowHandle);
                  // Ну или развернуть его если свернуто
                  // ну или что там про трей и меню...
                  break;
                }
              }
            }
          }
        }

    Модератор
  • Процесс и не прячется, процесс находится - тут проблем нет. Но если главное окно скрыто
    Form1.Hide();
    то для процесса
    process.MainWindowHandle будет равен 0
    

  • Замени mutex на именованный EventWaitHandle. В одном из конструкторов есть out bool createdNew.

    Если получил true - то это первый экземпляр. Создавай отдельный поток на ожидание этого эвента, c this.Show() после завершения ожидания.

    Если получил false - то сигналь евент и выходи.

    Тогда весь замут с поиском окон и ShowWindow станет не нужен.

    • Помечено в качестве ответа I.Vorontsov 17 мая 2010 г. 5:07
  • Могу дать такой совет: наройте книгу "Pro WPF in C# 2008: Windows Presentation Foundation with .NET 3.5, Second Edition". Хинт к предыдущему шагу - Гугл наше фсе! ;) Так вот в этой книге описывается встроенное в платформу решение по запуску одного экземпляра. Ищите в CHAPTER 3 этой книги статью Single-Instance Applications. Правда, предупреждаю, решение было реализовано для VB.NET. Но в книге разбирается как его "прикрутить" к связке C#/WPF. Для связки же C#/WinForms задача еще проще, просто пропустите некоторые шаги описанные в книге и дело с концом. Пробуйте!
  • Могу дать такой совет: отвечайте на сообщение топикастера, а не на последнее :)

    Неужели так сложно было дать линки на http://msdn.microsoft.com/en-us/library/8fz4ssw2.aspx и http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.applicationservices.windowsformsapplicationbase.startupnextinstance.aspx вместо посылания в книжку? :)