Как передать VLA-объект (ActiveX) объект в самописную LISP-фунцию?

Автор Тема: Как передать VLA-объект (ActiveX) объект в самописную LISP-фунцию?  (Прочитано 4689 раз)

0 Пользователей и 1 Гость просматривают эту тему.

Тема содержит сообщение с Решением. Нажмите здесь чтобы посмотреть его.

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Здравствуйте!
У меня не получилось напрямую передать vla-объект в лисп-функцию. Это логично - объект такого типа не имеет аналога в DXF. Как тогда можно обойти эту проблему?
Я рассматривал вариант передачи Handle или ObjectID объекта. Но чтобы потом получить ObjectId, надо иметь ссылку на базу данных, из которой получен объект. Каким образом передавать данные о БД? У неё, вроде как, нет какого-то уникального идентификатора. Пока что-то ничего придумать не могу. Может быть, у вас есть идеи на этот счёт?
Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.DatabaseServices;
  2. using Autodesk.AutoCAD.Runtime;
  3.  
  4. namespace ActiveXToDotNet
  5. {
  6.     public class LispFunctions
  7.     {
  8.         [LispFunction("ActiveXToDotNet")]
  9.         public static void ActiveXToDotNetFunc(ResultBuffer rb)
  10.         {
  11.  
  12.         }
  13.     }
  14. }

Код - Auto/Visual Lisp [Выбрать]
  1. (setq vla-obj (vlax-ename->vla-object (car (entsel "\nSelect object:"))))
  2.  
  3. ;; (ActiveXToDotNet vla-obj) <- так не работает
  4.  
  5. (ActiveXToDotNet (vla-get-Handle vla-obj)) ;; Это работает, но что потом с этим хендлом делать в .NET?

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Как тогда можно обойти эту проблему?
Никак.
Но чтобы потом получить ObjectId, надо иметь ссылку на базу данных, из которой получен объект. Каким образом передавать данные о БД?
ObjectId.Database - даёт базу данных. Но я так понимаю, что вопрос не в этом. lisp работает только с активной базой данных, загруженной в редактор AutoCAD, так что нет смысла изобретать велосипед.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
lisp работает только с активной базой данных
Даже при использовании такого механизма?

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Даже при использовании такого механизма?
Нет. Но оно тебе надо?
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Это не мне, это в помощь нашим коллегам-лиспописателям :)

Отмечено как Решение Дмитрий Загорулькин 03-10-2019, 15:15:34

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Это не мне, это в помощь нашим коллегам-лиспописателям :)
Насколько я помню если передавать ENAME, то AutoCAD сам понимает к какой базе это относится. Причем передаваемый из lisp ENAME преобразуется автоматически в ObjectId в ResultBuffer.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Так и есть:
Код - C# [Выбрать]
  1. using System;
  2. using Autodesk.AutoCAD.Runtime;
  3. using Autodesk.AutoCAD.ApplicationServices;
  4. using Autodesk.AutoCAD.DatabaseServices;
  5. using Autodesk.AutoCAD.Geometry;
  6. using Autodesk.AutoCAD.EditorInput;
  7.  
  8. // This line is not mandatory, but improves loading performances
  9. [assembly: CommandClass(typeof(Rivilis.LispToNet))]
  10.  
  11. namespace Rivilis
  12. {
  13.   public class LispToNet
  14.   {
  15.     [LispFunction("LispToNet")]
  16.     public ResultBuffer LispToNetHandler(ResultBuffer args)
  17.     {
  18.       Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
  19.       foreach (var val in args)
  20.       {
  21.         if (val.TypeCode == 5006)
  22.         {
  23.           ObjectId id = (ObjectId)val.Value;
  24.           ed.WriteMessage($"\nid={id} Database={id.Database.Filename}");
  25.         }
  26.       }
  27.       return null;
  28.     }
  29.  
  30.   }
  31. }
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Спасибо! Осталось только проверить будет ли это работать при открытии документа через ObjectDBX из лиспа

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Насколько я помню если передавать ENAME, то AutoCAD сам понимает к какой базе это относится.
Алексей Кулик сказал, что при использовании ENAME в чертеже, открытом через ObjectDBX, могут быть глюки того плана, что вместо передаваемого объекта из открытого в фоне чертежа, берётся объект в текущем чертеже. Возможно, ENAME не уникальны в разных чертежах в рамках сессии? Знать бы как работает этот внутренний механизм лисповой идентификации объектов...

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Возможно, ENAME не уникальны в разных чертежах в рамках сессии?
Сложный вопрос, но мне кажется, что ENAME - это некий адрес оперативной памяти. Так что он должен быть уникальным. но на 100% я не уверен.
Знать бы как работает этот внутренний механизм лисповой идентификации объектов...
Это не лисповский механизм. Это общий ObjectARX/Lisp механизм. В ObjectARX это структура ads_name. И в ObjectARX есть специальная функция преобразования  ads_name в AcDbObjectId с именем acdbGetObjectId
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Всё проверил. При конвертации VLA-OBJECT в ENAME и передачи его в самописную LISP-функцию никаких проблем не возникает, даже при работе из LISP с неактивным документом, полученным с помощью ODBX. Проверял кодом:
Код - Auto/Visual Lisp [Выбрать]
  1. (setq odbx (_lispru-odbx))
  2.  
  3. (vla-open odbx (findfile "D:\\Tmp\\1.dwg"))
  4.  
  5. (vlax-for obj (vla-get-ModelSpace odbx)
  6.   (setq ename (vlax-vla-object->ename obj))
  7.   (CheckObjectIdOwner ename)
  8. )
  9.  
  10. (vlax-release-object odbx)
  11.  
  12. (setq odbx nil)
