Несколько вопросов работе с AutoCAD через COM

Автор Тема: Несколько вопросов работе с AutoCAD через COM  (Прочитано 14337 раз)

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

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Доброго времени суток.

- 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". Это легко можно получить через реестр, но на всякий случай уточняю наличия функции, делающей то же самое...

Спасибо.
« Последнее редактирование: 12-03-2014, 18:30:24 от Андрей Бушман »

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

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

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Спасибо.

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Поторопился я... Пару раз отработало нормально, а сейчас какая-то муть в результате...

Итак, код:

Код - 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. }

Ниже прикреплён результат в виде изображения...

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Не помню где был разбор полетов на эту тему - один из вариантов возникновения такой ошибки - автокад может не успевать загружаться - то есть после CreateInstance может вернуть null, но чуть подождав процесс "ловится" на GetActiveObject; то есть это не столько косяк акада сколько виндового механизма - возникает на "тяжелых" приложениях - ошибка "плавающая" происходит при "удачном" скоплении звезд на небе.

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Пока ехал домой вспомнил, что в проекте подключены библиотеки для версии 19.0 (скрины здесь), а в коде я дёргаю первый элемент (17), который соответствует AutoCAD 2009 - пока грешу на это... Завтра попробую дёргать не первый элемент, а последний.

Если дела действительно обстоят так, как пишет Дима_, то это хреново. Особенно учитывая тот факт, что от версии к версии AutoCAD загружается всё медленней и медленней. Например, то, с какой скоростью грузится 2014-й лично я считаю неприемлемым (Autodesk очевидно считает иначе).

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Тогда он должен материться на приведение типа - то есть срабатывать catch...

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
не ругается и вряд ли должен, т.к. я же выполняю приведение не так:
Код - C# [Выбрать]
  1. AcadApplication app1 = (AcadApplication) System.Activator.CreateInstance(type);

P.S. Александр Наумович затаился, закидывается попкорном и потягивает колу через трубочку :)

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Autodesk очевидно считает иначе
У них наверное машинки помощней - соотв. да простых смертных им...

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
У них наверное машинки помощней - соотв. да простых смертных им...

вспомнилось:
Цитата: Служебный роман
Я вам тоже не барахло... (с)
Мой домашний комп вроде как тоже не смартфон... И тем не менее... :)

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Тогда он должен материться на приведение типа - то есть срабатывать catch...
Нет. Андрей же использует конструкцию as, так что если преобразование не прошло, то и будет null без всяких Exception.
Тут интересно as убрать. Может всё-таки возникнет Exeption. Что же касается того, что Дима пишет о необходимости задержки, то это правда.
И решается эта проблема вот так:  Cannot instantiate AutoCAD 2010 from an external .NET application after installing Update 1
Конечно это относится не только к AutoCAD 2010...

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

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
не ругается и вряд ли должен...
точно забыл - он же в случае не удачи преобразования как раз и должен null вернуть...
з.ы. опс - уже ответили - Александр приятного аппетита (про попкорн) :).

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
И решается эта проблема вот так:  Cannot instantiate AutoCAD 2010 from an external .NET application after installing Update 1
Мне сложно читать язык гоблинов. :) Завтра конвертну в C# и попробую осмыслить.

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

  • Administrator
  • *****
  • Сообщений: 13830
  • Карма: 1784
  • Рыцарь ObjectARX
  • Skype: rivilis
Нашел не "на языке гоблинов": http://through-the-interface.typepad.com/through_the_interface/2010/02/handling-com-calls-rejected-by-autocad-from-an-external-net-application.html
Но на всякий случай нужно будет перепроверить.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Вот лучше бы уж это перевели, а не про TLB :)

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
не ругается и вряд ли должен...
точно забыл - он же в случае не удачи преобразования как раз и должен null вернуть...
и судя по ссылке там все-же должна возникать ошибка создания - то есть скорее все же неверная библиотека, хотя это никак не исключает "плохого" варианта (тем паче, что как я уже говорил ошибка та с "характером").
з.ы. опс - уже ответили - Александр приятного аппетита (про попкорн) :).
з.з.ы. - видимо если в процессе редактирования сообщения добавляется еще несколько в бд Insert вместо Update вылазит.
« Последнее редактирование: 12-03-2014, 22:57:40 от Дима_ »

Оффлайн Андрей БушманАвтор темы

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Если запускать (или получать доступ) 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. }

Оффлайн Дима_

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
тогда уж используй динамический вызов (я правда не помню в каком c# он появился)
з.ы. у меня в свое время так и не получилось создать vla методами Region через позднее связывание - в итоге забил просто.