ADN Open CIS
Сообщество программистов Autodesk в СНГ

31/03/2014

Основной файл меню AutoCAD

Как было сказано в статьях "Последовательность загрузки приложений в AutoCAD" и "Последовательность загрузки приложений в AutoCAD с учетом SECURELOAD", можно организовать загрузку своих приложений, используя mnl-файлы. Чем я до поры до времени успешно и пользовался.

Но, к сожалению, не все так легко и просто.

Загрузка частичных меню вносит изменения в файл основного меню – хотим мы того или нет. Казалось бы, все просто: организовываем несколько профилей AutoCAD и в каждый загружаем разный набор частичных меню. Так? Не совсем. Если внимательно посмотреть на путь, указанный к основному файлу меню в разных профилях, то можно увидеть, что по умолчанию путь будет один и тот же.

Поэтому, если планируется тестирование нескольких файлов меню, придется либо мириться с бардаком в загруженных частичных файлах меню, либо создавать отдельный каталог, в который копировать “чистые” файлы основного меню и сопутствующие им (т.е. mnl, dll), устанавливать эту копию в качестве основного файла меню и уже потом подгружать свои файлы. Конечно, можно и третий / четвертый / пятый путь придумать, но мне было лень :) Так что я выбирал между двумя.

Меня бардак не сильно устраивает, поэтому я пошел по второму пути. Идеальным вариантом, конечно, был бы “сохранить где-то в определенном месте абсолютно чистые файлы меню”, но ситуации бывают разные. Именно для этих разных ситуаций и будем “рисовать” код.

Как всегда, перед вызовом VLIDE не помешает подумать – что и в каком виде будем обрабатывать и передавать.

Нам необходимо будет определить каталог, в котором хранятся все файлы меню – и касается это прежде всего, конечно, основного меню. Файлы стандартных частичных меню туда закидывать особого смысла лично я не вижу, поэтому про них просто забудем

Также в качестве параметра понадобится имя файла погружаемого собственного меню. Было бы неплохо также и имя группы меню, которое он импортирует, но это не критично. Для упрощения будем считать, что нам известно не только имя файла, но и имя импортируемой группы меню – так работать значительно проще.

Вроде бы все, ничего не забыл. Поехали.

Итак, прежде всего – определяем каталог, в котором будут храниться файлы основного меню и нашего тестируемого кусочка. Я обычно для таких целей использую каталог %AppData% – еще ни разу не встречал ситуации, чтобы туда нельзя было бы выполнять запись. Но просто %AppData% использовать по меньшей мере неразумно, поэтому я там организовываю подкаталог (kpblc), потом имя приложения (ну, например, LispRu). Дальше, учитывая, что меню обычно тестируется как минимум на 2-3 версиях AutoCAD, а то и вертикальные решения начинают вносить свою лепту, указываю имя приложения с учетом его версии, локализации и разрядности. Так, например, для AutoCAD 2014 x64 Eng следующим подкаталогом будет ACA2014x64En, для AutoCAD Civil 3D 2014 Rus – ACAC2014x64Ru и т.п.

Таким образом, получается, что надо создавать каталог вида %AppData%\kpblc\LispRu\ACA2014x64Ru

Все? Нет, не все. У AutoCAD могут быть разные профили (смотрим справку, ключи вызова). Так что понадобится еще добавить имя профиля AutoCAD, не забывая про недопустимые символы. Ну и для полного счастья остается добавить подкаталог menu, откуда собственно и будет все наше счастье загружаться.

Значит, результатом будет %AppData%\kpblc\LispRu\{Приложение}{Версия}{Разрядность}{Локализация}\{ИмяПрофиля}\menu

Ффух, теперь уж точно все касательно каталога. Поскольку подобная информация может потребоваться не раз и не два, возникает вопрос – то ли вычислять ее, то ли где-то хранить. Я пока предпочитаю вычислять.

