Модераторы: gambit, Partizan
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Один экземпляр приложения, с передачей аргументов, решение: базовый класс главной формы 
:(
    Опции темы
farad
Дата 17.1.2008, 15:23 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Бывалый
*


Профиль
Группа: Участник
Сообщений: 199
Регистрация: 1.11.2007

Репутация: 3
Всего: 6



Поскольку вопрос, «как сделать, чтобы нельзя было запускать более одного экземпляра приложения» возникает с завидной регулярностью, то хочу предложить свое решение данного вопроса. Ваял для себя, но возможно кому-нибудь пригодится еще. Решение позволяет активировать главное окно уже запущенного приложения, при попытке запуска второго экземпляра. При этом происходит передача аргументов командной строки второго экземпляра первому. Например, если приложение поддерживает обработку командной строки, скажем открытие файла определенного типа из проводника, то если при запущенном приложении, щелкнуть по файлу подходящего типа – этот файл будет открыт запущенным экземпляром. Передача аргументов идет через реестр – решил, что это проще всего.
Короче, это базовый класс, от которого нужно наследовать главную форму приложения и перегрузить в своей форме  метод, который осуществляет обработку аргументов командной строки. Если не требуется обработка аргументов, перегружать ничего не надо. Достаточно просто наследовать главную форму от этого класса, и все.. второй экземпляр вашего приложения уже не запустится… 

Код

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using Microsoft.Win32;
using System.Runtime.InteropServices;
namespace forms
{
    public  class SingleHinstForm:Form
    {
        // сообщение которое будем отправлять существующему окну
        // для передачи аргументов
        const int WM_SENDARGS = 0x0400/*WM_USER*/ + 100;
        
        //сюда будем сохранять хендл и аргументы
        //командной строки
        static readonly string RegPath = 
            "Software\\"+ Application.CompanyName +
            "\\" +Application.ProductName+"\\SingleHinst\\";

        const string RegName = "MainWindowHandle";
        const string RegArgsFolder = "CommandLineArguments";
        
        //для контроля
        static Mutex _mutex=null;
        
        [DllImport("User32", CharSet = CharSet.None)]
        static extern long PostMessage(System.IntPtr hWnd,
            int Msg, IntPtr wParam, IntPtr lParam);
        [DllImport("User32.dll", CharSet = CharSet.None)]
        static extern bool SetForegroundWindow(System.IntPtr Handle);

        protected override void Dispose(bool disposing)
        {
            base.Dispose(disposing);
            FreeMutex();
        }
        //здесь будем ловить сообщения 
        //от других экземпляров приложения
        protected override void WndProc(ref Message m)
        {
            base.WndProc(ref m);

            //пользователь запустил еще один экземпляр и он
            //от него пришло сообщение
            if (m.Msg == WM_SENDARGS)
            {
                //разворачиваем окно и выводим на передний план
                if (WindowState == FormWindowState.Minimized)
                    WindowState = FormWindowState.Normal;
                Activate();

                //обрабатываем аргументы
                //которые были переданы новому экземпляру
                ArgsProc(GetInsideArgs());
            }
        }

        protected override void OnLoad(EventArgs e)
        {
            //если в системе уже запущен экземпляр формы,
            //то диспозимся
            if (Exist())
            {
                Dispose();
                return;
            }
            base.OnLoad(e);
        }

        //здесь в потомке нужно реализовать обработку аргументов
        //командной строки
        //если обработка не нужна - метод
        //можно исключить
        protected virtual void ArgsProc(string[] args)
        {

        }
        
        //ищем существующий экземпляр формы 
        //если находим -  шлем ему сообщение
        bool Exist()
        {
            bool test;
            _mutex = 
                new Mutex(false, Application.ProductName + ".Mutex", out test);
            if (test)
            {
                SaveHwnd();
                return false;
            }
            else
            {
                _mutex = null;
                IntPtr hwnd = GetExistingHwnd();
                
                //не нашли - можно продолжать выполнение приложения
                if (hwnd == IntPtr.Zero)
                    return false;
                
                //сохраним аргументы командной строки
                SaveArgs();

                //отправим уведомление о том что была попытка запуска
                //второго экземпляра
                PostMessage(hwnd,WM_SENDARGS,IntPtr.Zero,IntPtr.Zero);
                SetForegroundWindow(hwnd);
                return true;
            }
        }

        //сохранение хендла главного окна приложения в реестре
        void SaveHwnd()
        {
            RegistryKey rk = Registry.CurrentUser.CreateSubKey(RegPath);
            if (rk != null)
            {
                rk.SetValue(RegName,this.Handle.ToInt32());
                rk.Close();
            }
        }

        //получение хендла главного окна ранее запущенного приложения
        IntPtr GetExistingHwnd()
        {
            try
            {

                RegistryKey rk = Registry.CurrentUser.OpenSubKey(RegPath);
                if (rk != null)
                {
                    int ret = (int)rk.GetValue(RegName);
                    rk.Close();
                    return new IntPtr(ret);
                }
            }
            catch { }
            return IntPtr.Zero;
        }
        
        //сохранение аргументов командной строки для передачи
        //уже запущенному приложению.
        //метод можно исключить если не нужна обработка аргументов
        void SaveArgs()
        {
            string[] args=Environment.GetCommandLineArgs();

            RegistryKey rk,subrk;
            rk=subrk= null;

            try
            {
                rk = Registry.CurrentUser.CreateSubKey(RegPath);
                if (rk != null)
                {
                    subrk = rk.CreateSubKey(RegArgsFolder);
                    if (subrk != null)
                    {
                        for (int i = 0; i < args.Length; i++)
                            subrk.SetValue(string.Format("{0}", i), args[i]);
                        subrk.Close();
                        subrk = null;
                    }
                    rk.Close();
                    rk = null;
                }
            }
            finally
            {
                if (subrk != null) subrk.Close();
                if (rk != null) rk.Close();
            }
        }

        //получение аргументов которые были переданы новому 
        //экземпляру приложения
        //можно исключить если не нужна обработка аргументов
        protected string[] GetInsideArgs()
        {
            string[] ret = null;
            RegistryKey rk,subrk;
            rk=subrk=null;
            try
            {
                rk = Registry.CurrentUser.OpenSubKey(RegPath);
                if (rk != null)
                {
                    subrk = rk.OpenSubKey(RegArgsFolder);
                    if (subrk != null)
                    {
                        string[] names = subrk.GetValueNames();
                        if (names.Length > 0)
                        {
                            ret = new string[names.Length];
                            int j = 0;
                            for (int i = 0; i < names.Length; i++)
                            {
                                ret[j++] = (string)subrk.GetValue(names[i]);
                            }
                        }
                        subrk.Close();
                        subrk = null;
                        subrk = rk.OpenSubKey(RegArgsFolder, true);
                        for (int i = 0; i < names.Length; i++)
                        {
                            subrk.DeleteValue(names[i]);
                        }
                        subrk.Close();
                        return ret;
                    }
                }
                return null;
            }
            catch
            {
                return null;
            }
            finally
            {
                if(subrk!=null)subrk.Close();
                if(rk!=null)rk.Close();
            }
        }
        
        
        void FreeMutex()
        {
            if (_mutex == null)
                return;
            _mutex.Close();
            _mutex = null;
            
            Registry.CurrentUser.DeleteSubKey(RegPath+RegArgsFolder,false);
            Registry.CurrentUser.DeleteSubKey(RegPath,false);
        }
    }
}



Это сообщение отредактировал(а) farad - 28.1.2008, 16:02
PM MAIL   Вверх
Softaz
Дата 18.2.2008, 14:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


wasm
**


Профиль
Группа: Участник
Сообщений: 373
Регистрация: 16.1.2006

Репутация: 3
Всего: 16



А если атомы использовать? Все-таки как-то по мне криовато через реестр хранить.


--------------------
Разочарованный в .NET
PM MAIL WWW   Вверх
nettitan
Дата 6.5.2008, 22:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 45
Регистрация: 9.6.2006
Где: г. Хмельницкий

Репутация: нет
Всего: нет



Какие еще атомы?,  smile  плз...
PM MAIL WWW ICQ Skype   Вверх
jonie
Дата 6.5.2008, 22:58 (ссылка) |    (голосов:1) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 5613
Регистрация: 21.8.2005
Где: Владимир

Репутация: 5
Всего: 118



определяют стандартно через семафоры.. 

атомы это системные объекты в MSDN примерно там : windowsuserinterface/dataexchange/atoms
суть такая : вы пихаете в этот атом строку командную (заметим что атомов может понадобиться несколько, т.к. они ограничены длиной)....потом шлее сообщение окну, где указываете в одном из параметров DWORD атома. и в первом приложении его зачитываете.

но мне кажется атомы тут тоже не нужны.
1) вы можете использовать IPC (довольно имхо накладно)
2) а просто передавать используя сообщение WM_COPYDATA не проще ли?

