Когда необходимо обеспечить взаимодействие c данными вашего приложения, можно использовать специальные механизмы операционной системы. Если говорить о приложениях WIndows, то для них доступно множество различных механизмов, но в рамках этой статьи коснемся только некоторых из них:

  1. Открытие файлов через ассоциацию с приложением

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

  2. Реализация собственного протокола в системе

    Этот механизм позволяет приложениям обрабатывать специфические ссылки. Например, при установке мессенджера Telegram в Windows регистрируется протокол tg://. Когда пользователь нажимает на такую ссылку в браузере или документе, система понимает, что ее должен обработать клиент Telegram, поскольку информация о протоколе хранится в реестре Windows. Такая возможность реализуется по аналогии с типовыми протоколами, такими как http://, mailto: или ftp://, — разработчики могут создавать собственные, кастомные протоколы для запуска приложений и передачи в них данных.

  3. Механизм Drag & Drop (перетаскивание файлов)

    Drag In (перетаскивание внутрь) — когда пользователь перетаскивает файл в окно приложения, то программа его открывает.

    Drag Out (перетаскивание наружу) — приложение позволяет пользователю «вытянуть» объект за пределы окна приложения, передав его в другую программу или создав файл-ярлык в Windows.

  4. Копирование и вставка данных через буфер обмена

    Буфер обмена Windows позволяет приложениям передавать данные между собой с помощью копирования и вставки. Информация в буфере может храниться в разных форматах: простой текст, изображения, файлы, специальные ссылки и даже структурированные данные в виде XML или JSON. Возможна регистрация и использование своего уникального формата данных.

Поскольку я сейчас работаю над проектами, связанными с ЛОЦМАН:PLM, на примере десктопного приложения ЛОЦМАН:PLM Клиент покажу, где применяются эти механизмы. А в примерах кода более подробно продемонстрирую, как можно реализовать их в приложениях, написанных в Lazarus IDE.

ЛОЦМАН:PLM — это решение для управления инженерными данными и жизненным циклом изделий, разрабатываемое компанией АСКОН. Система предоставляет инструменты для работы с данными на всех этапах жизненного цикла продукта.

2022-Схема.png
Модули ЛОЦМАН:PLM

Десктопный клиент ЛОЦМАН:PLM — это классическое Windows-приложение, которое позволяет пользователю работать с базой данных комплекса PLM, управляя проектами и информационными объектами. В зависимости от своей роли пользователь может взаимодействовать с определёнными типами объектов, обеспечивающих поддержку различных этапов жизненного цикла изделия. Это могут быть объекты систем��ой инженерии, а также конструкторской или технологической подготовки производства.

рис 2.png
Различные варианты структур

ЛОЦМАН:PLM Клиент работает с понятием информационного объекта, который имеет идентификационные данные, позволяющие ссылаться на него. Для передачи этих данных и последующего поиска соответствующего объекта в приложении реализованы специальные функции.

Примеры функций работы с объектом в ЛОЦМАН:PLM Клиент

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

Для анализа содержимого буфера обмена можно воспользоваться утилитой Free Clipboard Viewer, она позволит увидеть как там хранится информация скопированная в него. Утилита покажет, что в записи LoodsmanBuffer хранится информация в виде JSON-объекта следующего содержания:

CB.png
Просмотр содержимого буфера обмена

Сам формат LoodsmanBuffer поддерживается только ЛОЦМАН:PLM, вставить такие данные в другие приложения не получится.

Однако, для передачи информации о конкретных объектах во внешние программы или пересылки ее пользователям, реализован механизм создания специальных ссылок. Выделив объект в дереве, можно воспользоваться командой «Инструменты → Ссылки → Копировать ссылку», после чего в буфер обмена будет помещена строка следующего вида: ask:Loodsman.URL?Action=Navigate,params=NTF8REJfTkFNRXw0ODR8NDg1fHw0ODV8NDU4fDQ3OXw1MDR8NTA1

Здесь можно видеть что эта ссылка зарегистрированного протокола, которая имеет специальный формат, в которой ask: — это имя протокола, который обрабатывает ЛОЦМАН:PLM Клиент. Параметр Action=Navigate говорит приложению о том, что нужно перейти в дереве объектов на определенный объект, а params= содержит закодированную в Base64 строку, содержащую идентификационные данные.

Декодированная строка имеет следующий вид: 51|DB_NAME|484|485||485|458|479|504|505, Здесь в строке, через разделитель | указаны имя базы данных и уникальные идентификаторы объектов: выбранного и его родительского.

Ссылка может быть вставлена в текст и будет обрабатываться как гиперссылка. При переходе по этой ссылке система автоматически запустит ЛОЦМАН:PLM Клиент, и передаст ему ее в качестве параметра. Клиент в свою очередь обработает полученные данные, найдет в дереве и выделит требуемый объект.

Помимо копирования в буфер обмена, имеется еще один способ создания ссылки на объект — это перетаскивание объекта за пределы приложения. В этом случае ЛОЦМАН:PLM Клиент генерирует специальный файл-ярлык (.lnk), который сохраняется в том месте, где пользователь отпустит кнопку мыши после перетаскивания.