Сначала вычислим путь, а уже потом будем его создавать

Вычисляем %AppData%. Казалось бы, тут ничего сложного нет:
должен вернуть все что надо. Но, к сожалению, именно должен. А не обязан. На некоторых версиях Windows вместо ожидаемого, к примеру C:\Document and Settings\DomainUser можно запросто получить C:\DOCUME~1\DOMAIN~2
На Windows 7 я такого, конечно, уже не встречал, но предусмотреть подобное поведение не помешает.

Итак, для того, чтобы получить каталог %AppData%, сначала попробуем проанализировать (getenv “AppData”) на предмет содержания в нем символов ~, и, если таковые будут обнаружены, полезем в реестр:
Код - Auto/Visual LISP: [Выделить]
  1. (defun _kpblc-get-path-appdata-current-user (/)
  2.                                             ;|
  3. * Получение пути установок, хранимых на локальной машине. (CurrUser)
  4. |;
  5.   (strcat
  6.     (_kpblc-dir-path-and-splash
  7.       (cond
  8.         ((not (wcmatch (getenv "AppData") "*~*"))
  9.          (getenv "AppData")
  10.          )
  11.         (t
  12.          (_kpblc-dir-path-and-splash
  13.            (vl-registry-read
  14.              "HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Shell Folders"
  15.              "AppData"
  16.              ) ;_ end of vl-registry-read
  17.            ) ;_ end of _kpblc-dir-path-and-splash
  18.          )
  19.         ) ;_ end of cond
  20.       ) ;_ end of _kpblc-dir-path-and-splash
  21.     "kpblc"
  22.     ) ;_ end of strcat
  23.   ) ;_ end of defun

Тут используется одна служебная функция, возвращающая каталог с гарантированным слешем на конце:
Код - Auto/Visual LISP: [Выделить]
  1. (defun _kpblc-dir-path-and-splash (path)
  2.                                   ;|
  3. * Возвращает путь со слешем в конце
  4. * Параметры вызова:
  5.      path - обрабатываемый путь
  6. * Примеры вызова: (_kpblc-dir-path-and-splash "c:\\kpblc-cad") ; "c:\\kpblc-cad\\"
  7. |;
  8.   (strcat (vl-string-right-trim "\\" path)
  9.           "\\"
  10.           ) ;_ end of strcat
  11.   ) ;_ end of defun

Вспомним, что надо получить в результате (я время от времени буду напоминать это дело):
%AppData%\kpblc\LispRu\{Приложение}{Версия}{Разрядность}{Локализация}\{ИмяПрофиля}\menu

Теперь пришла очередь {Приложение}. Я пошел по пути получения имени приложения из реестра и последующего его преобразования в более короткий и понятный вид.

