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

ADN Club => AutoCAD .NET API => Тема начата: Андрей Бушман от 12-03-2014, 18:01:27

Название: Несколько вопросов работе с AutoCAD через COM
Отправлено: Андрей Бушман от 12-03-2014, 18:01:27
Доброго времени суток.

- AutoCAD 2009 SP3 x64 Enu
- AutoCAD 2014 SP1 x64 Enu

Следующий код успешно работает:
Код - C# [Выбрать]
  1. // Получаю объект уже запущеного приложения AutoCAD
  2. AcadApplication app1 = System.Runtime.InteropServices.Marshal.GetActiveObject("AutoCAD.Application") as AcadApplication;
  3. // Создаю новый объект приложения AutoCAD
  4. System.Type type = System.Type.GetTypeFromProgID("AutoCAD.Application");
  5. AcadApplication app2 = System.Activator.CreateInstance(type) as AcadApplication;

Если вместо "AutoCAD.Application" использовать "AutoCAD.Application.17.2", "AutoCAD.Application.19.1", "AutoCAD.Application.17"  или "AutoCAD.Application.19", то всё так же работает.

Несколько вопросов:

1. Если у меня одновременно запущено несколько приложений AutoCAD, причём разных версий, то:
1.1. Как в подобных случаях получить их перечень и отличать др. от друга (не хотелось бы процессы перебирать в цикле)?
1.2. Как указать, для какого именно из запущенных приложений следует получить объект AcadApplication?

2. Если на компьютере установлено сразу несколько версий AutoCAD, то:
2.1. Можно ли при помощи COM получить их перечень? Т,е. хотелось бы получить, к примеру, массив: "AutoCAD.Application.17.2", "AutoCAD.Application.19.1". Это легко можно получить через реестр, но на всякий случай уточняю наличия функции, делающей то же самое...

