Предыстория: Наконец-то настал момент, когда мои эксперименты с C++ нашли практическую сторону и я займусь чем-то полезным. Задумка попробовать некоторые плюсовые библиотеки была давно, запаздывала только реализация - я честно скажу, садился и бросал C++ раза четыре, уж оооочень жестко было вникать сразу с хардкорной темы - сборка open-source проектов с кучей внешних библиотек и непонятной внутренней кухней приложения. Отдельные open-source проекты типа OpenCASCADE, GMSH, LibreDWG я даже успешно собирал и они работали, некоторые типа SFCGAL или CloudCompare вводили меня в ужас количеством библиотек и сложностью сборки. Тем не менее я не бросал и двигался дальше.
В данной статье хотелось бы уделить внимание важной и сложной теме - отладке Renga под C++. Проблема имеет 2 особенности - программа Renga не поддерживает возможности загрузки и отладки плагинов как это реализовано в Revit/AutoCAD, а также сама идея писать на C++ влечет некоторые неприятные обходные пути таких действий, но - обо всем по порядку.
1. Немного про Renga API
Вообще говоря, про Renga API кроме редких публикаций вендора и SDK с примерами известно мало. На сайте программы есть набор расширений, преимущественно от отечественных вендоров, устанавливающих связь своих продуктов с Renga, но об исходном коде приложений мечтать не приходится.
Немного разбавляет положение GitHub страница вендора с исходным кодом плагина ModelExplorer представленного среди упомянутых расширений выше (надеюсь в свете начавшихся массовых блокировок GitHub российских разработчиков его это не затронет, т.к. зеркал я не видел).
Другим живым примером можно назвать репозитории ModPlus популярного разработчика приложений для AutoCAD, Revit, Renga - Александра Пекшева. Для Renga там числятся 7 страничек, язык разработки - C# . Небольшая подборка из также на С # была и у меня в январской серии статей по интеграции Renga с Dynamo Core.
На этом наверное и всё. А теперь переходим к ягодках - к разработке плагинчика на C++.
Стоит отметить общую особенность разработки на любых языках программирования под Renga - это механику работы через COM, даже на С++, несмотря на то, исходное API у продукта на C++.
2. О начале разработки на C++
В отличие от .NET здесь есть свои "языковые" особенности, диктующие свои правила логики приложения (библиотеки).
Если в .NET у нас была библиотека классов (на .NET Framework), то в C++ используется похожий тип приложения - Dynamic Link Library. При всем при этом, несмотря на данный тип, для отладки приложения мы будем использовать сущность статичной библиотеки, но об этом далее.
Если в .NET мы подключали к проекту COM-библиотеку (описание функций) "RengaCOMAPI.tlb" и "Renga.NET.PluginUtility.dll"; то в случае с C++ вместо "Renga.NET.PluginUtility.dll" у нас появляется набор заголовочных файлов (header-файлов), идущих в поставке с Renga SDK - они описывают те же структуры, что и в .NET-библиотеке, то есть Enum и прочие доп. определения. Также как в .NET, здесь прямое подключение библиотеки RengaCOMAPI.tlb приведет скорее всего к появлению в проекте ошибок (не найден файл *.tlh).
Здесь есть интересный принцип работы в C++ с COM: а именно, если функции определены в *.tlb-библиотеке, то нам надо после ее импорта в программу (и не как include, а как import) единожды собрать проект, для того, чтобы в bin-директории у нас появился заголовочный файл для данного ЯП (в нашем случае, для C++ - это *.tlh файл).
Из-за специфики отладки приложения и в целом принципа работы с COM (расскажу далее), я буду следовать следующей архитектуре приложения: DLL-библиотека и консольное приложение в рамках одного решения (solution).
2.1 Создание структуры приложения
Сразу упомяну исходный код демонстрируемого примера https://github.com/GeorgGrebenyuk/renga_3d-export/tree/main/src. Если GitHub меня заблокирует, то я перемещусь на GitFlic и ссылки обновлю ...
Теперь переходим к настройке проектов - начнем с первого (нашей библиотеки lib_core):
2.2 Редактирование параметров основной библиотеки
Теперь надо инициализировать наш плагин с помощью стандартизированных процедур, описанных в справке к Renga SDK, для достижения чего сделать обращение к COM-библиотеке.
Удобно зафиксировать все заголовочные файлы в одном header-файле, в моем случае я назвал его "Renga_import.h".
В теле реализующего его cpp-файла есть дополнительные незадекларированные структуры: класс для отслеживания нажатий пользователя на кнопку. https://github.com/GeorgGrebenyuk/renga_3d-export/blob/main/src/renga_core/plugin_start.cpp.
2.3. Настройка статической библиотеки для консольного приложения
В силу того, что для отладки мы будем запускать отдельный экземпляр Renga и подключаться к проекту по имени файла, то нам потребуется фактически дважды объявить точку входа в приложения - в первом случае - под логику плагина (размещенного в выделенной папке программы), а с другой стороны - через статическую библиотеку (static library), описывающую функцию входа (в общем случае там может описываться много чего, но мы ограничимся только одним методом).
Компиляция библиотеки с такой конструкцией приведет к появлению нового файла *.lib среди скомпилированных файлов. Именно этот файл мы будем использовать как подключаемый к нашему консольному проекту для точки входа в приложение.
Согласен, выглядит топорно - но хотя бы работает ....
Для примера установим точку останова в тестовом классе и глянем на свойство проекта - путь к файлу.
3. Важные ограничения и некоторые мысли
1. Стоит отметить важную особенность - Renga при запуске инициализирует структуру библиотеки плагина и случается, крашится. Никаких логов не оставляет, но экспериментально было выявлено что причина крашей связана с линковкой файлов зависимостей библиотек - связанной с COM.
То есть в режиме отладки все работает отлично, а вот в режиме плагина она вообще не запускается.
2. На .NET пользователю щадяще оставлены .NET'овские типы данных, в то время как в C++ у нас их значительное многообразие - для одних только строк придется использовать различные конструкции типа char *, std::string, stg::wstring и пр.
3. Как я заметил, обращение с COM-библиотекой очень нестабильно (объяснить по другому краш Ренги при запуске я не могу), по причине чего желательно все нужные объекты свойств и геометрии желательно переводить в нейтральные классы, апеллирующие примитивами языка.
4. Вместо заключения
На этом я наверное пока останавливаюсь.
Могу, конечно еще упомянуть про попытку анализа крашей - при их возникновении формируются как минимум 2 файла
Отчет "Report.wer" xml-типа в генерируемой папке с путем типа такого: C:\ProgramData\Microsoft\Windows\WER\ReportArchive\AppCrash_Renga.exe_be83edeeda6066787737a9ab56190416fe579fc_2e055962_12116fa8-3ac3-4c3c-99cb-f9dc781557ac
И dump загруженных в память приложений по пути C:\Users\Georg\AppData\Local\CrashDumps с префиксом Renga.exe.
Ещё раз ссылаюсь на свой тестовый проект здесь - он будет посвящен логике преобразования объектов из 3D-представления Renga в популярные форматы моделей - Autodesk NWC и позже хочу ещё MSH (gmsh) и citygml.
И да, в следующей части я попробую рассказать как получить всю геометрии модели вместе со свойствами и упаковать в структуры нового класса, на базе которого уже буду формироватах 3D-представление в других форматах.
Не пропускайте публикации, подписывайтесь на Telegram-канал с тизерами статей.
#renga #rengabim #com api #Ренга