Работа с неактивным документом

Подавляющее большинство лисп-функций, показываемых на форумах и сайтах, работают с текущим документом. Как правило, этого достаточно. Но что делать, если надо обрабатывать несколько документов? Здесь я хотел бы рассмотреть некоторые вопросы, связанные именно с обработкой неактивного документа.

В текущем (активном) документе программисту доступны все возможности 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, сначала определим версию:

(defun _lispru-acad-version ()
;|
*    Возвращает номер сборки AutoCAD'a. Для 2005 вернет 16.1, для 2006 - 16.2
* и т.д.
|;

(atof (getvar "acadver"))
) ;_ end of defun

После получения версии запущенного AutoCAD можно уже и пытаться вызвать ObjectDBX:

(defun _lispru-odbx (/)
                    ;|
*    функция возвращает интерфейс 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

Ну хорошо, сам интерфейс получен. А дальше-то что? А дальше вот что:

_$ (setq odbx (_lispru-odbx))
#<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-open odbx filename)

А вот с обработкой и сохранением уже не все так очевидно.

  1. Не допускается применение интерактивных и командных методов. Только vla и ename (да и то, последнее только в случае крайней необходимости).
  2. ObjectDBX при открытии файла блокирует его. Доступ в файлу возможен только при условии монопольного к нему доступа. Если кто-то этот файл уже открыл (например, сосед), то ObjectDBX этот файл не то что не обработает, а даже не откроет.
  3. Несмотря на заявленную поддержку метода Save, этот метод не работает, приходится использовать SaveAs.
  4. Сохранение файла возможно только в текущей версии, без вариантов.
  5. Файл можно закрыть и без сохранения изменений. Для этого достаточно выполнить (vlax-release-object) к указателю на интерфейс.
  6. Для файла, открытого через ObjectDBX, невозможно добраться до состава внешних ссылок. С внешними ссылками внутри ObjectDBX вообще надо быть очень осторожными, постоянно проверяя тип ссылки – вставленная она или наложенная (бывают случаи, когда перечисляются все, независимо от уровня вложенности).
  7. С получением путей вставленных растров тоже могут быть проблемы.
  8. В файле, открытом через ObjectDBX, невозможно выполнить проверку целостности: vla-auditinfo работает только в текущем документе.

Несмотря на эти ограничения (кстати, возможно, я перечислил не все!), механизм достаточно удобен, особенно при аккуратном с ним обращении: можно получить список описаний блоков стороннего документа, а потом через CopyObjects скопировать их в текущий. Или наоборот. Можно привести сотни файлов в стандартный вид, не особо беспокоясь о потерях времени: при открытии файла до 95% времени тратится на его регенерацию. Можно… В общем, сделать можно много.

Осталось только одно: запомнить, что сохраняется файл только в текущей версии и только через метод SaveAs:

(vla-saveas odbx FileName)

Естественно, что, имея один указатель на ObjectDBX, можно открывать далеко не один и не два файла (хотя лично я такого не делаю. Я предпочту мириться с потерями памяти, но не с потерями работы: в версии 2005 и 2006, по-моему, использование одного указателя иногда приводило к странным зависаниям. То есть сейчас ситуация у меня такова: создал указатель – открыл файл – обработал файл – сохранил файл – “отрелизил” указатель – обнулил указатель и все сначала). В конце всеобщей обработки надо не забыть выполнить vlax-release-object, ну и, для гарантии, обнулить переменную:

(setq odbx nil)

Примеры использования, я думаю, будут. Но позже.

Ссылки для скачивания:
_lispru-acad-version.lsp
_lispru-odbx.lsp

Размещено в Код LISP, Новости, Функции LISP · Метки: ,



Комментарии

Есть 8 коммент. к “Работа с неактивным документом”
  1. TararykovDG пишет:

    Здравствуйте Алексей, подскажите пожалуйста, а возможно ли используя интерфейс IAxDbDocument (или ObjectDBX) при работе с неактивным документом, создать в этом документе какой-либо примитив. Если возможно, то подскажите как. У меня лично не получилось.

    (vla-AddCircle odbx (vlax-3D-point ‘(0 0 0)) 100)
    ; Ошибка: ActiveX Server возвратил ошибку: неизвестное имя: AddCircle

    Если посмотреть приведенный вами список доступных методов и свойств, полученных следующим образом:
    (vlax-dump-Object odbx t),
    То там есть всего лишь 8 доступных методов, и ни один из них не позволяет создать примитив в неактивном документе.

  2. Кулик Алексей aka kpblc пишет:

    Все верно. Сравните:
    (vla-AddCircle odbx (vlax-3D-point ‘(0 0 0)) 100)
    и
    (vla-AddCircle (vla-get-ModelSpace odbx) (vlax-3D-point ‘(0 0 0)) 100)
    Примитив не может быть создан в документе. Создаваемый примитив располагается в каком-то пространстве – модели, листа, блока…

  3. Кулик Алексей aka kpblc пишет:

    Кстати о примерах. Вот, не далее как сегодня нарисовалось: https://docs.google.com/Doc?docid=0AXNsAeY1XnTrZGY3cGJtcTlfNjNodG12ZHRkOQ&hl=ru

  4. Кулик Алексей aka kpblc пишет:

    Сегодня пришлось еще несколько времени потратить на использование ObjectDBX :)
    Несложно выяснить, что у ObjectDBX есть два интересных метода – DxfIn и DxfOut. С первым пока не воевал, а вот второй оказался, как бы сказать… В общем, это далеко не то же, что и SaveAs для активного документа :(
    DxfOut действительно создает dxf-файл, но вот формат этого dxf не выбрать и не назначить. Сохраняется только в “нативной” версии. Так что для нестандартных ситуаций, похоже, придется изобретать нестандартные решения…

  5. Do$ пишет:

    Алексей, большое спасибо! Наконец и мне пригодилось!

  6. Кулик Алексей aka kpblc пишет:

    Да не за что :) Я тут тоже с такой задачкой столкнулся – мама не горюй. Если появится гарантированно работающее решение, поделюсь ;)

  7. Кулик Алексей aka kpblc пишет:

    Хотелось бы сделать небольшое дополнение.
    Касается это возможности “открытия через ObjectDBX заблокированного файла”. На данный момент я через vl-file-systime проверяю доступность файла на открытие (функция возвращает nil, если файл кем-то открыт). Если файл недоступен, то я его копирую в %temp% и открываю уже оттуда.
    Не очень изящно, зато работает. Пока, по крайней мере…

Трэкбэки

Узнайте, что другие говорят про эту заметку...
  1. [...] его? Частично этот вопрос я поднимал в статье Работа с неактивным документом, но там был вопрос именно с чтением уже существующего [...]



Поделитесь своим мнением


Я не робот.