Спасибо.
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Александр Ривилис от 12-03-2014, 18:04:47
Отчасти должно помочь это: Доступ к COM-приложениям из Таблицы исполняющихся объектов (ROT) (http://adn-cis.org/dostup-k-com-prilozheniyam-iz-tabliczyi-ispolnyayushhixsya-obektov-%28rot%29.html)
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Андрей Бушман от 12-03-2014, 18:47:15
Спасибо.
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Андрей Бушман от 12-03-2014, 19:15:33
Поторопился я... Пару раз отработало нормально, а сейчас какая-то муть в результате...

Итак, код:

Код - C# [Выбрать]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Microsoft.Win32;
  5.  
  6. using AutoCAD;
  7. using AXDBLib;
  8.  
  9. namespace ConsoleTest {
  10.         class Program {
  11.                 static void Main(string[] args) {
  12.                         try {
  13.                                 // Используя эти идентификаторы можно указывать, объект
  14.                                 // приложения какой именно версии следует создать (см. метод
  15.                                 // CreateInstance)
  16.                                 String[] appIds = Registry.LocalMachine.OpenSubKey(
  17.                                         @"SOFTWARE\Classes", false).GetSubKeyNames().Where(n =>
  18.                                                 n.StartsWith("AutoCAD.Application")).ToArray();
  19.  
  20.                                 if (appIds.Length == 0) {
  21.                                         Console.WriteLine("Идентификаторы AutoCAD не обнаружены " +
  22.                                                 "в реестре.");
  23.                                         return;
  24.                                 }
  25.                                 Console.WriteLine("Обнаружены следующие идентификаторы AutoCAD:");
  26.                                 foreach (String item in appIds) Console.WriteLine(item);
  27.  
  28.                                 String strId = appIds[0];
  29.  
  30.                                 // Создаю новый объект приложения AutoCAD
  31.                                 System.Type type = System.Type.GetTypeFromProgID(strId);
  32.                                 AcadApplication app1 = System.Activator.CreateInstance(type)
  33.                                         as AcadApplication;
  34.                                 // Получаю объект уже запущеного приложения AutoCAD
  35.                                 AcadApplication app2 = System.Runtime.InteropServices.Marshal
  36.                                         .GetActiveObject(strId) as AcadApplication;
  37.  
  38.                                 if (app1 == null) Console.WriteLine("app1 == null");                           
  39.                                 else app1.Visible = true;
  40.  
  41.                                 if (app2 == null) Console.WriteLine("app2 == null");
  42.                                 else app2.Visible = true;
  43.                                
  44.                         }
  45.                         catch (Exception ex) {
  46.                                 Console.WriteLine("Ошибка: {0}", ex.Message);
  47.                         }
  48.                         Console.WriteLine("Нажмите любую клавишу для выхода...");
  49.                         Console.ReadKey();
  50.                 }
  51.         }
  52. }

Ниже прикреплён результат в виде изображения...
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Дима_ от 12-03-2014, 21:17:40
Не помню где был разбор полетов на эту тему - один из вариантов возникновения такой ошибки - автокад может не успевать загружаться - то есть после CreateInstance может вернуть null, но чуть подождав процесс "ловится" на GetActiveObject; то есть это не столько косяк акада сколько виндового механизма - возникает на "тяжелых" приложениях - ошибка "плавающая" происходит при "удачном" скоплении звезд на небе.
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Андрей Бушман от 12-03-2014, 21:51:50
Пока ехал домой вспомнил, что в проекте подключены библиотеки для версии 19.0 (скрины здесь (http://adn-cis.org/forum/index.php?topic=605.0)), а в коде я дёргаю первый элемент (17), который соответствует AutoCAD 2009 - пока грешу на это... Завтра попробую дёргать не первый элемент, а последний.

Если дела действительно обстоят так, как пишет Дима_, то это хреново. Особенно учитывая тот факт, что от версии к версии AutoCAD загружается всё медленней и медленней. Например, то, с какой скоростью грузится 2014-й лично я считаю неприемлемым (Autodesk очевидно считает иначе).
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Дима_ от 12-03-2014, 21:58:01
Тогда он должен материться на приведение типа - то есть срабатывать catch...
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Андрей Бушман от 12-03-2014, 22:01:17
не ругается и вряд ли должен, т.к. я же выполняю приведение не так:
Код - C# [Выбрать]
  1. AcadApplication app1 = (AcadApplication) System.Activator.CreateInstance(type);

P.S. Александр Наумович затаился, закидывается попкорном и потягивает колу через трубочку :)
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Дима_ от 12-03-2014, 22:03:05
Autodesk очевидно считает иначе
У них наверное машинки помощней - соотв. да простых смертных им...
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Андрей Бушман от 12-03-2014, 22:06:16
У них наверное машинки помощней - соотв. да простых смертных им...

вспомнилось:
Цитата: Служебный роман
Я вам тоже не барахло... (с)
Мой домашний комп вроде как тоже не смартфон... И тем не менее... :)
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Александр Ривилис от 12-03-2014, 22:22:30
Тогда он должен материться на приведение типа - то есть срабатывать catch...
Нет. Андрей же использует конструкцию as, так что если преобразование не прошло, то и будет null без всяких Exception.
Тут интересно as убрать. Может всё-таки возникнет Exeption. Что же касается того, что Дима пишет о необходимости задержки, то это правда.
И решается эта проблема вот так:  Cannot instantiate AutoCAD 2010 from an external .NET application after installing Update 1 (http://adndevblog.typepad.com/autocad/2012/05/cannot-instantiate-autocad-2010-from-an-external-net-application-after-installing-update-1.html)
Конечно это относится не только к AutoCAD 2010...

Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Дима_ от 12-03-2014, 22:29:52
не ругается и вряд ли должен...
точно забыл - он же в случае не удачи преобразования как раз и должен null вернуть...
з.ы. опс - уже ответили - Александр приятного аппетита (про попкорн) :).
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Андрей Бушман от 12-03-2014, 22:34:43
И решается эта проблема вот так:  Cannot instantiate AutoCAD 2010 from an external .NET application after installing Update 1
Мне сложно читать язык гоблинов. :) Завтра конвертну в C# и попробую осмыслить.
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Александр Ривилис от 12-03-2014, 22:37:46
Нашел не "на языке гоблинов": http://through-the-interface.typepad.com/through_the_interface/2010/02/handling-com-calls-rejected-by-autocad-from-an-external-net-application.html
Но на всякий случай нужно будет перепроверить.
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Андрей Бушман от 12-03-2014, 22:47:06
Вот лучше бы уж это перевели, а не про TLB :)
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Дима_ от 12-03-2014, 22:47:22
не ругается и вряд ли должен...
точно забыл - он же в случае не удачи преобразования как раз и должен null вернуть...
и судя по ссылке там все-же должна возникать ошибка создания - то есть скорее все же неверная библиотека, хотя это никак не исключает "плохого" варианта (тем паче, что как я уже говорил ошибка та с "характером").
з.ы. опс - уже ответили - Александр приятного аппетита (про попкорн) :).
з.з.ы. - видимо если в процессе редактирования сообщения добавляется еще несколько в бд Insert вместо Update вылазит.
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Андрей Бушман от 13-03-2014, 11:30:41
Если запускать (или получать доступ) AutoCAD той версии, которая соответствует подключенным библиотекам, то получаю не null, как и нужно. Соответственно, чтобы вообще избавиться от зависимости к версии AutoCAD, придётся использовать механизм позднего связывания (в этом случае никаких библиотек AutoCAD подключать не нужно):

