Работа с AutoCad как с COM объектом

Автор Тема: Работа с AutoCad как с COM объектом  (Прочитано 13719 раз)

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

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

Оффлайн Борис_САвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 238
  • Карма: 3
Добрый день!
Мне нужно работать с AutoCad как с COM объектом. Нужно ли для этого запускать AutoCad или можно создать COM объект чисто программно?
Спасибо.

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Работа с AutoCad как с COM объектом
« Ответ #1 : 15-03-2021, 15:56:53 »
Борис_С,
Я правильно понимаю, что сейчас речь идёт про работу с AutoCAD из внешнего приложения (exe-файла), а не из .NET-сборки, которая грузится внутрь AutoCAD (например, при помощи команды NETLOAD)?
Если речь идёт о внешнем приложении, то я давал уже две ссылки:
https://adn-cis.org/forum/index.php?topic=7364.0
https://adn-cis.org/forum/index.php?topic=606.0
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Борис_САвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 238
  • Карма: 3
Re: Работа с AutoCad как с COM объектом
« Ответ #2 : 15-03-2021, 16:18:01 »
Речь идет о работе с AutoCad из приложения типа Class Library, т.е. из NET-сборки. Только эта сборка не грузится в AutoCad, а грузится из внешнего приложения (exe-файла).

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Работа с AutoCad как с COM объектом
« Ответ #3 : 15-03-2021, 16:41:54 »
Речь идет о работе с AutoCad из приложения типа Class Library, т.е. из NET-сборки. Только эта сборка не грузится в AutoCad, а грузится из внешнего приложения (exe-файла).
Значит всё вышесказанное справедливо, так как сборка не грузится в AutoCAD и соответственно в ней нельзя использовать AutoCAD .NET API, а можно только COM/ActiveX. Прочитайте всё, что я дал в ссылках выше. Это то с чем вам придётся работать если пытаться общаться с AutoCAD через COM/ActiveX.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Работа с AutoCad как с COM объектом
« Ответ #4 : 15-03-2021, 16:58:46 »
На всякий случай, чтобы не было лишних вопросов, вот этого в твоей сборке быть не может:
Код - C# [Выбрать]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4. using Autodesk.AutoCAD.Geometry;
  5. using Autodesk.AutoCAD.Runtime;
Это можно использовать только в сборке, которая грузится внутрь AutoCAD
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Борис_САвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 238
  • Карма: 3
Re: Работа с AutoCad как с COM объектом
« Ответ #5 : 15-03-2021, 17:29:13 »
На операторе
Type comAppType = Type.GetTypeFromProgID("AutoCAD.Application");
ругается на метод GetTypeFromProgID.
Что нужно добавить, чтобы компилятор его увидел?

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

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

Оффлайн Борис_САвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 238
  • Карма: 3
Re: Работа с AutoCad как с COM объектом
« Ответ #7 : 15-03-2021, 20:23:35 »
Спасибо. Разобрался. У меня в студии стояла старая версия Net.

Оффлайн Борис_САвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 238
  • Карма: 3
