Главная » C++, C++ Builder » Приложение OnlyOnce CBuilder

0

На рис. 9.1 представлена форма, которую мы будем использовать для приложения OnlyOnce (ТолькоРаз). Как видите, ничего особенного на форме нет; на ней находится единственная метка статического текста, говорящая пользователю не запускать вторую копию. Хорошо запомните эту форму, так как больше мы на нее смотреть не будем.

Когда вы создадите новое приложение в CBuilder и форму, как на  рис. 9.1,  закройте  форму. Работа, которую нам нужно сделать, производится не над формой, а над исходным кодом приложения.

Выберите команду View|Project Source из главного меню интегрированной среды CBuilder. В редактор будет загружен исходный файл проекта (project1.cpp). Посмотрите на этот код, приведенный в листинге после рис. 9.1.

Рис. 9.1. Форма приложения OnlyOnce

//———————————————————

#include <vcl\vcl.h>

#pragma hdrstop

//———————————————————

USEFORM("Unit1.cpp",  Form1); USERES("Project1.res");

//———————————————————

WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int)

{

try

{

Application->Initialize();

Application->CreateForm(__classid(TForm1),  &Form1); Application->Run();

}

Поиск нужной функции API catch (Exception &exception)

{

Application->ShowException(&exception);

}

return 0;

}

Не так уж и много можно сказать про эту процедуру. Функция WinMain — это точка входа для всех Windows-приложений в любой версии Windows. Она будет вызвана при старте программы. На самом деле есть еще часть глобального кода, которая запускается перед этой функцией (код, запускающий приложение). Код, запускающий приложение, отвечает за создание объекта Application. Все дальнейшее происходит здесь.

Процесс запуска приложения в CBuilder состоит из трех шагов. Во-первых,  инициализируется объект Application. Это важно только в том случае, если ваша программа — сервер OLE Automation. Если ваша программа — не сервер, то можете удалить эту строчку из кода приложения.

Как только объект инициализирован, создаются все формы, предназначенные для автоматического создания. Такие формы перечислены в окне Project Manager (менеджер проектов) в системе CBuilder. Обычно, если вы не укажете обратное, то все формы, которые вы добавляете в проект, помечаются системой как создаваемые автоматически (auto-create).

Зачем вам может понадобиться отключить автоматическое создание формы? Если вы пишете MDI-приложение, то вам не всегда нужно, чтобы дочернее окно MDI создавалось при запуске приложения. Или, может быть, вы не захотите, чтобы форма присутствовала в приложении, если это редко используемое окно диалога, или же у вас есть набор форм, которые по смыслу исключают друг друга во время работы приложения.

Последний шаг процесса —  запустить приложение. Это делается вызовом метода Run объекта Application в стартовом коде. Этот вызов не вернется обратно, пока главная форма приложения не будет закрыта или не будет вызвана функция Application->Terminate().

Запрет запуска второй копии

Для  того  чтобы  не  запускать  вторую  копию  приложения,  нам  нужно  не  давать  приложению создавать формы и не давать запускать метод Run. Для этого мы сначала должны определить при

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

В Windows API нет прямого метода поиска программы. В старые времена в Windows 3.1 существовал параметр, передаваемый нашей функции WinMain, называющийся hPrevInstance (ссылка на предыдущую копию приложения), который можно было проверить. В программах под Windows 95/NT этот параметр всегда будет равен NULL. Однако можно найти окно с таким же заголовком, как и программа, которую вы пытаетесь запустить. Это делается при помощи функции FindWindowEx.

Функция API FindWIndowEx имеет четыре параметра: имя класса окна, заголовок окна, а также две ссылки, идентифицирующие дочернее и родительское окно.

Один или оба параметра «имя класса» и «заголовок» могут быть равны NULL, а также один или оба параметра, содержащих ссылки на окна, могут быть равны  NULL.  Если  ссылка  на родительское окно равна NULL, то поиск будет идти по всем окнам верхнего уровня (главным окнам).