на самом деле самая загвоздка передать HWND окна приемника (или вообще какие-то объекты)
но и это можно сделать, думаю, через вышеупомянытый семафор...

в общем как-то так будет работать вплоне даже..
Код

                Semaphore sp = new Semaphore(frm.Handle.ToInt32(), int.MaxValue, "SomeName", out isAlreadyExist);
                if (!isAlreadyExist)
                {
                    Console.WriteLine("already Exist!");
                    Console.WriteLine("HWND={0}", sp.Release());
                }



--------------------
Что-то не поняли? -> Напейтесь до зеленых человечков... эта сверхцивилизация Вам поможет...
PM MAIL Jabber   Вверх
source777
Дата 6.5.2008, 22:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


Профиль
Группа: Завсегдатай
Сообщений: 1878
Регистрация: 12.3.2007

Репутация: нет
Всего: 56



ужос. Как можно такие "решения" напоказ выставлять, такое можно только в справочнике "Как не надо делать ни в коем случае" показывать...

Итак, решения данной задачи:
1) SendMessage + WM_COPYDATA (самый быстрый способ)
2) GlobalAddAtom + GlobalFindAtom + GlobalDeleteAtom (периодический способ, на который указал Softaz, мне лично навязываемое данным способом активное ожидание не импонирует)
3) Класс Microsoft.VisualBasic.ApplicationServices.WindowsFormsApplicationBase (встроенная .NET-поддержка данного алгоритма, хороша тем, что предельно проста в использовании)