Код - C# [Выбрать]
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using Microsoft.Win32;
  5.  
  6. namespace ConsoleTest {
  7.         class Program {
  8.                 static void Main(string[] args) {
  9.                         String fullName = String.Empty;
  10.                         try {
  11.                                 // Используя эти идентификаторы можно указывать, объект
  12.                                 // приложения какой именно версии следует создать (см. метод
  13.                                 // CreateInstance)
  14.                                 String[] appIds = Registry.LocalMachine.OpenSubKey(
  15.                                         @"SOFTWARE\Classes", false).GetSubKeyNames().Where(n =>
  16.                                                 n.StartsWith("AutoCAD.Application")).ToArray();
  17.  
  18.                                 if (appIds.Length == 0) {
  19.                                         Console.WriteLine("Идентификаторы AutoCAD не обнаружены " +
  20.                                                 "в реестре.");
  21.                                         return;
  22.                                 }
  23.                                 Console.WriteLine("Обнаружены следующие идентификаторы AutoCAD:");
  24.                                 foreach (String item in appIds) Console.WriteLine(item);
  25.  
  26.                                 String strId = appIds[0];
  27.  
  28.                                 // Тип интересующего меня COM объекта
  29.                                 System.Type type = System.Type.GetTypeFromProgID(strId);                               
  30.  
  31.                                 // Вариант с использованием механизма позднего связывания.
  32.                                 // В этом случае не требуется подключать ссылки к библиотекам AutoCAD:
  33.                                 Object app = System.Activator.CreateInstance(type); // AcadApplication
  34.                                 fullName = type.InvokeMember("FullName", System.Reflection.BindingFlags
  35.                                         .GetProperty, null, app, new Object[] { }) as String;
  36.  
  37.                                 Boolean visible = true;
  38.                                 type.InvokeMember("Visible", System.Reflection.BindingFlags.SetProperty,
  39.                                         null, app, new Object[] { visible });
  40.  
  41.                                 Object activeDoc = type.InvokeMember("ActiveDocument",
  42.                                         System.Reflection.BindingFlags.GetProperty, null, app,
  43.                                         new Object[] { }); // AcadDocument
  44.  
  45.                                 String command = String.Format("(princ \"Hello, {0}\")(princ)\n",
  46.                                                 Environment.UserName);
  47.                                 type.InvokeMember("SendCommand", System.Reflection.BindingFlags.InvokeMethod,
  48.                                         null, activeDoc, new Object[] { command });
  49.                         }
  50.                         catch (Exception ex) {
  51.                                 Console.WriteLine("Ошибка: {0}", ex.Message);
  52.                         }
  53.                         Console.WriteLine("FullName: {0}", fullName);
  54.                         Console.WriteLine("Нажмите любую клавишу для выхода...");
  55.                         Console.ReadKey();
  56.                 }
  57.         }
  58. }
Название: Re: Несколько вопросов работе с AutoCAD через COM
Отправлено: Дима_ от 13-03-2014, 11:54:37
тогда уж используй динамический вызов (я правда не помню в каком c# он появился)
з.ы. у меня в свое время так и не получилось создать vla методами Region через позднее связывание - в итоге забил просто.