Re: Работа с AutoCad как с COM объектом
« Ответ #8 : 16-03-2021, 18:48:45 »
В объектной модели ActiveX (http://help.autodesk.com/view/ACD/2017/ENU/?guid=GUID-A809CD71-4655-44E2-B674-1FE200B9FE30) есть объект
Application.Document.ModelSpace. Как я понял, он содержит в себе все объекты пространства модели.
У него есть метод Item, который выдает элемент коллекции по заданному индексу.
В примере, который приведен для описания этого метода для VBA указан тип AcadEntity. В массив этого типа грузятся все элементы метода Item.
Какой аналог этого типа я могу использовать в С# и какие библиотеки я нужно добавить в проект?

Оффлайн Lemieux

  • ADN OPEN
  • ****
  • Сообщений: 389
  • Карма: 21
Re: Работа с AutoCad как с COM объектом
« Ответ #9 : 16-03-2021, 20:30:57 »
В примере, который приведен для описания этого метода для VBA указан тип AcadEntity. В массив этого типа грузятся все элементы метода Item.
Что это значит?

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Работа с AutoCad как с COM объектом
« Ответ #10 : 16-03-2021, 21:16:05 »
Какой аналог этого типа я могу использовать в С# и какие библиотеки я нужно добавить в проект?
Этот же тип (Autodesk.AutoCAD.Interop.Common.AcadEntity) и используешь. И берёшь его из Autodesk.AutoCAD.Interop.Common.dll - из каталога ObjectARX SDK\inc-x64 или \inc-win32 - в зависимости от разрядности AutoCAD.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Борис_САвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 238
  • Карма: 3
Re: Работа с AutoCad как с COM объектом
« Ответ #11 : 17-03-2021, 14:13:26 »
Я понял, что работать нужно со свойством ObjectName объекта Application.Document.ModelSpace.
В этом случае не нужно обращаться к библиотекам AutoCad.
По нему можно понять с каким элементом в данный момент я работаю.
А уже дальше из элемента вытаскивать нужные свойства.
Привожу код, который вытаскивает все тексты из графических элементов AutoCad.
Осталось сделать самое сложное - работа с BlockReference.
Код - C# [Выбрать]
  1.         public override bool OpenFile(string filePath)
  2.         {
  3.             try
  4.             {
  5.                 Type comAppType = Type.GetTypeFromProgID(
  6.                 "AutoCAD.Application");
  7.  
  8.                 dynamic app = GetApp(() => Activator
  9.                     .CreateInstance(comAppType));
  10.  
  11.                 if (app == null)
  12.                 {
  13.                     WriteLine("Не удалось получить объект " +
  14.                         "Application." +
  15.                         "\n\nPress any key for exit...");
  16.                     ReadKey();
  17.                     return false;
  18.                 }
  19.  
  20.                 //DoAction((object)app, () => app.Visible = false);
  21.  
  22.                 dynamic docs = CallFunc((object)app,
  23.                 () => app.Documents);
  24.  
  25.                 // Открываем документ
  26.                 dynamic acDoc = CallFunc((object)app,
  27.                     () => docs.Open(filePath));
  28.  
  29.                 dynamic acCurDb = CallFunc((object)app,
  30.                     () => acDoc.Database);
  31.  
  32.                 dynamic modelSpace = CallFunc((object)app,
  33.                     () => acDoc.ModelSpace);
  34.  
  35.                 dynamic count = CallFunc((object)app,
  36.                     () => modelSpace.Count);
  37.  
  38.                 for (int i = 0; i < (int)count; i++)
  39.                 {
  40.                     dynamic en = CallFunc((object)app,
  41.                         () => modelSpace.Item(i));
  42.                     dynamic className = CallFunc((object)app,
  43.                         () => en.ObjectName);
  44.                     if (className == "AcDbMText")
  45.                     {
  46.                         dynamic str = CallFunc((object)app, () => en.TextString);
  47.                     }
  48.                     else if (className == "AcDbText")
  49.                     {
  50.                         dynamic str = CallFunc((object)app, () => en.TextString);
  51.                     }
  52.                     else if (className == "AcDbTable")
  53.                     {
  54.                         dynamic rowsCount = CallFunc((object)app, () => en.Rows);
  55.                         dynamic columnsCount = CallFunc((object)app, () => en.Columns);
  56.                         for (int row = 0; row < rowsCount; row++)
  57.                         for (int col = 0; col < columnsCount; col++)
  58.                         {
  59.                             dynamic str = CallFunc((object)app, () => en.GetCellValue(row, col));
  60.                         }
  61.                     }
  62.                     else if (className == "AcDbMLeader")
  63.                     {
  64.                         dynamic str = CallFunc((object)app, () => en.TextString);
  65.                     }
  66.                     else if (className == "AcDbBlockReference")
  67.                     {
  68.                         dynamic attributes = CallFunc((object)app, () => en.GetAttributes);
  69.                         dynamic countAttributes = CallFunc((object)app, () => attributes.Count);
  70.                     }
  71.                 }
  72.  
  73.                 // закрытие без сохранения изменений
  74.                 DoAction((object)app, () => acDoc.Close(false));
  75.  
  76.                 DoAction((object)app, () => app.Quit());
  77.  
  78.                 return true;
  79.             }
  80.             catch (System.Exception e)
  81.             {
  82.                 WriteErrorMsg(e, ConsoleColor.Red);
  83.                 return false;
  84.             };
  85.         }
  86.  

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Re: Работа с AutoCad как с COM объектом
« Ответ #12 : 17-03-2021, 14:17:26 »
Осталось сделать самое сложное - работа с BlockReference.

BlockReference или Block? Из BlockReference ты можешь взять только атрибуты.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Борис_САвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 238
  • Карма: 3
Re: Работа с AutoCad как с COM объектом
« Ответ #13 : 18-03-2021, 16:13:34 »
BlockReference. Для плагина у меня эта ветка реализована:
Код - C# [Выбрать]
  1.         // Поиск всех текстов чертежа
  2.         public static void FindTextsInDrawing()
  3.         {
  4.             try
  5.             {
  6.                 ListNotes.Clear();
  7.  
  8.                 // Get the current document and database, and start a transaction
  9.                 Document acDoc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.
  10.                     MdiActiveDocument;
  11.                 Database acCurDb = acDoc.Database;
  12.  
  13.                 using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
  14.                 {
  15.                     // Открываю Block table для чтения
  16.                     BlockTable acBlkTbl;
  17.                     acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId,
  18.                                                  OpenMode.ForRead) as BlockTable;
  19.  
  20.                     // Открываю Block table record Model space для чтения
  21.                     BlockTableRecord acBlkTblRec;
  22.                     acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
  23.                                                     OpenMode.ForRead) as BlockTableRecord;
  24.                     FindItems(acTrans, acBlkTblRec);
  25.                 }
  26.             }
  27.             catch (System.Exception e)
  28.             {
  29.                 Editor acDocEd = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument.Editor;
  30.                 acDocEd.WriteMessage(String.Format("Ошибка. {0}{1}" + Environment.NewLine, e.Message, e.StackTrace));
  31.             }
  32.         }
  33.  
  34.         // поиск всех элементов Block table record
  35.         public static void FindItems(Transaction acTrans, BlockTableRecord blkTblRec)
  36.         {
  37.             foreach (ObjectId acObjId in blkTblRec)
  38.             {
  39.                 Entity en = acTrans.GetObject(acObjId, OpenMode.ForRead) as Entity;
  40.                 //ed.WriteMessage("\n" + acObjId.ObjectClass.DxfName);
  41.                 if (en != null)
  42.                 {
  43.                     if (en.GetType() == typeof(DBText))
  44.                     {
  45.                         string str = ((DBText)en).TextString;
  46.                         ListNotes.Add(new CNote(str, acObjId, DataType.DBText));
  47.                     }
  48.                     else if (en.GetType() == typeof(MText))
  49.                     {
  50.                         //if (ListObjectId.IndexOf(acObjId) == -1)
  51.                         //{
  52.                             string str = ((MText)en).Contents;
  53.                             ListNotes.Add(new CNote(str, acObjId, DataType.MText));
  54.                         //}
  55.                     }
  56.                     else if (en.GetType() == typeof(Table))
  57.                     {
  58.                         Autodesk.AutoCAD.DatabaseServices.Table tbl = (Table)en;
  59.                         for (int row = 0; row < tbl.Rows.Count; row++)
  60.                             for (int col = 0; col < tbl.Columns.Count; col++)
  61.                             {
  62.                                 string str = tbl.Cells[row, col].TextString;
  63.                                 if (!String.IsNullOrEmpty(str))
  64.                                     ListNotes.Add(new CNote(str, acObjId, DataType.Table, row, col));
  65.                             }
  66.                     }
  67.                     else if (en.GetType() == typeof(MLeader))
  68.                     {
  69.                         string str = ((MLeader)en).MText.Contents;
  70.                         ListNotes.Add(new CNote(str, acObjId, DataType.MLeader));
  71.                     }
  72.                     else if (en.GetType() == typeof(BlockReference))
  73.                     {
  74.                         BlockReference bref = en as BlockReference;
  75.                         AttributeCollection attributeCollection = bref.AttributeCollection;
  76.                         if (attributeCollection.Count > 0) // считывание атрибутов блока
  77.                         {
  78.                             foreach (ObjectId att in bref.AttributeCollection)
  79.                             {
  80.                                 DBObject dbObj = acTrans.GetObject(att, OpenMode.ForRead) as DBObject;
  81.                                 AttributeReference acAttRef = dbObj as AttributeReference;
  82.                                 //if (acAttRef.TextString.IndexOf("гост") != -1) {
  83.                                 //}
  84.                                 if (acAttRef.Visible)
  85.                                      ListNotes.Add(new CNote(acAttRef.TextString, acAttRef.ObjectId,
  86.                                         DataType.AttributeReference, 0, 0, acAttRef.IsMTextAttribute));
  87.                             }
  88.                         }
  89.                         // Открываю Block table record BlockReference для чтения
  90.                         BlockTableRecord  acBlkTblRec = acTrans.GetObject(bref.BlockTableRecord,
  91.                                                         OpenMode.ForRead) as BlockTableRecord;
  92.                         FindItems(acTrans, acBlkTblRec);
  93.                     }
  94.                 }
  95.             }
  96.         }
  97.  
Теперь мне нужно реализовать ветку else if (en.GetType() == typeof(BlockReference)) { ... } с помощью COM.

Оффлайн Борис_САвтор темы

  • ADN OPEN
  • ***
  • Сообщений: 238
  • Карма: 3
Re: Работа с AutoCad как с COM объектом
« Ответ #14 : 18-03-2021, 17:11:45 »
Если я правильно понимаю, свойство BlockReference.HasAttributes показывает, есть ли у блока атрибуты.
Если они есть, я должен получить ссылку на объект AttributeReference, и свойство TextString этого объекта содержит текст, привязанный к блоку.
В описании объекта AttributeReference указано, что получить его можно с помощью метода GetAttributes. Этот метод есть у объекта BlockReference.
Я написал такой код:
Код - C# [Выбрать]
  1.         dynamic blockHasAttributes = CallFunc((object)app, () => en.HasAttributes);
  2.         if (blockHasAttributes)
  3.         {
  4.              dynamic AttributeRef = CallFunc((object)app, () => en.GetAttributes());
  5.              dynamic str = CallFunc((object)app, () => AttributeRef.TextString);
  6.         }
  7.  
На операторе dynamic str = CallFunc((object)app, () => AttributeRef.TextString); происходит исключение.
Где я не прав?