Внутри файла хранится информация в следующем виде:

[InternetShortcut]
URL=ask:Loodsman.URL?Action=Navigate,params=MHxBS19QbG0wMXwyODk4fDEwMTI=
IconIndex=0
IconFile=C:\Program Files (x86)\ASCON\Loodsman\Client\Loodsman.exe

При двойном клике по ярлыку, как и при щелчке по ссылке вида ask: автоматически запускается ЛОЦМАН:PLM Клиент, или активируется уже запущенный экземпляр. А далее он получает из ярлыка необходимые данные и отображает окно с нужным объектом.

Файл-ярлык с расширением .lnk — это стандартный ярлык в системе Windows, предназначенный для быстрого доступа к программам, файлам, папкам или сетевым ресурсам. Его обработку выполняет сама система. Однако, помимо этого, ЛОЦМАН:PLM может работать с собственными ярлыками, поскольку при установке регистрируются следующие типы файлов, которые открываются с помощью приложения:

  • .lnd -Loodsman.LinkDocument (Ссылка на документ ЛОЦМАН:PLM)

  • .lnf - Loodsman.LinkFile (Ссылка на файл ЛОЦМАН:PLM)

  • .lno - Loodsman.LinkObject (Ссылка на объект ЛОЦМАН:PLM)

Выше были приведены примеры применения системных механизмов на примере ЛОЦМАН:PLM,  а далее будет приведена информация для реализации этих механизмов в собственном приложении.

Реализация механизмов в собственном приложении

В качестве примеров буду использовать код из заглушки, которая была написана для партнёров, разрабатывающих механизм интеграции с ЛОЦМАН:PLM. Заглушка позволяла протестировать нужный функционал без установки и развёртывания всего комплекса PLM на машинах разработчиков. Конечно, после отладки основных взаимодействий между приложениями, комплекс PLM был развернут на отдельной машине и уже там тестировалась совместная работа в полном объёме.

Открытие файла на двойной клик

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

Файл приложения

Для внесения в заглушку тестовых данных, которые были сгенерированы с помощью отчета из базы данных ЛОЦМАН:PLM, они были помещены в файл с расширением .lpf. Данные внутри файла представляют собой JSON массив объектов.

Добавление записи в реестр

Обычно все действия, связанные с добавлением записей в реестр Windows, прописываются в установщике приложения. Однако возможен и вариант создания REG-файла, который нужно было импортировать вручную. При составлении параметров записи важно учитывать, для какого пользователя вносятся изменения: если запись добавляется только для текущего пользователя, то используется ветка HKCU (HKEY_CURRENT_USER). Если же требуется реализация изменений для всех пользователей системы, то необходимо работать с веткой HKLM (HKEY_LOCAL_MACHINE), но это требует административных прав.

В установщиках приложений для указания пути до приложения обычно используются макросы, а в файле REG приходится указать полный путь до приложения заранее. В приведённом ниже примере предполагается, что приложение будет расположено в папке C:\tmp\loo_plug\, а иконка для файла будет браться из ресурса 1.

Пример добавления записей в реестр:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Classes\.lpf]
@="loo_plug_file"

[HKEY_CURRENT_USER\Software\Classes\loo_plug_file]
@=""
"EditFlags"=dword:00000000
"BrowserFlags"=dword:00000008

[HKEY_CURRENT_USER\Software\Classes\loo_plug_file\DefaultIcon]
@="\"C:\\tmp\\loo_plug\\loo_lpug.exe\",1"

[HKEY_CURRENT_USER\Software\Classes\loo_plug_file\shell]

[HKEY_CURRENT_USER\Software\Classes\loo_plug_file\shell\open]
@=""

[HKEY_CURRENT_USER\Software\Classes\loo_plug_file\shell\open\command]
@="\"C:\\tmp\\loo_plug\\loo_lpug.exe\" \"%1\""

Обработка передаваемых параметров командной строки

После внесения записи в реестр при двойном клике на файле с расширением .lpf система Windows будет запускать наше приложение и передавать путь к этому файлу в качестве аргумента командной строки. Поэтому необходимо, чтобы приложение корректно обрабатывало параметры командной строки, которые в него передаются.
Важно учитывать, что помимо пути к файлу могут передаваться и другие параметры с аргументами. Например, путь к файлу может быть не первым, а вторым или даже третьим аргументом. По этой причине при обработке параметров желательно анализировать их последовательно и выбирать тот, который соответствует нужному критерию — в нашем случае, заканчивается на ".lpf".

В заглушке я включил обработку передаваемых параметров командной строки в процедуру, которая назначена на создание главной формы OnFormCreate.

procedure TForm1.FormCreate(Sender: TObject);
var
  i: Integer;
  param: string;
begin
  for i := 1 to ParamCount do
  begin
    param := ParamStr(i);
    if LowerCase(ExtractFileExt(param)) = '.lpf' then
    begin
      // Найден параметр с расширением .lpf
      // Выполняется загрузка данных ...
    end;
  end;