Что мы хотим, так это все окна верхнего уровня, которые имеют такое же имя, что и окно нашей программы. Добавьте следующий код в функцию WinMain перед выражением try {, которое запускает весь код объекта Application:

HWND hWnd;

hWnd = FindWindowEx(

NULL, // ссылка на родительское окно NULL, // ссылка на дочернее окно NULL, // указатель на имя класса "OnlyOne" // Имя окна

);

if (hWnd != NULL)

{

BringWindowToTop(hWnd); return 0;

}

Этот код будет искать окно с текстом «OnlyOne» среди окон верхнего уровня. Если функция FindWindowEx найдет такое окно, то она вернет ссылку (handle) на это окно (примерно то же самое, что и свойство FormHandle в CBuilder). В таком случае мы используем вторую функцию API, BringWindowToTop, для выноса этого окна на передний план. BringWindowToTop имеет один параметр — ссылку на окно, которое нужно переместить.

После перемещения старого приложения на передний план нам нужно предотвратить запуск нового. Программа заканчивает работу, когда происходит выход из функции WinMain, так что проще всего выполнить оператор return (возврат из функции) в этот момент. Значение, которое вы возвращаете, традиционно является значением, возвращаемым программой, но используется оно редко, так что мы просто возвращаем 0.

Замечание

Не пытайтесь запускать это приложение из интегрированной среды CBuilder. Закройте CBuilder и затем запустите программу из проводника Windows. IDE системы CBuilder, похоже, держит скрытое окно с заголовком  нашей формы. Когда функция FindWindowEx найдет это окно, она скажет вам об этом, и ваше приложение никогда не запустится.

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

Перетаскивание  файлов

Когда программисты разговаривают о Windows 95 или Windows NT, они часто говорят о перетаскивании (drag-and-drop), и выше в книге мы рассматривали

пример перетаскивания данных между двумя списками в форме. Однако под drag-and-drop понимается также и нечто другое. Программа Windows Explorer (проводник) позволяет, как  и прочие, перетаскивать файлы в другие программы. Иногда, например, при перетаскивании файла на иконку программы эта программа запускается с параметром — именем этого файла. В других случаях это действие позволяет вам обрабатывать файлы, перетаскивая их из проводника в работающую программу. В этом примере мы как раз и рассмотрим данный случай.

Обработка drag-and-drop связана с тремя моментами. Во-первых, нужно объяснить системе Windows, что на вашу программу можно перетаскивать файлы. Во-вторых, нужно обрабатывать сообщения, которые Windows посылает вам, когда файл «роняется» на окно вашего приложения. И наконец, нужно что-то делать с этими перетащенными файлами.

Система CBuilder поддерживает некоторые формы перетаскивания, но только внутри системы. Если вы хотите обработать перетаскивание в системе CBuilder, то вам нужно слегка порыться в Windows API (именно поэтому, конечно, это и находится в данной главе). Давайте посмотрим на каждый кусочек отдельно, чтобы понять, что происходит.

Информирование Windows о том, что ваша программа принимает перетаски ваемые файлы, — довольно простая задача. Все, что нужно, — один вызов API, функция DragAcceptFiles. Эта функция API принимает два аргумента: ссылку на окно и логический флаг. Ссылка — это ссылка на окно, на которое вы хотите разрешить «ронять» файлы. Логическое значение же указывает, будет ли это окно (true) или нет (false) воспринимать перетащенные файлы. Очевидно, что переключая это значение, вы можете разрешать или запрещать перетаскивание во время работы программы. Я лично ни разу не встречал ситуации, в которой надо было бы отключить разрешение на перетаскивание, но, как мне кажется, такая ситуация возможна.

Когда вы сказали системе, что вы воспринимаете  перетаскиваемые файлы, следующий  шаг  — ждать, пока не придет сообщение о том, что вам «притащили» файл. Оно приходит в виде сообщения WM_DROPFILES, посылаемого вашей оконной функции. В форме CBuilder вы можете добавить функцию-обработчик сообщения. Сообщение WM_DROPFILES говорит вам о том, что система «роняет» один или более файлов на ваше окно, но оно не говорит вам, что это за файлы. Ваша работа как программиста — запрашивать информацию о каждом файле и соответственно его обрабатывать.

Третий шаг, обработка файла, зависит от приложения. Может быть, вам нужно открыть файл и прочитать из него данные. Может, удалить его или скопировать его куда-то еще. Что бы вам ни было нужно, у вас будет имя файла, с которым можно работать. Ваши действия с ним — ваш выбор.

На рис. 9.2 приведена форма, которую мы собираемся использовать в примере приложения DragDropFiles. На этой форме находиться одно поле  статического  текста  («Файлы, перетащенные сюда:»), кнопка, закрывающая форму, и окно списка для отображения файлов, которые будут перетащены на форму. В этом примере мы ничего не делаем с данными, только

отображаем для пользователя имена файлов.

Рис. 9.2. Форма приложения DragDropFiles

Первым делом, как мы уже говорили, надо сказать системе, что окно этой формы будет принимать файлы, перетащенные на него. Создайте обработчик события OnCreate для формы и добавьте в метод FormCreate следующий код:

void __fastcall TForm1::FormCreate(TObject *Sender)

{

// Позволить нашей форме принимать файлы

DragAcceptFiles (Handle, TRUE);

}

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

Обработка притащенных файлов

Когда мы сказали Windows о том, что будем обрабатывать перетащенные файлы, можно расслабиться и ждать, пока их к нам не притащат. Для этого события мы ждем сообщения WM_DROPFILES. В классе TForm нет прямого обработчика для этого сообщения, так что нам нужно добавить элемент в карту сообщений для формы, как мы делали в предыдущих примерах. Добавьте следующие строки в заголовочный файл для класса формы. Эти строки определят новый обработчик стандартного сообщения Windows и затем свяжут  его  с  сообщением WM_DROPFILES:

class TForm1 : public TForm

{

__published: // IDE-managed components TListBox *ListBox1;

TLabel *Label1; TButton *Button1;

void __fastcall Button1Click(TObject *Sender); void __fastcall FormCreate(TObject *Sender); private: // User declarations

void __fastcall HandleDropFiles(TMessage& Msg); public: // User declarations

__fastcall TForm1(TComponent *Owner); BEGIN_MESSAGE_MAP

MESSAGE_HANDLER(WM_DROPFILES,  TMessage,  HandleDropFiles);

END_MESSAGE_MAP(TForm)

};

После того как мы описали процедуру, которая должна делать всю работу, осталось только написать саму эту процедуру. Давайте сначала взглянем на код, а затем выясним, как он работает: void __fastcall TForm1::HandleDropFiles(TMessage& Msg)

{

// Получить информацию о перетащенном из сообщения

HDROP hDropInfo = (HANDLE)Msg.WParam; char  szFileName[_MAX_PATH];

int iFiles = DragQueryFile(hDropInfo, (DWORD)-1, (LPSTR)NULL, 0);

// Для каждого притащенного файла поместить его

// в массив Items списка

for (int nIdx = 0; nIdx < iFiles; ++nIdx)

{

DragQueryFiles (hDropInfo, nIdx, szFileName, _MAX_PATH); ListBox1->Items->Add (szFileName) ;

}

}

Сначала мы получаем объект типа HDROP, преобразуя  элемент  сообщения  WParam  к  этому типу. HDROP на самом деле просто индекс списка перетащенных файлов в системе. Используйте ссылку HDROP для получения отдельных файлов в блоке перетащенных.

Функция API DragQueryFile имеет два разных метода ее использования (как, вы не любите такие функции?). Если вы вызовете ее с индексом _1 и указателем, равным NULL, то она вернет вам количество элементов в списке перетащенного. Это и есть количество файлов, которые пользователь выделил в Windows Explorer и перенес на ваше приложение. Если вы не хотите разрешать перетаскивать сразу много файлов за раз, то можете добавить сюда код, который останавливается, если количество файлов больше  1. Заметьте,  что вы никогда не получите это сообщение, если список пуст.

Когда у нас есть несколько файлов в списке, вступает в игру второй вариант использования функции DragQueryFile. Если вы передадите нормальный индекс (второй аргумент функции) и буфер для хранения полученного  имени файла, то  функция передаст вам имя каждого  файла в списке. Как вы помните, Explorer работает с длинными именами файлов, так что нам нужно иметь буфер,   достаточный   для   размещения   имени  файла,   перетащенного   на   наше  окно.   Макрос

_MAX_PATH, определенный в системных заголовочных файлах, определяет размер, гарантиро ванно достаточный для хранения любого имени файла (до 256 символов), которое используется в системе Windows. Однако в качестве проверки мы передаем эту длину в функцию API, чтобы, в случае несовместимости в будущем (если имена файлов станут длиннее), программа не рушилась.

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

void __fastcall TForm1::Button1Click(TObject *Sender)

{

Application->Terminate();

}

Рис. 9.3. Форма приложения DragDropFiles в действии

Как видите, обработка перетаскивания файлов из Windows Explorer очень проста. Чтобы увидеть, как это работает, запустите программу и вызовите Windows

Explorer. Выберите несколько файлов в вашем каталоге и перетащите  их  (удерживая  нажатой левую кнопку мыши на выбранном файле и перемещая мышь за пределы окна) на работающее приложение. Отпустите левую кнопку мыши и посмотрите, какие имена файлов появятся в окне списка.

На рис. 9.3 приведено окно приложения с несколькими файлами, перетащен ными из окна Windows Explorer. Как видите, в списке появляются полные имена файлов (с путем), то есть приложение точно знает, что за файлы были выбраны пользователем и перетащены на форму.

Источник: Теллес М. – Borland C++ Builder. Библиотека программиста – 1998

По теме:

  • Комментарии