Это сообщение отредактировал(а) source777 - 6.5.2008, 23:02


--------------------
Если бы программистам платили за то, чтобы убирать код из программы вместо того, чтобы добавлять его, программы были бы намного лучше © Николас Негропонте
PM MAIL   Вверх
mr.DUDA
Дата 7.5.2008, 09:12 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


3D-маньяк
****


Профиль
Группа: Экс. модератор
Сообщений: 8244
Регистрация: 27.7.2003
Где: город-герой Минск

Репутация: 24
Всего: 232



Код
        //для контроля
        static Mutex _mutex=null;

Собственно, вот на этом мьютексе всё и можно было сделать, остальной код и не нужен.  smile 


--------------------
user posted image
PM MAIL WWW   Вверх
nettitan
Дата 8.5.2008, 08:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 45
Регистрация: 9.6.2006
Где: г. Хмельницкий

Репутация: нет
Всего: нет



СПС за инфу, буду пробывать все возможные варианты, дабы найти оптимальный  smile 
PM MAIL WWW ICQ Skype   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Прежде чем создать тему, посмотрите сюда:
mr.DUDA
THandle

Используйте теги [code=csharp][/code] для подсветки кода. Используйтe чекбокс "транслит" если у Вас нет русских шрифтов.
Что делать если Вам помогли, но отблагодарить помощника плюсом в репутацию Вы не можете(не хватает сообщений)? Пишите сюда, или отправляйте репорт. Поставим :)
Так же не забывайте отмечать свой вопрос решенным, если он таковым является :)


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, mr.DUDA, THandle.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Разработка Windows Forms | Следующая тема »


 




[ Время генерации скрипта: 0.0769 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.