А. Григорьев - О чём не пишут в книгах по Delphi
Пример, который мы здесь рассмотрим (программа DKSView), умеет ассоциировать файлы с расширением dks с собой, а также проверять, не были ли они ассоциированы с другим приложением. DKSView является MDI-приложением, т.е. может открывать одновременно несколько файлов. Если приложение уже запущено, а пользователь пытается открыть еще один dks-файл, желательно, чтобы он открывался не в новом экземпляре DKSView, а в новом окне уже имеющегося. Поэтому наш пример будет также уметь обнаруживать уже запущенный экземпляр программы и переадресовывать открытие файла ему.
1.3.2.1. Ассоциирование расширения с приложением
Файловые ассоциации прописываются в реестре, в разделе HKEY_CLASSES_ROOT. Чтобы связать расширение с приложением, необходимо выполнить следующие действия:
1. В корне раздела HKEY_CLASSES_ROOT нужно создать новый раздел, имя которого совладает с расширением с точкой перед ним (в нашем случае это будет раздел с именем ".dks"). В качестве значения по умолчанию в этот раздел должна быть записана непустая строка, которая будет идентифицировать соответствующий тип файла. Содержимое этой строки может быть произвольным и определяется разработчиком (в нашем случае эта строка имеет значение "DKS_View_File").
2. Далее в корне раздела HKEY_CLASSES_ROOT следует создать раздел, имя которого совпадает со значением ключа из предыдущего пункта (т.е. в нашем случае — с именем "DKS_View_File"). В качестве значения по умолчанию для этого ключа нужно поставить текстовое описание типа (это описание будет показываться пользователю в Проводнике в качестве типа файла).
3. В этом разделе создать подраздел Shell, в нем — подраздел Open, а в нем — подраздел Command, значением по умолчанию которого должна стать командная строка для запуска файла. Имя файла в ней заменяется на %1 (подробнее о командной строке чуть ниже).
4. Описанных действий достаточно, чтобы система знала, как правильно открывать файл из Проводника или с помощью ShellExecute. Однако правила хорошего тона требуют, чтобы с файлом была ассоциирована также иконка, которую будет отображать рядом с ним Проводник. Для этого в разделе, созданном во втором пункте, следует создать подраздел "DefaultIcon" и в качестве значения по умолчанию задать ему имя файла, содержащего иконку. Если это ico-файл, содержащий только одну иконку, к имени файла ничего добавлять не нужно. Если иконка содержится в файле, в котором может быть несколько иконок (например, в exe или dll), после имени файла следует поставить запятую и номер требуемой иконки (иконки нумеруются, начиная с нуля).
Приведенный список — это самый минимальный набор действий, необходимых для ассоциирования расширения с приложением. Вернемся к третьему пункту. Имя подраздела "Open" задает команду, связанную с данным расширением, т.е. в данном случае — команду "Open". В разделе Shell можно сделать несколько аналогичных подразделов — в этом случае с файлом будет связано несколько команд. У функции ShellExecute есть параметр lpOperation, в котором задается имя требуемой команды. Пользователь Проводника может выбрать одну из возможных команд через контекстное меню, которое появляется при нажатии правой кнопки мыши над файлом. Существует возможность установить для этих пунктов меню более дружественные имена. Для этого нужно задать значение по умолчанию соответствующего подраздела. В этой строке допустим символ "&" для указания "горячей" клавиши, аналогично тому, как это делается, например, в компоненте TButton.
Если в ShellExecute команда не указана явно, используется команда по умолчанию (то же самое происходит при двойном щелчке на файле в Проводнике). Если не оговорено обратное, командой по умолчанию является команда "Open" или, если команды "Open" нет. первая команда в списке. При необходимости можно задать другую команд) по умолчанию. Для этого нужно указать ее название в качестве значения по по умолчанию раздела Shell.
В нашем примере будет две команды: Open (открыть для редактирования) и View (открыть для просмотра). Поэтому информация в реестр заносится так, как показано в листинге 1.46.
Листинг 1.46. Занесение в реестр информации, необходимой для ассоциирования файла с приложениемconst
FileExt = '.dks';
FileDescr = 'DKS_View_File'.
FileTitle = 'Delphi Kingdom Sample file';
OpenCommand = '&Открыть';
ViewCommand = '&Просмотреть';
// Занесение в реестр информации об ассоциации
// Расширения dks с программой
procedure TDKSViewMainForm.SetAssociation(Reg: TRegistry);
begin
Reg.OpenKey('' + FileExt, True);
Reg.WriteString('' , FileDescr);
Reg.OpenKey('' + FileDescr, True);
Reg.WriteString(FileTitle);
Reg.OpenKey('Shell', True);
Reg.OpenKey('Open', True);
Reg.WriteString('', OpenCommand);
Reg.OpenKey('command', True);
Reg.WriteString('', '"' + ParamStr(0) + '" "%1"');
Reg.OpenKey('' + FileDescr, True);
Reg.OpenKey('Shell', True);
Reg.OpenKey('View', True);
Reg.WriteString('', ViewCommand);
Reg.OpenKey('command', True);
Reg.WriteString('' + ParamStr(0) + '" "%1" /v');
Reg.OpenKey('' + FileDescr, True);
Reg.OpenKey('DefaultIcon', True);
Reg.WriteString('', ParamStr(0) + ',0');
end;
1.3.2.2. Командная строка
Командная строка досталась Windows по наследству от DOS. Там основным средством общения пользователя с системой был ввод команд с клавиатуры. Команда запуска приложения выглядела так:
<Имя приложения> <Строка параметров>
Строка параметров — это произвольная строка, которая без изменений передавалась программе. От имени программы она отделялась пробелом (пробелы в именах файлов и директорий в DOS не допускались). Разработчик конкретного приложения мог, в принципе, интерпретирован, эту строку как угодно, но общепринятым стал способ, когда строка разбивалась на отдельные параметры, которые разделялись пробелами. Вид и смысл параметров зависел от конкретной программы. В качестве параметров нередко передавались имена файлов, с которыми должна была работать программа.
В Windows мало что изменилось — функции CreateProcess и ShellExecute, запускающие приложение, по-прежнему используют понятие командной строки. Разве что теперь максимальная длина строки стала существенно больше, и командную строку можно получить в кодировке Unicode. Но, как и раньше, разделителем параметров считается пробел. Однако теперь пробел может присутствовать и в имени файла, как в имени самой программы, так и в именах файлов, передаваемых в качестве параметров. Чтобы отличать такой пробел от пробела-разделителя, параметры, содержащие пробелы, заключаются в двойные кавычки. Если имя программы содержит пробелы, они тоже заключаются в двойные кавычки. И, конечно же, если в кавычки окажется заключенным параметр, в котором нет пробелов, хуже от этого не будет.
Для работы с параметрами командной строки в Delphi существуют две стандартные функции: ParamCount и ParamStr. Функция ParamCount возвращает количество параметров, переданных в командной строке. ParamStr — параметр с заданным порядковым номером. Параметры нумеруются начиная с единицы, нулевым параметром считается имя самой программы (при подсчетах с помощью ParamCount этот "параметр" не учитывается). Эти функции осуществляют разбор командной строки по описанным ранее правилам: разделитель —пробел, за исключением заключенных в кавычки. Кавычки, в которые заключен параметр, функция ParamStr не возвращает.
Ассоциированный файл запускается с помощью механизма командной строки. В реестр записывается командная строка (вместе с именем приложения), в которой имя открываемого файла заменяется на %1. Когда пользователь запускает ассоциированный файл (или он запускается приложением через ShellExecute), система извлекает из реестра соответствующую командную строку, вместо %1 подставляет реальное имя файла и пытается выполнить получившуюся команду. Отметим, что если имя файла содержит пробелы, в кавычки оно автоматически не заключается, поэтому о кавычках приходится заботиться самостоятельно, заключая в них %1. Таким образом, в реестр в качестве командной строки должно записываться следующее
<Имя программы> "%1"
Если существуют разные варианты запуска одного файла (т.е. как в нашем случае — open и view), они различаться дополнительным параметрами. В частности, в нашем примере для открытия для редактирования не будут требоваться дополнительные параметры, для открытия для просмотра в качестве второго параметра должен передаваться кляч v, т.е. в реестр для этой команды будет записана такая строка:
<Имя программы> "%1" v
Программа должна анализировать переданные ей параметры и открывать соответствующий файл в требуемом режиме. В нашем случае этот код выглядит очень просто (листинг 1.47).