В случае аварийного завершения программы система сама закроет все открытые ею дескрипторы, поэтому даже если первая копия будет снята системой и не сможет корректно закрыть дескриптор почтового ящика, ящик будет уничтожен и не помешает пользователю запустить новую копию программы.
Почтовый ящик лучше создать как можно раньше, поэтому мы будем его создавать не в методе формы, а в основном коде проекта, который обычно программист не исправляет. В результате код в dpr-файле проекта будет выглядеть так, как показано в листинге 1.48.
const
MailslotName = '\\.\mailslot\DelphiKingomSample_Viewer_FileCommand';
EventName = 'DelphiKingdomSamplе_Viewer_Command_Event';
var
ClientMailslotHandle: THandle;
Letter: string;
OpenForView: Boolean;
BytesWritten: DWORD;
begin
// Пытаемся создать почтовый ящик
ServerMailslotHandle:= CreateMailSlot(MailslotName, 0,
MAILSLOT_WAIT_FOREVER, nil);
if ServerMailslotHandle = INVALID_HANDLE_VALUE then
begin
if GetLastError = ERROR_ALREADY_EXISTS then
begin
// Если такой ящик уже есть, подключаемся к нему, как клиент
ClientMailslotHandle:= CreateFile(MailslotName, GENERIC_WRITE,
FILE_SHARE_READ, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
// В зависимости от того, какие переданы параметры, формируем
// строку для передачи предыдущему экземпляру. Первый символ
// строки — команда:
// e — открыть файл для редактирования
// v — открыть файл для просмотра
// s — просто активизировать предыдущий экземпляр
// Для команд e и v к строке, начиная со 2-го символа,
// добавляется имя файла
if ParamCount > 0 then
begin
OpenForView:= (ParamCount > 1) and
(CompareText(ParamStr(2), '/v') = 0);
if OpenForView then Letter:= 'v' + ParamStr(1)
elsе Letter:= 'e' + ParamStr(1);
end
else Letter:= 's';
// Отправляем команду в почтовый ящик
WriteFile(ClientMailslotHandle, Letter[1], Length(Letter),
BytesWritten, nil);
// Сигнализируем об отправке данных через специальное событие
CommandEvent:= OpenEvent(EVENT_MODIFY_STATE, False, EventName);
SetEvent(CommandEvent);
// Закрываем все дескрипторы
CloseHandle(CommandEvent);
CloseHandle(ClientMailslotHandle);
end;
end
else
begin
// Создаем событие для сигнализирования о поступлении данных
CommandEvent:= CreateEvent(nil, False, False, EventName);
// Выполняем обычный для VCL-приложений цикл
Application.Initialize;
Application.CreateForm(TDKSViewMainForm, DKSViewMainForm);
Application.Run;
// Закрываем все дескрипторы
CloseHandle(ServerMailslotHandle);
CloseHandle(CommandEvent);
end;
end.
Теперь осталось "научить" первую копию приложения обнаруживать момент, когда в почтовом ящике оказываются сообщения, и забирать их оттуда. Было бы идеально, если при поступлении данных главная форма получала бы какое-то сообщение, но готового такого механизма, к сожалению, не существует. Из положения можно выйти, задействовав события.