Отправляет email-рассылки с помощью сервиса Sendsay

Borland C++ Builder - всякая всячина

  Все выпуски  

Borland C++ Builder всякая всячина (Предупреждение повторного запуска программы)


Служба Рассылок Subscribe.Ru проекта Citycat.Ru

Приветствую всех получателей рассылки Borland C++ Builder - всякая всячина!

Предупреждение повторного запуска программы

Во-первых, сразу хочу извиниться за нерегулярность выпусков - 3-4 выпуска в неделю пока, к сожалению, не получаются... Поэтому, я или буду сокращать количество выпусков или делать их более короткими, разбивая на несколько частей крупные темы. Либо буду чередовать полноценные статьи с короткими вопросиками (может быть, по два-три таких вопросика на выпуск).
Еще очень хотелось бы узнать, что интересует народ в первую очередь, поскольку пока я пишу статьи на основе решения собственных проблем, причем есть полная гарантия того, что все приведенные примеры - рабочие и успешно применяются в реальных программах. В дальнейшем, после запуска сайта, я выложу все эти примеры отдельным архивом.

Поэтому, большая просьба к подписчикам: не стесняйтесь писать мне на p38008@homeline.ru о каких-то проблемах, я постараюсь помочь автору письма в случае удачного решения, а затем, если вопрос будет достаточно интересен, сделаю выпуск по данному вопросу...

"А теперь - дискотека..."

Рассмотрим такую ситуацию: вы запустили свою программу, например, Project1.exe, открылось окно, пошла работа, чтение, обработка, запись информации, и тут вы запускаете на выполнение еще один экземпляр того же файла... Windows-то что, она же запустит... Дальше будет что? Скорее всего ничего не будет: откроется окно, пойдет работа... Но! бывают ситуации, когда повторный запуск модет привести к непредсказуемым последствиям, например, программа блокирует какой-либо файл или девайс, забирает в эксклюзивное использование какую-нибудь таблицу, использует один и тот же порт TCP/IP, что и первая копия и т.д.

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

Поскольку, в нашем случае, второй экземпляр не должен появляться на рабочем столе вообще, сделаем проверку на повторный запуск в главной функции программы - WinMain. Я так понимаю, что вы уже открыли Билдер и создали новый проект? Тогда в меню View->Units... выберите просмотр основного файла проекта Project1.cpp. В нем, помимо всякой шелухи, вы найдете главную функцию программы WinMain. В "чистом" проекте она выглядит так:

//---------------------------------------------------------------------------
 
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    try
    {
      Application->Initialize();
      Application->CreateForm(__classid(TForm1), &Form1);
      Application->Run();
    }
    catch (Exception &exception)
    {
      Application->ShowException(&exception);
    }
    return 0;
}
//---------------------------------------------------------------------------

Общая идея проверки на "вторую копию" была блестяще сформулирована Юрием Зотовым на сайте http://delphi.mastak.com/, за что я ему очень благодарен, поскольку переделал свои программы с использованием этого метода. Идея звучит так:

  • Создаем (есть несколько способов) некий системный объект, который имеет уникальное имя и "виден" из любой программы. Я обычно использую Mutex - объект для синхронизации потоков, что это такое - не суть важно, главное, что он подходит под вышеназванные критерии.

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

  • Если же объект был успешно создан, мы продолжаем выполнение программы и по ее завершении удаляем созданный объект.

Ценность данного метода в том, что он "не позволит" запустить вторую копию ни при каких условиях, даже если очень напрячь мышку или клавишу "Enter", поскольку создание объекта и проверка его существования занимает милисекунды машинного времени.

Терепь, перейдем к конкретике. Доработайте функцию WinMain следующим образом:

//---------------------------------------------------------------------------
 
WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
{
    HANDLE hMutex= CreateMutex(0 ,true, "TestMutex");
    if (GetLastError()!=ERROR_ALREADY_EXISTS)
    {    // Нет второго экземпляра
      try
      {
        Application->Initialize();
        Application->CreateForm(__classid(TForm1), &Form1);
        Application->Run();
      }
      catch (Exception &exception)
      {
        CloseHandle(hMutex);
        Application->ShowException(&exception);
      }
      CloseHandle(hMutex);
    }
    else
    {    // Второй экземпляр
      CloseHandle(hMutex);
      HWND OldHandle= NULL;
      OldHandle= FindWindowEx(NULL, NULL, "TApplication", "TestProject");
      if (OldHandle!=NULL)
      {
        if (IsIconic(OldHandle)) ShowWindow(OldHandle, SW_RESTORE);
        else SetForegroundWindow(OldHandle);
      }
    }
    return 0;
}
//---------------------------------------------------------------------------

Итак, первой строкой мы создаем вышеупомянутый объект Mutex (как я уже сказал, его назначение в данном случае нас не интересует) с именем "TestMutex", которое вы можете выбрать совершенно произвольно. И тут же выполняем проверку правильности создания этого объекта: если объект был еспешно создан, то выполняем код программы, сгенерированный автоматически, за исключением того, что в исключительных ситуациях и при завершении работы нам придется освободить память, занимаемую этим объектом.

Если же при создании объекта возникла ошибка ERROR_ALREADY_EXISTS, то выполняем код активации первой копии программы и завершаем работу главной функции (я бы даже сказал "не продолжаем" работу приложения, поскольку мы просто не инициируем его запуск).

Вот здесь мы остановимся чуть подробнее, поскольку активация приложения имеет свои ньюансы. Во первых, нам надо получить хэндл этого приложения. Для этого нужно найти первую копию в списке запущенных задач с помощью функции FindWindowEx. В качестве первого параметра она получает хэндл родительского окна, если мы ищем дочернее окно какого-нибудь приложения или NULL, если мы "перебираем" приложения на рабочем столе. Второй параметр - это хэндл окна текущего уровня, с которого мы должны начать поиск (NULL - поиск с начала списка). Третий параметр это имя класса главного окна приложения (для задая, зарегистрированных на панели задач и сохданных в среде C++ Builder или Delphi он всегда равен "TApplication". Четвертый параметр - это заголовок приложения, который однозначно должен идентифицировать нашу программу, поэтому в меню Project->Options->Application->Title очень желательно всегда писать внятное имя создаваемого приложения (в нашем случае это - "TestProject").

В том случае, если эта функция вернула хэндл первой копии приложения, мы должны отработать два варианта: 1. Окно свернуто в значек на панели задач. 2. Окно отображается на экране. Если функция IsIconic возвращает true (1-й вариант), то мы "возвращаем" окно на экран вызовом функции ShowWindow. Если же окно уже отображено на экране, то мы просто передвигаем его в z-буффере рабочего стола на передний план вызовом функции SetForegroundWindow.

Вот так. Хочется еще добавить, что при работе мной был использован пример того же Юрия Зотова на сайте "Королевство Delphi". Буду ждать ваших вопросов и предложений. Да! Если кого-то не устраивают цвета или оформление рассылки (машины у всех разные) - тоже пишите - что-нибудь придумаем. На текстовый вариант рассылки я пока особого внимания не обращаю, поскольку, судя по статистике, подписавшихся на текстовый вариант пока не наблюдается.



С уважением, Васильев Евгений...

http://subscribe.ru/
E-mail: ask@subscribe.ru
Отписаться
Убрать рекламу
Рейтингуется SpyLog

В избранное