Для преобразования понадобятся две функции: преобразование строки в список (обратная задача прямо тут не понадобится)
Код - Auto/Visual LISP: [Выделить]
  1. (defun _kpblc-conv-string-to-list (string separator / i)
  2.                                   ;|
  3. *    Функция разбора строки. Возвращает список либо точечную пару
  4. *    Параметры вызова:
  5.         string      разбираемая строка
  6.         separator   символ, используемый в качестве разделителя частей
  7. *    Примеры вызова:
  8. (_kpblc-conv-string-to-list "1;2;3;4;5;6" ";") ;'(1 2 3 4 5 6)
  9. (_kpblc-conv-string-to-list "1;2" ";")         ;'(1 2)
  10. *    За основу взяты уроки Евгения Елпанова по рекурсиям |;
  11.   (cond
  12.     ((= string "") nil)
  13.     ((vl-string-search separator string)
  14.      ((lambda (/ pos res)
  15.         (while (setq pos (vl-string-search separator string))
  16.           (setq res    (cons (substr string 1 pos) res)
  17.                 string (substr string (+ (strlen separator) 1 pos))
  18.                 ) ;_ end of setq
  19.           ) ;_ end of while
  20.         (reverse (cons string res))
  21.         ) ;_ end of lambda
  22.       )
  23.      )
  24.     ((wcmatch (strcase string)
  25.               (strcat "*" (strcase separator) "*")
  26.               ) ;_ end of wcmatch
  27.      ((lambda (/ pos res _str prev)
  28.         (setq pos  1
  29.               prev 1
  30.               _str (substr string pos)
  31.               ) ;_ end of setq
  32.         (while (
Получим собственно имя приложения. Читать будем из HKEY_LOCAL_MACHINE\Software\Autodesk\<И что-то тут>. Учтем по ходу дела тот факт, что, например, в Civil 3D или AutoCAD Architecture есть уникальные ключи, в которых хранится короткое имя приложения (в AutoCAD таких ключей, например, нет).
Код - Auto/Visual LISP: [Выделить]
  1. (defun _kpblc-get-acad-product-name (/ res)
  2.                                     ;|
  3. *    Возвращает имя приложения, с указанием разрядности и локализации
  4. |;
  5.   (strcat
  6.     (cond
  7.       ((setq res (vl-registry-read
  8.                    (strcat "HKEY_LOCAL_MACHINE\\" (vlax-product-key))
  9.                    "ProductNameShort"
  10.                    ) ;_ end of vl-registry-read
  11.              ) ;_ end of setq
  12.        (vl-string-subst "" " " res)
  13.        )
  14.       ((setq res (vl-registry-read
  15.                    (strcat "HKEY_LOCAL_MACHINE\\" (vlax-product-key))
  16.                    "ProductName"
  17.                    ) ;_ end of vl-registry-read
  18.              ) ;_ end of setq
  19.        (apply (function strcat)
  20.               (vl-remove-if-not
  21.                 (function (lambda (x) (wcmatch (strcase x) "*CAD,####")))
  22.                 (_kpblc-conv-string-to-list res " ")
  23.                 ) ;_ end of vl-remove-if-not
  24.               ) ;_ end of apply
  25.        )
  26.       ) ;_ end of cond
  27.     "x"
  28.     (if (wcmatch (getvar "platform") "*x64*")
  29.       "64"
  30.       "32"
  31.       ) ;_ end of if
  32.     (cond ((= (vl-registry-read
  33.                 (strcat "HKEY_LOCAL_MACHINE\\" (vlax-product-key))
  34.                 "LocaleId"
  35.                 ) ;_ end of vl-registry-read
  36.               "409"
  37.               ) ;_ end of =
  38.            "En"
  39.            )
  40.           ((=
  41.              (vl-registry-read
  42.                (strcat "HKEY_LOCAL_MACHINE\\" (vlax-product-key))
  43.                "LocaleId"
  44.                ) ;_ end of vl-registry-read
  45.              "419"
  46.              ) ;_ end of =
  47.            "Ru"
  48.            )
  49.           (t "UnKnown")
  50.           ) ;_ end of cond
  51.     ) ;_ end of strcat
  52.   ) ;_ end of defun

Вызов функции в AutoCAD 2014 x64 Eng вернет "AutoCAD2014x64Eng", Civil 3D 2014 x64 Rus – "C3D2014x64Ru" и т.п.

Теперь имя профиля. Вроде бы и тут ничего сложного быть не должно – (getvar “cprofile”) и ура. Но в имени профиля запросто могут подержаться символы, неподдерживаемые файловой системой (самый яркий пример – <<профиль без имени>>. Имя профиля содержит запрещенных символы < и >). То есть, получив имя профиля, оттуда надо убрать неправильные символы:
Код - Auto/Visual LISP: [Выделить]
  1. (defun _kpblc-get-profile-name ()
  2.                                ;|
  3. *    Замена стандартному (getvar "cprofile")
  4. |;
  5.   (vl-list->string
  6.     (vl-remove-if-not
  7.       (function (lambda (x)
  8.                   (or (<= 48 x 57)
  9.                       (<= 65 x 90)
  10.                       (

Ну и напоследок – собственно создание каталога.
Код - Auto/Visual LISP: [Выделить]
  1. (defun _kpblc-dir-create (path / tmp)
  2.                          ;|
  3. *    Гарантированное создание каталога. * Параметры вызова: path создаваемый каталог
  4. |;
  5.   (cond ((vl-file-directory-p path) path)
  6.         ((setq tmp (_kpblc-dir-create (vl-filename-directory path)))
  7.          (vl-mkdir (strcat tmp
  8.                            "\\"
  9.                            (vl-filename-base path)
  10.                            (cond ((vl-filename-extension path))
  11.                                  (t "")
  12.                                  ) ;_ end of cond
  13.                            ) ;_ end of strcat
  14.                    ) ;_ end of vl-mkdir
  15.          (if (vl-file-directory-p path)
  16.            path
  17.            ) ;_ end of if
  18.          )
  19.         ) ;_ end of cond
  20.   ) ;_ end of defun

Теперь в каталог, созданный через
Код - Auto/Visual LISP: [Выделить]
  1. (_kpblc-dir-create
  2.   (strcat (_kpblc-get-path-appdata-current-user)
  3.           "\\LispRu\\"
  4.           (_kpblc-get-acad-product-name)
  5.           "\\"
  6.           (_kpblc-get-profile-name)
  7.           "\\menu"
  8.           ) ;_ end of strcat
  9.   ) ;_ end of _kpblc-dir-create
надо будет копировать основной файл меню (который придется вычислять), его сопутствующие файлы (dll / mnl),и загружаемые файлы частичных меню (не забывая про те же dll, mnl и удаление – на всякий случай – mnr и mnc).

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

Т.е. перед выполнением следующих шагов надо
  • получить текущее значение системной переменной WSCURRENT;
  • сохранить рабочее пространство;
  • установить новый файл основного меню;
  • восстановить WSCURRENT.


Позволю себе пофилонить и полностью все проверки не прописывать (ну, например, я принципиально "забыл" о существовании AutoCAD версий до 2005 включительно).

Для работы понадобятся еще 3 функции – преобразования указателей в ename, в vla и vla в список. Конечно, можно поизвращаться и без них обойтись, но моя практика показала необходимость подобных функций:

Код - Auto/Visual LISP: [Выделить]
  1. (defun _kpblc-conv-ent-to-ename (ent_value / _lst)
  2.                                 ;|
  3. *    Функция преобразования полученного значения в ename
  4. *    Параметры вызова:
  5.   ent_value значение, которое надо преобразовать в примитив. Может быть именем примитива, vla-указателем или просто
  6.             списком.
  7. *    Если не принадлежит ни одному из указанных типов, возвращается nil
  8. *    Примеры вызова:
  9. (_kpblc-conv-ent-to-ename (entlast))
  10. (_kpblc-conv-ent-to-ename (vlax-ename->vla-object (entlast)))
  11. |;
  12.   (cond ((= (type ent_value) 'vla-object)
  13.          (vlax-vla-object->ename ent_value)
  14.          )
  15.         ((= (type ent_value) 'ename) ent_value)
  16.         ((and (= (type ent_value) 'str)
  17.               (handent ent_value)
  18.               (entget (handent ent_value))
  19.               ) ;_ end of and
  20.          (handent ent_value)
  21.          )
  22.         ((and (= (type ent_value) 'str)
  23.               (handent ent_value)
  24.               (tblobjname "style" ent_value)
  25.               ) ;_ end of and
  26.          (tblobjname "style" ent_value)
  27.          )
  28.         ((and (= (type ent_value) 'str)
  29.               (handent ent_value)
  30.               (tblobjname "dimstyle" ent_value)
  31.               ) ;_ end of and
  32.          (tblobjname "dimstyle" ent_value)
  33.          )
  34.         ((and (= (type ent_value) 'str)
  35.               (handent ent_value)
  36.               (tblobjname "block" ent_value)
  37.               ) ;_ end of and
  38.          (tblobjname "block" ent_value)
  39.          )
  40.         ((and (= (type ent_value) 'list) (cdr (assoc -1 ent_value)))
  41.          (cdr (assoc -1 ent_value))
  42.          )
  43.         (t nil)
  44.         ) ;_ end of cond
  45.   ) ;_ end of defun
  46.  
  47. (defun _kpblc-conv-ent-to-vla (ent_value / res)
  48.                               ;|
  49. *    Функция преобразования полученного значения в vla-указатель.
  50. *    Параметры вызова:
  51.         ent_value значение, которое надо преобразовать в указатель. Может
  52.                   быть именем примитива, vla-указателем или просто
  53.                   списком. Если не принадлежит ни одному из указанных типов,
  54.                   возвращается nil
  55. *    Примеры вызова:
  56. (_kpblc-conv-ent-to-vla (entlast))
  57. (_kpblc-conv-ent-to-vla (vlax-ename->vla-object (entlast)))
  58. |;
  59.   (cond ((= (type ent_value) 'vla-object) ent_value)
  60.         ((= (type ent_value) 'ename)
  61.          (vlax-ename->vla-object ent_value)
  62.          )
  63.         ((setq res (_kpblc-conv-ent-to-ename ent_value))
  64.          (vlax-ename->vla-object res)
  65.          )
  66.         ) ;_ end of cond
  67.   ) ;_ end of defun
  68.  
  69. (defun _kpblc-conv-vla-to-list (value / res)
  70.                                ;|
  71. *    Преобразовывает vlax-variant или vlax-safearray в список.
  72. |;
  73.   (cond ((listp value)
  74.          (mapcar (function _kpblc-conv-vla-to-list) value)
  75.          )
  76.         ((= (type value) 'variant)
  77.          (_kpblc-conv-vla-to-list (vlax-variant-value value))
  78.          )
  79.         ((= (type value) 'safearray)
  80.          (if (>= (vlax-safearray-get-u-bound value 1) 0)
  81.            (_kpblc-conv-vla-to-list (vlax-safearray->list value))
  82.            ) ;_ end of if
  83.          )
  84.         ((and (member (type value) (list 'ename 'str 'vla-object))
  85.               (= (type (_kpblc-conv-ent-to-vla value)) 'vla-object)
  86.               (vlax-property-available-p
  87.                 (_kpblc-conv-ent-to-vla value)
  88.                 'count
  89.                 ) ;_ end of vlax-property-available-p
  90.               ) ;_ end of and
  91.          (vlax-for sub (_kpblc-conv-ent-to-vla value)
  92.            (setq res (cons sub res))
  93.            ) ;_ end of vlax-for
  94.          )
  95.         (t value)
  96.         ) ;_ end of cond
  97.   ) ;_ end of defun

В результате получается:
Код - Auto/Visual LISP: [Выделить]
  1. (defun _kpblc-menu-update2 (/ loc_dir main_menu)
  2.                            ;|
  3. *    Обновление файла меню
  4. |;
  5.   (setq loc_dir   (_kpblc-dir-create
  6.                     (strcat (_kpblc-get-path-appdata-current-user)
  7.                             "\\LispRu\\"
  8.                             (_kpblc-get-acad-product-name)
  9.                             "\\"
  10.                             (_kpblc-get-profile-name)
  11.                             "\\menu"
  12.                             ) ;_ end of strcat
  13.                     ) ;_ end of _kpblc-dir-create
  14.         main_menu (vla-get-menufile
  15.                     (vla-get-files
  16.                       (vla-get-preferences (vlax-get-acad-object))
  17.                       ) ;_ end of vla-get-files
  18.                     ) ;_ end of vla-get-menufile
  19.         ) ;_ end of setq
  20.   (if (/= (_kpblc-dir-path-and-splash
  21.             (strcase (vl-filename-directory main_menu))
  22.             ) ;_ end of _kpblc-dir-path-and-splash
  23.           (_kpblc-dir-path-and-splash (strcase loc_dir))
  24.           ) ;_ end of /=
  25.     (progn
  26.       ;; Каталоги не совпадают. Надо копировать и устанавливать новый основной файл меню
  27.       ;; не забывая про все остальное
  28.       ;; Удаляем старые варианты,- вдруг они там есть
  29.       (foreach file (vl-directory-files
  30.                       loc_dir
  31.                       (strcat (vl-filename-base main_menu) ".*")
  32.                       1
  33.                       ) ;_ end of vl-directory-files
  34.         (vl-file-delete
  35.           (strcat (_kpblc-dir-path-and-splash loc_dir) file)
  36.           ) ;_ end of vl-file-delete
  37.         ) ;_ end of foreach
  38.       ;; Теперь копируем из текущего положения основного файла меню все необходимое
  39.       (foreach file
  40.                (vl-remove-if-not
  41.                  (function
  42.                    (lambda (x)
  43.                      (wcmatch
  44.                        (strcase
  45.                          (vl-string-trim "." (vl-filename-extension x))
  46.                          ) ;_ end of strcase
  47.                        "CUI,CUIX,MNL,MNS,MNU,DLL,MNL"
  48.                        ) ;_ end of wcmatch
  49.                      ) ;_ end of lambda
  50.                    ) ;_ end of function
  51.                  (vl-directory-files
  52.                    (vl-filename-directory main_menu)
  53.                    (strcat (vl-filename-base main_menu) ".*")
  54.                    1
  55.                    ) ;_ end of vl-directory-files
  56.                  ) ;_ end of vl-remove-if-not
  57.         (vl-file-copy
  58.           (strcat (_kpblc-dir-path-and-splash
  59.                     (vl-filename-directory main_menu)
  60.                     ) ;_ end of _kpblc-dir-path-and-splash
  61.                   file
  62.                   ) ;_ end of strcat
  63.           (strcat (_kpblc-dir-path-and-splash loc_dir) file)
  64.           ) ;_ end of vl-file-copy
  65.         ) ;_ end of foreach
  66.       ;; Теперь получаем список уже загруженных файлов частичных меню.
  67.       ;; Потом мы их снова загрузим
  68.       ;; Получим общий список и из него исключим файл основного меню,
  69.       ;; и файлы меню в loc_dir
  70.       (setq partial_menus
  71.              (vl-remove-if
  72.                (function
  73.                  (lambda (x)
  74.                    (or
  75.                      (= (strcase (cdr (assoc "name" x)))
  76.                         (strcase (vl-filename-base main_menu))
  77.                         ) ;_ end of =
  78.                      (wcmatch (strcase (vl-filename-directory
  79.                                          (cdr (assoc "name" x))
  80.                                          ) ;_ end of vl-filename-directory
  81.                                        ) ;_ end of strcase
  82.                               (strcat (strcase loc_dir "*"))
  83.                               ) ;_ end of wcmatch
  84.                      ) ;_ end of or
  85.                    ) ;_ end of lambda
  86.                  ) ;_ end of function
  87.                (mapcar
  88.                  (function
  89.                    (lambda (x)
  90.                      (list
  91.                        (cons "name" (vla-get-name x))
  92.                        (cons "file" (vla-get-menufilename x))
  93.                        ) ;_ end of list
  94.                      ) ;_ end of lambda
  95.                    ) ;_ end of function
  96.                  (_kpblc-conv-vla-to-list
  97.                    (vla-get-menugroups (vlax-get-acad-object))
  98.                    ) ;_ end of _kpblc-conv-vla-to-list
  99.                  ) ;_ end of mapcar
  100.                ) ;_ end of vl-remove-if
  101.             ) ;_ end of setq
  102.       ;; Сохраняем текущее рабочее пространство
  103.       (if (setq wscurrent (getvar "wscurrent"))
  104.         (command "_.wssave" wscurrent "_y")
  105.         ) ;_ end of if
  106.       ;; Меняем основной файл меню
  107.       (vla-load
  108.         (vla-get-menugroups
  109.           (vla-get-activedocument (vlax-get-acad-object))
  110.           ) ;_ end of vla-get-menugroups
  111.         (findfile
  112.           (strcat
  113.             (_kpblc-dir-path-and-splash loc_dir)
  114.             (car
  115.               (vl-remove-if
  116.                 (function (lambda (x) (wcmatch (strcase x) "*.bak.cui*"))
  117.                           ) ;_ end of function
  118.                 (vl-directory-files
  119.                   loc_dir
  120.                   (strcat (vl-filename-base main_menu) ".cui*")
  121.                   ) ;_ end of vl-directory-files
  122.                 ) ;_ end of vl-remove-if
  123.               ) ;_ end of car
  124.             ) ;_ end of strcat
  125.           ) ;_ end of findfile
  126.         :vlax-true
  127.         ) ;_ end of vla-load
  128.       (foreach item partial_menus
  129.         (vl-catch-all-apply
  130.           (function
  131.             (lambda ()
  132.               (vla-load (vla-get-menugroups
  133.                           (vla-get-activedocument (vlax-get-acad-object))
  134.                           ) ;_ end of vla-get-menugroups
  135.                         (cdr (assoc "file" item))
  136.                         :vlax-false
  137.                         ) ;_ end of vla-load
  138.               ) ;_ end of lambda
  139.             ) ;_ end of function
  140.           ) ;_ end of vl-catch-all-apply
  141.         ) ;_ end of foreach
  142.       ;; Для ExpressTools немного "своя" доработка
  143.       (if (findfile "acettest.fas")
  144.         (progn (load "acettest.fas")
  145.                (vla-sendcommand
  146.                  (vla-get-activedocument (vlax-get-acad-object))
  147.                  "_expresstools "
  148.                  ) ;_ end of vla-sendcommand
  149.                ) ;_ end of progn
  150.         ) ;_ end of if
  151.       ;; Восстанавливаем рабочее пространство
  152.       (if wscurrent
  153.         (progn (setvar "wscurrent" wscurrent)
  154.                (command "_.wssave" wscurrent "_y")
  155.                ) ;_ end of progn
  156.         ) ;_ end of if
  157.       ) ;_ end of progn
  158.     ) ;_ end of if
  159.   ) ;_ end of defun
К сожалению, тщательное тестирование кода показало, что попытки выполнения строк наподобие
Код - Auto/Visual LISP: [Выделить]
  1. (vla-load (vla-get-menugroups (vla-get-activedocument
  2.     (vlax-get-acad-object))) new_main_menu :vlax-false)
в 64-разрядных AutoCAD версий 2009, 2013, 2014 (в остальных просто не пробовал) выполнение подобной конструкции (vla-load … :vlax-true) приводит к ошибке ядра! Отловить подобное лиспом невозможно.

Поэтому пришлось вспомнить старые командные методы и выполнять обычную команду
Код - Auto/Visual LISP: [Выделить]
  1. (command
  2.     "_.menu" new_main_menu)
При этом частичные меню уже загружены и контролировать их уже не требуется, что прилично упрощает код. Мне кажется, это достаточно адекватная плата за применение "немодных" командных методов.

На основе материалов статей
Продолжаю войну с меню… и Смена файла основного меню

Обсуждение: http://adn-cis.org/forum/index.php?topic=646

Опубликовано 31.03.2014
Отредактировано 21.01.2015 в 21:40:55