Самописная LISP-функция для проверки:
Код - C# [Выбрать]
  1. [LispFunction("CheckObjectIdOwner")]
  2. public static void CheckObject(ResultBuffer rb)
  3. {
  4.     if (rb != null)
  5.     {
  6.         TypedValue[] arr = rb.AsArray();
  7.         if (arr.Length == 1 && arr[0].Value is ObjectId id)
  8.         {
  9.             if (id.IsValid)
  10.             {
  11.                 Application.ShowAlertDialog("File name: " + id.Database.Filename);
  12.             }
  13.         }
  14.     }
  15. }
  16.  
_lispru-acad-version и _lispru-odbx взял тут: https://autolisp.ru/2010/04/08/proceed-unactive-document/

Оффлайн Александр Ривилис

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Дмитрий ЗагорулькинАвтор темы

  • ADN
  • *
  • Сообщений: 2531
  • Карма: 735
Альтернативный вариант - без использования ENAME (лёгкие пути - не для нас! ;D):
Код - Auto/Visual Lisp [Выбрать]
  1. (StartCatchDatabase)
  2.  
  3. (setq odbx (_lispru-odbx))
  4.  
  5. (vla-open odbx (findfile "D:\\Tmp\\1.dwg"))
  6.  
  7. (vlax-for obj (vla-get-ModelSpace odbx)
  8.   (CheckObjectOwner (vla-get-Handle obj))
  9. )
  10.  
  11. (vlax-release-object odbx)
  12.  
  13. (setq odbx nil)
  14.  
  15. (StopCatchDatabase)
Самописные LISP:
Код - C# [Выбрать]
  1. private static bool _catchIsRunned = false;
  2. private static Database _lastOpened = null;
  3.  
  4. [LispFunction("StartCatchDatabase")]
  5. public static void StartCatchDatabase(ResultBuffer rb)
  6. {
  7.     if (!_catchIsRunned)
  8.     {
  9.         Database.DatabaseConstructed += Database_DatabaseConstructed;
  10.         _catchIsRunned = true;
  11.     }
  12. }
  13.  
  14. [LispFunction("StopCatchDatabase")]
  15. public static void StopCatchDatabase(ResultBuffer rb)
  16. {
  17.     if (_catchIsRunned)
  18.     {
  19.         Database.DatabaseConstructed -= Database_DatabaseConstructed;
  20.         _catchIsRunned = false;
  21.         _lastOpened = null;
  22.     }
  23. }
  24.  
  25. private static void Database_DatabaseConstructed
  26.     (object sender, System.EventArgs e) => _lastOpened = sender as Database;
  27.  
  28. [LispFunction("CheckObjectOwner")]
  29. public static void EditObject(ResultBuffer rb)
  30. {
  31.     if (rb != null)                
  32.     {
  33.         Database db =
  34.             _lastOpened != null && !_lastOpened.IsDisposed && !string.IsNullOrEmpty(_lastOpened.Filename)
  35.             ? _lastOpened
  36.             : HostApplicationServices.WorkingDatabase;
  37.         TypedValue[] tVals = rb.AsArray();
  38.         if (tVals.Length == 1
  39.             && tVals[0].Value is string handleString)
  40.         {
  41.             ObjectId id = GetObjectId(db, handleString);
  42.             if (id.IsValid)
  43.             {
  44.                 Application.ShowAlertDialog("File name: " + id.Database.Filename);
  45.             }
  46.         }
  47.     }
  48. }
  49.  
  50. private static ObjectId GetObjectId(Database db, string handleString)
  51. {
  52.     ObjectId id = ObjectId.Null;
  53.     if (long.TryParse
  54.         (handleString,
  55.         NumberStyles.AllowHexSpecifier,
  56.         CultureInfo.InvariantCulture,
  57.         out long handleNum))
  58.     {
  59.         Handle handle = new Handle(handleNum);
  60.         db.TryGetObjectId(handle, out id);
  61.     }
  62.     return id;
  63. }
  64.  

Оффлайн Алексей Кулик

  • Administrator
  • *****
  • Сообщений: 1097
  • Карма: 172
Спасибо! Приеду домой - попробую погонять :)
P.S. Дим, статью еще сделай - будет вообще шикарно!
Все, что сказано - личное мнение.

Правила форума существуют не просто так!

Приводя в сообщении код, не забывайте про его форматирование!