Работа с неактивным документом
Подавляющее большинство лисп-функций, показываемых на форумах и сайтах, работают с текущим документом. Как правило, этого достаточно. Но что делать, если надо обрабатывать несколько документов? Здесь я хотел бы рассмотреть некоторые вопросы, связанные именно с обработкой неактивного документа.
В текущем (активном) документе программисту доступны все возможности lisp’a – это и командные методы, и интерактивное формирование наборов примитивов, и ename-методы, и activex. Но стоит только заикнуться об обработке неактивного документа, как перечень возможных средств моментально сужается до состояния “ActiveX и практически больше ничего”.
Прежде чем приступать к дальнейшим рассуждениям, напомню, что AutoCAD должен быть переведен в многодокументный режим (системная переменная SDI равна 0).
Первое желание – написать scr-файл (возможно, программно) по алгоритму: открыть файл, загрузить лисп, выполнить лисп, сохранить файл, открыть следующий и т.д.; и запустить его на выполнение. Чем плох подобный подход? Ну, хотя бы тем, что обрабатываемые файлы могут быть очень тяжелыми, времени на их открытие и регенерацию может уходить немеряно. Внутри файлов могут встретиться, например, прокси-объекты. Или будет запрос на поиск shx-файла (о том, как от этого запроса избавиться, поговорим позже). Или еще чего-то… Короче, придется сидеть рядом с компьютером и ждать, пока он не закончит выполнение скрипта. Не, это скучно.
Второе движение – выполнить программное открытие нужного dwg-файла, обработать его и закрыть. Все хорошо, да только вот проблема – такой подход практически никогда не работает. Открытие файла (хоть через команду _.open с установленной filedia = 0, хоть через vla-open) почти гарантированно приводит к активации только что открытого файла. Почему “почти”? Потому, что в некоторых условиях такого не происходит. Насколько я помню, этого не происходило в AutoCAD 2005 (чистом AutoCAD, а не входящем в состав, например, ADT). Но требуется-то разработать функцию, которая будет гарантированно корректно работать в любых условиях! Поэтому такой вариант тоже не годится.
Третий вариант – залезьть достаточно глубоко в дебри AutoCAD и открывать файл dwg без активизации. На нем я и хотел бы остановиться.
Скажу честно – идея не совсем моя, я только дорабатывал ее. Лично я считаю авторами и реализаторами идеи двух человек - Н.Н.Полещука и Fatty (он же fixo, он же Олег jr.). Суть метода состоит в том, чтобы обратиться к интерфейсу IAxDbDocument (или ObjectDBX) и с его помощью уже и обрабатывать dwg.
ObjectDBX появился не так уж и давно, и его регистрация в начале его существования отличалась от той, что используется в версиях последнего времени.
Поскольку вызов ObjectDBX напрямую зависит от версии AutoCAD, сначала определим версию:
;|
* Возвращает номер сборки AutoCAD'a. Для 2005 вернет 16.1, для 2006 - 16.2
* и т.д.
|;
(atof (getvar "acadver"))
) ;_ end of defun
После получения версии запущенного AutoCAD можно уже и пытаться вызвать ObjectDBX:
;|
* функция возвращает интерфейс IAxDbDocument (для работы с файлами DWG без
* их открытия). Если интерфейс не поддерживается, возвращает nil. Проверено
* на ACAD 2002, 2004, 2005, 2006, 2007, 2008, 2010
* Автор - Fatty aka Олег jr. Моего только адаптация под общую систему и
* переименование
* Параметры вызова:
* нет
* Примеры вызова:
(_lispru-odbx)
|;
(cond
((< (_lispru-acad-version) 15.06)
(alert
"ObjectDBX method not applicable\nin this AutoCAD version"
) ;_ end of KPBLC-MSG-ALERT
nil
)
((= (fix (_lispru-acad-version)) 15)
(if (not (vl-registry-read
"HKEY_CLASSES_ROOT\\ObjectDBX.AxDbDocument\\CLSID"
) ;_ end of vl-registry-read
) ;_ end of not
(startapp "regsvr32.exe"
(strcat "/s \"" (findfile "axdb15.dll") "\"")
) ;_ end of startapp
) ;_ end of if
(vla-getinterfaceobject
(vlax-get-acad-object)
"ObjectDBX.AxDbDocument"
) ;_ end of vla-getinterfaceobject
)
(t
(vla-getinterfaceobject
(vlax-get-acad-object)
(strcat "ObjectDBX.AxDbDocument." (itoa (fix (_lispru-acad-version))))
) ;_ end of vla-getinterfaceobject
)
) ;_ end of cond
) ;_ end of defun
Ну хорошо, сам интерфейс получен. А дальше-то что? А дальше вот что:
#<VLA-OBJECT IAxDbDocument 000000003bcdea60>
_$ (vlax-dump-Object odbx t)
; IAxDbDocument: IAxDbDocument Interface
; Property values:
; Application (RO) = Ошибка
; Blocks (RO) = #<VLA-OBJECT IAcadBlocks 000000003bd3d6e8>
; Database (RO) = #<VLA-OBJECT IAcadDatabase 000000003bb04488>
; Dictionaries (RO) = #<VLA-OBJECT IAcadDictionaries 000000003bd3d928>
; DimStyles (RO) = #<VLA-OBJECT IAcadDimStyles 000000003bd3d9b8>
; ElevationModelSpace = 0.0
; ElevationPaperSpace = 0.0
; FileDependencies (RO) = #<VLA-OBJECT IAcadFileDependencies 000000003bdf6878>
; Groups (RO) = #<VLA-OBJECT IAcadGroups 000000003bd3da48>
; Layers (RO) = #<VLA-OBJECT IAcadLayers 000000003bd3dad8>
; Layouts (RO) = #<VLA-OBJECT IAcadLayouts 000000003bd3db68>
; Limits = (0.0 0.0 12.0 9.0)
; Linetypes (RO) = #<VLA-OBJECT IAcadLineTypes 000000003bd3dbf8>
; Materials (RO) = #<VLA-OBJECT IAcadMaterials 000000003bd3dc88>
; ModelSpace (RO) = #<VLA-OBJECT IAcadModelSpace 000000003bb13408>
; Name = ""
; PaperSpace (RO) = #<VLA-OBJECT IAcadPaperSpace 000000003bb134a8>
; PlotConfigurations (RO) = #<VLA-OBJECT IAcadPlotConfigurations 000000003bd3dd18>
; Preferences (RO) = #<VLA-OBJECT IAcadDatabasePreferences 000000003bdf6838>
; RegisteredApplications (RO) = #<VLA-OBJECT IAcadRegisteredApplications 000000003bd3dda8>
; SectionManager (RO) = Ошибка
; SummaryInfo (RO) = #<VLA-OBJECT IAcadSummaryInfo 000000003bce2f18>
; TextStyles (RO) = #<VLA-OBJECT IAcadTextStyles 000000003bd3de38>
; UserCoordinateSystems (RO) = #<VLA-OBJECT IAcadUCSs 000000003bd3dec8>
; Viewports (RO) = #<VLA-OBJECT IAcadViewports 000000003bd3df58>
; Views (RO) = #<VLA-OBJECT IAcadViews 000000003bd3dfe8>
; Methods supported:
; CopyObjects (3)
; DxfIn (2)
; DxfOut (3)
; HandleToObject (1)
; ObjectIdToObject (1)
; ObjectIdToObject32 (1)
; Open (2)
; Save ()
; SaveAs (2)
T
_$
На данный момент наиболее интересны методы Open и SaveAs (а не Save, как это ни парадоксально). Пришло время поговорить о том, как работает ObjectDBX, какие у него ограничения и как их обходить.
Вопрос с открытием файла можно даже не поднимать:
А вот с обработкой и сохранением уже не все так очевидно.
- Не допускается применение интерактивных и командных методов. Только vla и ename (да и то, последнее только в случае крайней необходимости).
- ObjectDBX при открытии файла блокирует его. Доступ в файлу возможен только при условии монопольного к нему доступа. Если кто-то этот файл уже открыл (например, сосед), то ObjectDBX этот файл не то что не обработает, а даже не откроет.
- Несмотря на заявленную поддержку метода Save, этот метод не работает, приходится использовать SaveAs.
- Сохранение файла возможно только в текущей версии, без вариантов.
- Файл можно закрыть и без сохранения изменений. Для этого достаточно выполнить (vlax-release-object) к указателю на интерфейс.
- Для файла, открытого через ObjectDBX, невозможно добраться до состава внешних ссылок. С внешними ссылками внутри ObjectDBX вообще надо быть очень осторожными, постоянно проверяя тип ссылки – вставленная она или наложенная (бывают случаи, когда перечисляются все, независимо от уровня вложенности).
- С получением путей вставленных растров тоже могут быть проблемы.
- В файле, открытом через ObjectDBX, невозможно выполнить проверку целостности: vla-auditinfo работает только в текущем документе.
Несмотря на эти ограничения (кстати, возможно, я перечислил не все!), механизм достаточно удобен, особенно при аккуратном с ним обращении: можно получить список описаний блоков стороннего документа, а потом через CopyObjects скопировать их в текущий. Или наоборот. Можно привести сотни файлов в стандартный вид, не особо беспокоясь о потерях времени: при открытии файла до 95% времени тратится на его регенерацию. Можно… В общем, сделать можно много.
Осталось только одно: запомнить, что сохраняется файл только в текущей версии и только через метод SaveAs:
Естественно, что, имея один указатель на ObjectDBX, можно открывать далеко не один и не два файла (хотя лично я такого не делаю. Я предпочту мириться с потерями памяти, но не с потерями работы: в версии 2005 и 2006, по-моему, использование одного указателя иногда приводило к странным зависаниям. То есть сейчас ситуация у меня такова: создал указатель – открыл файл – обработал файл – сохранил файл – “отрелизил” указатель – обнулил указатель и все сначала). В конце всеобщей обработки надо не забыть выполнить vlax-release-object, ну и, для гарантии, обнулить переменную:
Примеры использования, я думаю, будут. Но позже.
Ссылки для скачивания:
_lispru-acad-version.lsp
_lispru-odbx.lsp
Материалы для проектирования, работы и разработки (и не только в AutoCAD)
Здравствуйте Алексей, подскажите пожалуйста, а возможно ли используя интерфейс IAxDbDocument (или ObjectDBX) при работе с неактивным документом, создать в этом документе какой-либо примитив. Если возможно, то подскажите как. У меня лично не получилось.
(vla-AddCircle odbx (vlax-3D-point ‘(0 0 0)) 100)
; Ошибка: ActiveX Server возвратил ошибку: неизвестное имя: AddCircle
Если посмотреть приведенный вами список доступных методов и свойств, полученных следующим образом:
(vlax-dump-Object odbx t),
То там есть всего лишь 8 доступных методов, и ни один из них не позволяет создать примитив в неактивном документе.
Все верно. Сравните:
(vla-AddCircle odbx (vlax-3D-point ‘(0 0 0)) 100)
и
(vla-AddCircle (vla-get-ModelSpace odbx) (vlax-3D-point ‘(0 0 0)) 100)
Примитив не может быть создан в документе. Создаваемый примитив располагается в каком-то пространстве – модели, листа, блока…
Кстати о примерах. Вот, не далее как сегодня нарисовалось: https://docs.google.com/Doc?docid=0AXNsAeY1XnTrZGY3cGJtcTlfNjNodG12ZHRkOQ&hl=ru
Сегодня пришлось еще несколько времени потратить на использование ObjectDBX

Несложно выяснить, что у ObjectDBX есть два интересных метода – DxfIn и DxfOut. С первым пока не воевал, а вот второй оказался, как бы сказать… В общем, это далеко не то же, что и SaveAs для активного документа
DxfOut действительно создает dxf-файл, но вот формат этого dxf не выбрать и не назначить. Сохраняется только в “нативной” версии. Так что для нестандартных ситуаций, похоже, придется изобретать нестандартные решения…
Алексей, большое спасибо! Наконец и мне пригодилось!
Да не за что
Я тут тоже с такой задачкой столкнулся – мама не горюй. Если появится гарантированно работающее решение, поделюсь
Хотелось бы сделать небольшое дополнение.
Касается это возможности “открытия через ObjectDBX заблокированного файла”. На данный момент я через vl-file-systime проверяю доступность файла на открытие (функция возвращает nil, если файл кем-то открыт). Если файл недоступен, то я его копирую в %temp% и открываю уже оттуда.
Не очень изящно, зато работает. Пока, по крайней мере…