Небольшое предупреждение: я ничего не понимаю в языке С, поэтому здесь могут быть весьма грубые ошибки в высказываниях про него.
В общем, нужно было организовать взаимодействие с Pro/Engineer из своей программы. Поскольку пишу я на Delphi, то я (естественно) и решил использовать Delphi :)
Для взаимодействия, Pro/Engineer предлагает нам Pro/Toolkit. В папочке можно найти доки (*.html), заголовочники под C (*.h) и Сишные библиотеки (*.lib).
Если бы там была бы DLL-ка - этого поста бы не было. Потому что в этом случае я мог бы просто выкинуть lib-файлы, перевести h-файлы на Delphi и подключить готовую DLL-ку напрямую.
Но по какой-то причине никакой DLL нет, есть только lib (понятно, что это два разных случая - в первом в lib, грубо говоря, содержатся только ссылки на DLL, а во втором - там содержится вся реализация функций). Поэтому, единственный вариант использовать Pro/Engineer в Delphi - это собрать эту самую DLL-ку самому. Именно этому и посвящён этот пост.
Собираем DLL для Delphi из Сишных lib-файлов.
1). Создаём проект DLL в C++.
Для начала нам надо бы поставить MSVS (Microsoft Visual Studio), а точнее - её C++ часть. После установки я залез в File/New/Project и выбрал "Win32 Project". Затем указал тип проекта DLL и отметил галочку "Exports symbols" (в конце-концов, это единственное, зачем нам нужна эта DLL - чтобы она экспортировала функции из lib-файлов).После сохранения (да, перед этим я удалил демо-код, который создал wizard) я создал подпапки includes и lib в папке с проектов, в которые перетащил *.h и *.lib файлы из папки с Pro/Toolkit. После чего, залез в настройки проекта и после 10-ти минутного копания в настройках нашёл опции, куда можно вписать эти два каталога ("Additional Include/Library directories").
2). "Подключаем uses-ы".
Далее, нам надо как-то подключить все заголовочники. Не проблема: вспоминаем свои навыки в DOS, открываем cmd, входим в каталог Includes (поскольку я использую Total Commander, то я сперва вошёл в каталог, а затем запустил cmd) и пишем:dir *.h > t.txtПосле этого мы получаем список всех файлов в текстовом файле t.txt. Они очень красиво расположены по колонкам, поэтому осталось только открыть файл в продвинутом редакторе (я воспользовался редактором Delphi) и удалить начала строк. Строки
...Становятся:
2009.04.08 17:20 14'191 ProAnimate.h
2009.04.08 17:20 13'416 ProAnnotation.h
2009.04.08 17:20 31'698 ProAnnotationElem.h
2009.04.08 17:20 5'268 ProAnnotationFeat.h
2009.04.08 17:20 3'100 ProANSI.h
2009.04.08 17:20 6'621 ProArray.h
...
...После чего, два Search & Replace сделают нам:
ProAnimate.h
ProAnnotation.h
ProAnnotationElem.h
ProAnnotationFeat.h
ProANSI.h
ProArray.h
...
...(к счастью, каждый заголовочник начинается с 'Pro', поэтому мне легко удалось сделать замену 'Pro' -> '#include "Pro').
#include "ProAnimate.h"
#include "ProAnnotation.h"
#include "ProAnnotationElem.h"
#include "ProAnnotationFeat.h"
#include "ProANSI.h"
#include "ProArray.h"
...
Полученный текст я вставил в проект (насколько я понимаю, это можно сделать в главный cpp-файл или же в главный h-заголовочник проекта).
3). Подготовка списка функций.
Окей, следующий шаг - сказать, что каждая функция из этих заголовочников должна быть экспортирована. Для этого нам нужен список всех функций в h-файлах.Я пробежался по исходникам и заметил, что каждая экспортируемая функция начинается с 'extern'. Следовательно, нам просто нужно вытащить все такие строки из всех h-файлов. Для этого, я сперва слил все файлы в один большой файл командой:
copy *.h data.txtПолучил один большой файл в 4 Мб. Далее, я помнил, что в DOS была вроде бы утилитка grep, которая могла дёргать строки из файла по условию - её также применяли для фильтрации вывода (и, вроде бы, она пришла из unix). Я попробовал вызвать grep из cmd на удачу - и, действительно, такая утилитка нашлась, хотя, как оказалось, она не входит в состав ОС, а была установлена с каким-то средством разработки (скорее всего - Delphi).
Почитав справку (grep ?), я запустил её с такими параметрами:
grep ^extern* data.txt > grep.txtЯ ничего не понимаю в регулярных выражениях и, по моему мнению, эта команда должна была выдернуть из файла все строки, начинающиеся с 'extern'. На самом деле она выдернула все строки, содержащие 'extern' (все лишние - это слова типа external и т.п.). Если указать "extern " - строк будет поменьше, хотя в заголовочниках также встречаются extern + TAB. TAB в командной строке мне задать не удалось. Поэтому сделал в несколько этапов: сперва
grep extern data.txt > grep.txtЗатем:
grep -v+ extern[a-z] grep.txt > grep2.txtКонечный список пришлось слегка подрихтовать вручную. В итоге получил список типа:
...Потом пришлось написать небольшую программку на Delphi, которая обрезала бы скобки с параметрами (примитив, но как это сделать без программы - мне в голову не пришло).
ProMdlDisplay ( ProMdl model );
ProMdlErase (ProMdl handle);
ProMdlEraseAll(
ProMdlEraseNotDisplayed( void );
ProMdlfileCopy ( ProMdlType mdl_type,
ProMdlGtolVisit( ProMdl model,
ProMdlIdGet ( ProMdl model,
ProMdlInit (ProName name,
ProMdlIsModifiable(ProMdl p_model,
...
4). "Выписываем exports".
Как только мы получили полный список функций - осталось оформить его в виде списка функций для экспорта из DLL. Для этого, насколько я помнил, в Сях есть def-файлы. Щёлкнув правой кнопкой по проекту в Solution Explorer, я выбрал Add new item, "Module definition file (.def)".Далее, я запустил поиск по всему диску на *.def файлы, чтобы посмотреть - как же они должны выглядеть :)
Оказалось, что очень просто. Я просто взял список функций с предыдущего шага и вставил его в def-файл вот так:
LIBRARY ProEToolkitОсталось щёлкнуть по Build и... получить кучу сообщений о unresolved external symbol. Что есть логично - ведь никакой lib-файл мы ещё не подключили. У нас объявлена куча экспортируемых функций, но линкёр не знает, где брать реализацию.
EXPORTS
Pro2dCadamImportCreate
Pro2dExport
Pro2dImportAppend
Pro2dImportCreate
ProAccessorywindowCreate
ProAnalysisAttrIsSet
...
5). Поиск реализации.
Побегав ещё пять минут по опциям проекта, я нашёл опцию "Additional dependencies", где увидел вписанные kernel32.lib, user32.lib, gdi32.lib и т.д. Туда же я добавил protk_dllmd.lib. Собственно, у Pro/Engineer-а есть такие файлы:protkmd.libНа всякие разные случаи. Ведь можно писать плагин для ProE, а можно - совершенно внешнюю программу. Плюс разные варианты одного способа. Например, есть подозрение, что mt - это от MultiThreaded, т.е. этот вариант либы скомпилирован с многопоточной версией RTL (или как так это в Сях называется). Я взял наугад (окей, вообще документацию надо читать, но мне лениво). Сборка.... и всего 8 функций не хватает. Причём все предназначены для установки соединения с Pro/Engineer. Похоже, этот вариант lib - для плагинов. Я ещё потыкался наугад, но ничего путного у меня не вышло. Ладно, пора читать документацию :D
protkmt.lib
protk_dll.lib
protk_dllmd.lib
protoolkit.lib
ptasyncmd.lib
ptasyncmt.lib
ptasync_coremt.lib
pt_asynchronous.lib
Быстро пробежавшись по докам, выясняем, что нам нужен так называемый асинхронный режим, для которого нужно подключать pt_asynchronous.lib. К сожалению, этого не достаточно для успешной компиляции, поэтому я запустил поиск файлов в папке Pro/Toolkit, содержащих слово 'pt_asynchronous'. И нашёл пример (файл make_async):
# LibrariesВозможно, можно было бы просто использовать готовый make-файл, но я не умею с ними работать. Поэтому я просто взял список lib-файлов. Кстати, в этом списке оказались и некоторые стандартные (не Pro/Engineer-ные) lib-ы, которых не было в настройках моего проекта. Однако, глянув в папку, я увидел так ещё make_async_md и make_async_mt. Судя по описанию в комментах, мне подходит именно make_async_md, хотя никакого pt_asynchronous у него не указано.
PTCLIBS = $(PRODEV_SYS)/obj/prodevelop.lib $(PROTOOL_SYS)/obj/protoolkit.lib \
$(PROTOOL_SYS)/obj/pt_asynchronous.lib
LIBS = libc.lib kernel32.lib user32.lib wsock32.lib advapi32.lib mpr.lib winspool.lib netapi32.lib psapi.lib
# Object files
OBJS = pt_async_src.obj pt_utils.obj
Запустив повторный поиск по документации с именем make-файла (это ведь make-файлы?), я, наконец, нашёл нужную информацию:
Standard LibrariesОткуда окончательно следует, что нам нужен вариант "Multi-Threaded DLL Libraries for Windows" для "Asynchronous mode library" и нас интересует файлик make_async_md (всё-таки я угадал верно).
Most Pro/TOOLKIT users will be able to use the standard Pro/TOOLKIT libraries. These libraries are available on all platforms and are used by the majority of Pro/TOOLKIT sample applications.
Library Name
Purpose protoolkit.lib (.a) Spawn mode library pt_asynchronous.lib (.a) Asynchronous mode library protk_dll.lib (.a) DLL mode library
Alternate Libraries
Pro/TOOLKIT offers alternate libraries that may be useful for specialized applications. These libraries are similar to the standard Pro/TOOLKIT libraries in content, but differ in their construction. This makes them compatible with other static and dynamic libraries.
Multi-Threaded DLL Libraries for Windows
Library Name
Purpose protkmd.lib Spawn mode library ptasyncmd.lib Asynchronous mode library protk_dllmd.lib DLL mode library
Multi-Threaded DLL (MD) libraries are used to build a multithreaded DLL for Windows using the /MD compiler flags. You can use these libraries for the following type of applications:
The makefiles make_install_md and make_async_md build with these libraries.
- DLL mode applications compiled with the MD flags (if required to link with other MD compiled libraries).
- Asynchonous mode applications compiled as DLLs to be loaded into processes external to Pro/ENGINEER.
Note: Although the library flags provide compatibility with multithreaded components, Pro/TOOLKIT calls must be made within a single thread. Pro/ENGINEER does not respond to calls made from multiple threads.
Multi-Threaded Libraries for Windows
Library Name
Purpose protkmt.lib Spawn mode library ptasyncmt.lib Asynchronous mode library
Multi-Threaded (MT) libraries are used to build a multithreaded executable for Windows using the /MT compiler flags. You can use these libraries for spawn and asynchonous mode applications compiled as executables (.exe). The makefiles make_install_mt and make_async_mt build with these libraries.
Note: Although the library flags provide compatibility with multithreaded components, Pro/TOOLKIT calls must be made within a single thread. Pro/ENGINEER does not respond to calls made from multiple threads.
6). Подключаем реализацию.
Ладно, после прописывания библиотек (кстати, во всех файлах первой идёт ссылка на несуществующую lib-у со словом 'develop' в названии - я её просто выкинул) и компиляции получаем конфликты линковки (unresolved symbol-ов уже нет): одна и та же функция входит в несколько lib-файлов. Компилятор MSVS посоветовал использовать ключ /NODEFAULTLIB. Покопавшись в опциях проекта, я решил, что это будет опция "Ignore specific library". Я вписал туда msvcrt.lib и проверил, что это именно то, что мне нужно - увидев в командной строке линкёра слова '/NODEFAULTLIB:"msvcrt.lib"'. Правда, от этого стало только хуже: похоже линкёр вообще на неё забил, и я получил кучу unresolved symbols. Тогда я подсмотрел ещё раз в make_async_md и увидел такие строчки:$(LINK) /dll /force /ignore:4006 /subsystem:console -out:$(EXE) /debug:noneПохоже, что здесь линкёр запускается с доп. параметрами /force и /ignore:4006 - я просто добавил эти параметры в "Additional parameters" в опциях командной строки линкёра проекта.
Сборка... и успешно! Правда, линкёр ругнулся (warning-ом) на мой флаг: "image being generated due to /FORCE option; image may not run". Тем не менее, DLL-ка была получена всего с 1-м варнингом.
А самое главное, что эта DLL даже работает! :D
Совсем неплохо для того, кто в первый раз запустил MSVS? Вообще, это был мой первый проект на Сях.
Мораль сей истории: не будьте беспомощны! Вы сами можете найти ответы на вопросы! Гугл + эксперименты - и у вас всё получится.
P.S. Да, кстати, я там ещё в опциях проекта нашёл опцию генерации map-файла - пригодится для отладки: EurekaLog его подцепит, если вдруг будет исключение в этой DLL.
P.P.S. Вообще-то я сделал эту DLL ещё год назад, просто недавно мне нужно было использовать новую функцию, а её не оказалось в этой DLL - при сборке первый раз она не попала в def-файл (там был случай extern + TAB - я эти случаи не заметил в первый раз). Поэтому пришлось DLL-ку пересобрать. Ну и заодно решил рассказать об этом здесь. Откровенно говоря, чтобы придумать план генерации DLL по lib-ам и использования уже готовой DLL в Delphi, мне пришлось предварительно погуглить. Я вообще сперва думал, что использовать Pro/Engineer в Delphi невозможно и боялся, что придётся изучать новый язык :) Не, не пришлось.
P.P.P.S. Мне подсказали, что можно приаттачить DLL и демку. Брать тут. В архиве исходники DLL на MS C++. И сама DLL. А также пример-приложение на Delphi, её использующее. Для перекомпиляции папку ProToolkit надо добавить в Search path проекта Delphi (в папке ProToolkit лежат транслированные для Delphi заголовочники (не все, разумеется, а только те, что мне нужны были)). Сама демка представляет автономный exe-файл, который можно запустить отдельно от ProE, ткнуть ей на любой файл ProEngineer-а, и она покажет его атрибутику и состав. В файле ProEClass.pas - весь код по работе с ProE. Это я навыдергал из рабочего проекта, поэтому тут много лишнего, но основные моменты можно посмотреть. Далее, для работы с ProE из внешних программ он требует установки системной переменной окружения PRO_COMM_MSG_EXE. В принципе, демка устанавливает переменную при запуске, но если у вас она не была раньше установлена - вам придётся сделать рестарт.
2) у dir есть удобный свитч /b (bare mode) - который позволяет получать на выходе только имена файлов. А при задании имени, советую использовать вослицательный знак, ибо такой файл всегда будет первым в списке файлов отсортированных по алфавиту.
ОтветитьУдалитьdir *.h /b > !.txt
Я очень часто использую подобную конструкцию для создания package-й из кучи .pas файлов.
п.с. А для добавления #include " можно использовать search i replace для символа перевода строки(Shift+Enter в Notepad++).
Спасибо за замечания! :)
ОтветитьУдалитьПривет! Спасибо за статью. Я пока только начинаю разбираться с PRO TOOLKIT. Подскажи у него оболочка какая-нибудь есть? Или смысл в том, чтобы написать программу на С и потом запустить ее из ProEngineera Я для себя хочу написать кнопку, ну или добавить менюшку в про_Инженер, чтобы оттуда вызывать нужные мне функции. Возможно что можно и написать dll но их я тоже писать не умею (((
ОтветитьУдалить>>> Подскажи у него оболочка какая-нибудь есть?
ОтветитьУдалитьНе ясно, о чём идёт речь.
>>> смысл в том, чтобы написать программу на С и потом запустить ее из ProEngineera
Я предлагаю заглянуть в документацию по Pro/Toolkit - там достаточно понятно описаны основные варианты интеграции (тема "Developing a Pro/TOOLKIT Application" и далее).
Затем, если вы планируете писать в Delphi, то собираете себе DLL-ку, по аналогии с тем, как я это сделал выше. Только, вероятно, вы будете подключать другой набор lib-файлов и список функций будет слегка отличаться, т.к. вам нужен плагин для Pro/E, а не внешнее управляющее приложение, как у меня.
>>> Возможно что можно и написать dll но их я тоже писать не умею (((
Наверное, сперва стоит всё же овладеть инструментом (языком и средой разработки), а уж потом решать практические задачи?
P.S. Этот пост говорит не столько о Pro/Engineer и Pro/Toolkit, сколько об использовании lib-файлов C в Delphi-приложениях.
ОтветитьУдалитьРаз вы так хорошо все расписали то не могли бы вы выложить и готовую dll-ку. С уважением за ваш труд!
ОтветитьУдалитьНе знаю, кому от этого польза будет, но вот: http://dl.getdropbox.com/u/201788/ProEDllExample.7z (680 Кб)
ОтветитьУдалитьВ любом случае польза будет, у нас вот тут идет активное обсуждение, программирования под ProEngineer, если интересно:
ОтветитьУдалитьhttp://www.sapr2k.ru/index.php?showtopic=27306&st=30
И еще раз огромное спасибо Вам
Спасибо за ссылку - не думал, что это так актуально.
ОтветитьУдалитьПосему собрал ещё небольшую демку, скачать тут. Демка представляет автономный exe-файл, который можно запустить отдельно от ProE, ткнуть ей на любой файл ProEngineer-а, и она покажет его атрибутику и состав.
ПроЕшную DLL-ку брать по предыдущей ссылке.
В демке скомпиленная прога и исходники. В папке ProToolkit лежат транслированные для Delphi заголовочники (не все, разумеется, а только те, что мне нужны были).
В файле ProEClass.pas - весь код по работе с ProE. Это я навыдергал из рабочего проекта, поэтому тут много лишнего, но основные моменты можно посмотреть.
Далее, для работы с ProE из внешних программ он требует установки системной переменной окружения PRO_COMM_MSG_EXE. В принципе, демка устанавливает переменную при запуске, но если у вас она не была раньше установлена - вам придётся сделать рестарт.
P.S. Это пример ВНЕШНЕГО приложения.
ОтветитьУдалитьЕсли вам нужен какой-нибудь встраиваемый расширитель менюшек - вам придётся курить API Toolkit-а самостоятельно.
На будущее про grep: в ДОСе (cmd) ^ - это эскейп символ, поэтому для поиска надо было делать grep "^extern" или grep ^^extern
ОтветитьУдалить;)
разрешите мой вопрос есть ли возможность автоматически узнавать и записывать (в параметры) габариты мат модели по тиу ширина длина и высота в любом месте дерева постраения сборки очень надо
ОтветитьУдалитьЯ же сказал, что ничего не понимаю в Pro/Engineer, а уж тем более в этом вашем "по тиу ширина длина и высота в любом месте дерева постраения" (ругательства-то какие страшные).
ОтветитьУдалитьЕщё раз: эта статья о том, как использовать Сишные файлы в Delphi. Всё. Точка.
Со своими проблемами идите на форумы.
P.S. Ах, да, забыл сказать: использовал WildFire 3.0.
ОтветитьУдалитьАлександр, респект за объем и качество работы! С уважением,бывший коллега.
ОтветитьУдалитьНа мой взгляд вместо ваяния программы (Потом пришлось написать небольшую программку на Delphi, которая обрезала бы скобки с параметрами (примитив, но как это сделать без программы - мне в голову не пришло).) можно было открыть файл в обычной MS Exel, как табуляцию указать символ "(", проделать необходимые действия (как то: удалить все колонки слева, добавление колонки с текстом "#include ") и сохранить как текстовый файл или в буфер.
ОтветитьУдалитьДанный метод спасал по жизни очень часто =)
Тема интересная, GunSmoker, подскажи плииз, как воспользоваться твоей демкой, вываливается ошибка инициализации ProE. Может нужна библиотечка какая, кстати, не получилось скачать файл по ссылке http://dl.getdropbox.com/u/201788/ProEDllExample.7z (680 Кб). Спасибо!
ОтветитьУдалитьСсылка на демку есть в тексте поста: http://dl.dropbox.com/u/201788/Projects/Demos/ProEDemo.7z.
ОтветитьУдалитьОшибка инициализации - смотря какая ошибка. Вообще, непонятно как проверяли, если демку скачать не смогли.
Что нужно проверить:
1. DLL находится в путях поиска программы.
2. Моя DLL собрана для ProE/WF 3. Без понятия, что там будет в других версиях.
3. Для работы с Pro/E из внешних приложений требуется установка системной переменной окружения PRO_COMM_MSG_EXE. См. SDK Pro/E для дальнейшей информации.
Александр, подскажите пожалуйста. Скачал, вашу библиотеку и демку. Теперь хочу написать подобную программу в Delphi, только для 4 ProE. Ваша библиотека подойдет для моей задачи? Так же может у вас остались какие-то материалы по Pro/E а именно по апи функциям. Какие вы использовали для получения состава сборки и атрибутов? Спасибо!
ОтветитьУдалитьЯ понятия не имею, ProE 4 я даже не видел. Я этим давно уже на занимаюсь, ProE у меня нет, доков тоже.
ОтветитьУдалитьПосмотрите по документации ProToolkit для ProE 4. Там должно быть написано, какие требования, что делать и т.п.
Будет ли работать библиотека - тоже без понятия. Если не будет - надо пересобрать с тулкитом от 4-го ProE.
Конвертация форматов COFF и OMF — это больная тема, и про UniLink не упоминают, потому что он не находится по ключевым словам COFF OMF converter. Это линкер, а нужен якобы конвертер.
ОтветитьУдалитьТем не менее, при помощи UniLink можно переделать COFF (GCC) формат в OMF (Delphi) формат. Таргеты UniLink включают в себя библиотеки C++ Builder, как динамические, так и статические. Статическую библиотеку можно в исходниках на Delphi подключить командой $L, далее, используя export; сделать все те же объявления, и на выходе получить единый исполняемый файл.