end;

Открытие файла перетаскиванием

Открытие файла перетаскиванием на форму в самой заглушке не реализовывалось, но реализация такой функции достаточно проста. Для этого в настройках формы необходимо включить пункт AllowDropFiles, а далее создать функцию обработчика событий OnDropFiles . В функцию будет передан массив строк с путями до файлов, которые были перенесены на форму приложения. После фильтрации их можно передавать в функцию загрузки данных из файла.

Поддержка открытия ссылки собственного протокола

Для того чтобы обрабатывать собственный протокол, как и в случае с ассоциацией типа файла, необходимо внести записи в реестр. Для обработки протокола ASK приложением заглушкой, которое будет находиться по пути C:\tmp\loo_plug\, необходимо добавить в реестр следующие записи:

Windows Registry Editor Version 5.00

[HKEY_CURRENT_USER\Software\Classes\ask]
"URL Protocol"=""

[HKEY_CURRENT_USER\Software\Classes\ask\DefaultIcon]
@="C:\\tmp\\loo_plug\\loo_lpug.exe,0"

[HKEY_CURRENT_USER\Software\Classes\ask\Shell]

[HKEY_CURRENT_USER\Software\Classes\ask\Shell\Open]

[HKEY_CURRENT_USER\Software\Classes\ask\Shell\Open\Command]
@="\"C:\\tmp\\loo_plug\\loo_lpug.exe\" \"%1\""

Поскольку ранее мы реализовали обработку только аргументов, оканчивающихся на ".lpf", теперь необходимо добавить поддержку строк, начинающихся с "ask:", и передавать такие строки в функцию, которая вернёт ИД объекта.

Пример кода функции, для сборки нужно включить в uses модуль base64

function ExtractAndDecode(const input: string; out ObjectID: Integer; out DatabaseName: string): Boolean;
var
  paramsStart, paramsEnd: Integer;
  paramsValue, decodedString: string;
  elements: TStringArray;
begin
  ObjectID := 0;
  DatabaseName := '';

  paramsStart := Pos('params=', input);
  if paramsStart = 0 then Exit(False);

  paramsStart := paramsStart + Length('params=');
  paramsEnd := Length(input);

  paramsValue := Copy(input, paramsStart, paramsEnd - paramsStart + 1);

  decodedString := DecodeStringBase64(paramsValue);
  if decodedString = '' then Exit(False);

  elements := decodedString.Split(['|']);
  if Length(elements) < 4 then Exit(False);

  DatabaseName := elements[1];
  try
    ObjectID := StrToInt(elements[3]);
  except
    Exit(False);
  end;

  result:=True;
end;

Чтобы для каждой новой ссылки не запускался свой экземпляр приложения, нужно организовать передачу информации от вновь запускаемого дубликата в уже запущенное приложение. Такой вариант можно реализовать, используя связку Mutex и Named Pipes, но об этом расскажу в следуюoщей статье.

Создание ярлыка со ссылкой "вытягиванием" объекта

В Lazarus, к сожалению, отсутствует встроенная поддержка простого механизма "вытягивания" объектов за пределы формы, аналогичного ��еханизму перетаскивания файлов из системы на форму. Однако для упрощения этой задачи, можно использовать модуль uShellDragDrop, который предоставляет готовые функции для реализации как вытягивания объектов за пределы формы, так и копирования их в буфер обмена.

Модуль был найден на форуме Lazarus в ветке, где участник форума под псевдонимом ASerge, представил этот модуль в ответ на запрос другого пользователя, желающего реализовать перетаскивание файлов из приложения Lazarus в другие программы, поддерживающие WM_DROPFILES.

Основная идея состоит в следующем:

  • На основе данных приложения создается временный файл.

  • С помощью функции DragandDropCopyComplete из модуля uShellDragDrop.pp этот файл передается в целевую директорию.

Файл, который мы создаём, представляет собой обычный INI-файл с расширением .URL, содержащий ссылку на нужный ресурс или действие. Пример содержимого такого файла:

[InternetShortcut]
URL=ask:Loodsman.URL?Action=Navigate,params=MHxBS19QbG0wMXwyODk4fDEwMTI=
IconIndex=1
IconFile=C:\tmp\loo_plug\loo_lpug.exe

Что записано внутри файла:

  • URL указывает на специальный протокол ask:, который будет обработан соответствующим приложением.

  • IconFile задаёт путь к иконке, которая будет использоваться для ярлыка.

После выполнения функции DragandDropCopyComplete, в целевой директории появится копия временного .URL-файла — полноценный ярлык. При его запуске будет автоматически вызвано приложение, ответственное за обработку протокола ask:.

Заключение

Надеюсь, приведенные выше примеры пригодятся вам для понимания того, как организовать взаимодействие между пользователем, системой и вашим приложением. А в следующем материале я продолжу эту тему и расскажу, как организовать передачу данных между экземплярами приложения с помощью Mutex и Named Pipes, чтобы избежать запуска дублирующихся копий клиента и обеспечить "бесшовную" обработку новых запросов.