COM: Объект не соответствует конечному типу.

Автор Тема: COM: Объект не соответствует конечному типу.  (Прочитано 6490 раз)

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

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

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

Внимание: закрывать AutoCAD вручную не надо, т.к. объект AcadApplication продолжает использоваться далее в коде. AutoCAD будет закрыт автоматически, как только будет нажата любая кнопка в активном окне консольного приложения.

У меня на машинке установлены:

- AutoCAD 2009 x64 SP3 Enu
- AutoCAD 2012 x64 SP1 Enu

Соответственно, код запустит AutoCAD 2014. Но обнаружилась проблема: если в строке 51 указанного кода переменной index назначить значение 0, чтобы открывался самый старый AutoCAD (в моём случае 2009-й), то я получаю исключение в строке 75:

Цитировать
Объект не соответствует конечному типу.

Код - C# [Выбрать]
  1. // Sample.cs
  2. // © Андрей Бушман, 2014
  3. // Пример того, как запускать AutoCAD из внешнего приложения и затем работать
  4. // с ним через COM, используя для этого механизм позднего связывания. При таком
  5. // подходе нет необходимости в подключении к проекту каких-либо библиотек
  6. // AutoCAD, однако работать такой код будет несколько медленнее, чем код,
  7. // использующий подключенные к проекту библиотеки AutoCAD.
  8.  
  9. // Закомментируйте определение CLSID_USE, если желаете запускать AutoCAD не при
  10. // помощи CLSID, а через строковое представление его ProgID:
  11. #define CLSID_USE
  12.  
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Linq;
  16. using Microsoft.Win32;
  17.  
  18. namespace ConsoleTest {
  19.         class Program {
  20.                 static void Main(string[] args) {
  21.                         String fullName = String.Empty;
  22.                         System.Type type = null; // Тип интересующего меня COM объекта
  23.                         Object app = null; // объект приложения (AcadApplication)
  24.                         Object activeDoc = null; // активный документ (AcadDocument)
  25.                         try {
  26.                                 // Используя эти строковые идентификаторы можно указывать, объект
  27.                                 // приложения какой именно версии следует создать (см. метод
  28.                                 // CreateInstance)
  29.                                 String[] appIds = Registry.LocalMachine.OpenSubKey(
  30.                                         @"SOFTWARE\Classes", false).GetSubKeyNames().Where(n =>
  31.                                                 n.StartsWith("AutoCAD.Application")).ToArray();
  32. #if CLSID_USE
  33.                                 // Вместо строковых идентификаторов приложений можно
  34.                                 // использовать их CLSID
  35.                                 Guid[] guids = new Guid[appIds.Length];
  36.                                 for (Int32 i = 0; i < appIds.Length; i++) {
  37.                                         String strGuid = Registry.LocalMachine.OpenSubKey(
  38.                                         @"SOFTWARE\Classes", false).OpenSubKey(appIds[i]
  39.                                         + @"\CLSID", false).GetValue(String.Empty, String.Empty,
  40.                                         RegistryValueOptions.DoNotExpandEnvironmentNames) as String;
  41.                                         guids[i] = new Guid(strGuid);
  42.                                 }
  43. #endif
  44.                                 if (appIds.Length == 0) {
  45.                                         Console.WriteLine("Идентификаторы AutoCAD не обнаружены " +
  46.                                                 "в реестре.");
  47.                                         return;
  48.                                 }
  49.                                 Console.WriteLine("Обнаружены следующие идентификаторы AutoCAD:");
  50.                                 foreach (String item in appIds) Console.WriteLine(item);
  51.                                 Int32 index = appIds.Length - 1; // Запускаю самую новую версию из доступных
  52. #if CLSID_USE
  53.                                 Guid guid = guids[index];
  54.                                 type = System.Type.GetTypeFromCLSID(guid);
  55. #else
  56.                                 String strId = appIds[index];
  57.                                 type = System.Type.GetTypeFromProgID(strId);
  58. #endif
  59.                                 // Инициализируем объект приложения AutoCAD
  60.                                 // (создаём новый процесс acad.exe)
  61.                                 app = System.Activator.CreateInstance(type);
  62.  
  63.                                 fullName = type.InvokeMember("FullName", System.Reflection.BindingFlags
  64.                                         .GetProperty, null, app, new Object[] { }) as String;
  65.                                 Boolean visible = true; // Пусть AutoCAD отображается на экране
  66.                                 type.InvokeMember("Visible", System.Reflection.BindingFlags.SetProperty,
  67.                                         null, app, new Object[] { visible });
  68.  
  69.                                 activeDoc = type.InvokeMember("ActiveDocument", System.Reflection
  70.                                         .BindingFlags.GetProperty, null, app, new Object[] { });
  71.  
  72.                                 // Для примера, запустим произвольный LISP в командной строке AutoCAD:
  73.                                 String command = String.Format("(princ \"Hello, {0}\")(princ)\n",
  74.                                                 Environment.UserName);
  75.                                 type.InvokeMember("SendCommand", System.Reflection.BindingFlags
  76.                                         .InvokeMethod, null, activeDoc, new Object[] { command });
  77.                         }
  78.                         catch (Exception ex) {
  79.                                 Console.WriteLine("Ошибка: {0}", ex.Message);
  80.                         }
  81.                         Console.WriteLine("FullName: {0}", fullName);
  82.                         Console.WriteLine("Нажмите любую клавишу для выхода (AutoCAD " +
  83.                                 "так же завершит свою работу)...");
  84.                         Console.ReadKey();
  85.                         if (app != null)
  86.                                 // Завершаем работу приложения AutoCAD
  87.                                 type.InvokeMember("Quit", System.Reflection.BindingFlags.InvokeMethod,
  88.                                                 null, app, new Object[] { });
  89.                 }
  90.         }
  91. }

В чём может быть проблема?

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

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Мне сейчас не проверить т.к. с планшета, но странно  что в 2014 работает - сендкоманд это метод документа - соответственно и инвокемембер надо делать от ТИПА документа а не автокада.
з.ы. средства динамического вызова не используются из религиозных  соображений?
« Последнее редактирование: 14-03-2014, 18:42:40 от Дима_ »

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Мне сейчас не проверить т.к. с планшета, но странно  что в 2014 работает - сендкоманд это метод документа - соответственно и инвокемембер надо делать от ТИПА документа а не автокада.
Семён Семёнович... Ну конечно же... :) В коде я вызываю метод для экземпляра документа  (activeDoc указан в одном из параметров), вот только тип использую не от AcadDocument, а от AcadApplication (банальная невнимательность). :)
Спасибо, исправил, заработало:
Код - C# [Выбрать]
  1. // Sample.cs
  2. // © Андрей Бушман, 2014
  3. // Пример того, как запускать AutoCAD из внешнего приложения и затем работать
  4. // с ним через COM, используя для этого механизм позднего связывания. При таком
  5. // подходе нет необходимости в подключении к проекту каких-либо библиотек
  6. // AutoCAD, однако работать такой код будет несколько медленнее, чем код,
  7. // использующий подключенные к проекту библиотеки AutoCAD.
  8.  
  9. // Закомментируйте определение CLSID_USE, если желаете запускать AutoCAD не при
  10. // помощи CLSID, а через строковое представление его ProgID:
  11. #define CLSID_USE
  12.  
  13. using System;
  14. using System.Collections.Generic;
  15. using System.Linq;
  16. using Microsoft.Win32;
  17.  
  18. namespace ConsoleTest {
  19.   class Program {
  20.     static void Main(string[] args) {
  21.       String fullName = String.Empty;
  22.       Type appType = null; // Тип приложения
  23.       Type docType = null; // Тип документа
  24.       Object app = null; // объект приложения (AcadApplication)
  25.       Object activeDoc = null; // активный документ (AcadDocument)
  26.       try {
  27.         // Используя эти строковые идентификаторы можно указывать, объект
  28.         // приложения какой именно версии следует создать (см. метод
  29.         // CreateInstance)
  30.         String[] appIds = Registry.LocalMachine.OpenSubKey(
  31.                 @"SOFTWARE\Classes", false).GetSubKeyNames().Where(n =>
  32.                         n.StartsWith("AutoCAD.Application")).ToArray();
  33. #if CLSID_USE
  34.         // Вместо строковых идентификаторов приложений можно
  35.         // использовать их CLSID
  36.         Guid[] guids = new Guid[appIds.Length];
  37.         for (Int32 i = 0; i < appIds.Length; i++) {
  38.           String strGuid = Registry.LocalMachine.OpenSubKey(
  39.           @"SOFTWARE\Classes", false).OpenSubKey(appIds[i]
  40.           + @"\CLSID", false).GetValue(String.Empty, String.Empty,
  41.           RegistryValueOptions.DoNotExpandEnvironmentNames) as String;
  42.           guids[i] = new Guid(strGuid);
  43.         }
  44. #endif
  45.         if (appIds.Length == 0) {
  46.           Console.WriteLine("Идентификаторы AutoCAD не обнаружены " +
  47.                   "в реестре.");
  48.           return;
  49.         }
  50.         Console.WriteLine("Обнаружены следующие идентификаторы AutoCAD:");
  51.         foreach (String item in appIds) Console.WriteLine(item);
  52.         // Int32 index = appIds.Length - 1; // Запускаю самую новую версию из доступных
  53.         Int32 index = 0; // Запускаю самую старую версию из доступных
  54. #if CLSID_USE
  55.         Guid guid = guids[index];
  56.         appType = System.Type.GetTypeFromCLSID(guid);
  57. #else
  58.         String strId = appIds[index];
  59.         appType = System.Type.GetTypeFromProgID(strId);
  60. #endif
  61.         // Инициализируем объект приложения AutoCAD
  62.         // (создаём новый процесс acad.exe)
  63.         app = System.Activator.CreateInstance(appType);
  64.  
  65.         fullName = appType.InvokeMember("FullName", System.Reflection.BindingFlags
  66.                 .GetProperty, null, app, new Object[] { }) as String;
  67.         Boolean visible = true; // Пусть AutoCAD отображается на экране
  68.         appType.InvokeMember("Visible", System.Reflection.BindingFlags.SetProperty,
  69.                 null, app, new Object[] { visible });
  70.  
  71.         activeDoc = appType.InvokeMember("ActiveDocument", System.Reflection
  72.                 .BindingFlags.GetProperty, null, app, new Object[] { });
  73.         docType = activeDoc.GetType();
  74.  
  75.         // Для примера, запустим произвольный LISP в командной строке AutoCAD:
  76.         String command = String.Format("(princ \"Hello, {0}\")(princ)\n",
  77.                         Environment.UserName);
  78.         docType.InvokeMember("SendCommand", System.Reflection.BindingFlags
  79.                  .InvokeMethod, null, activeDoc, new Object[] { command });
  80.       }
  81.       catch (Exception ex) {
  82.         Console.WriteLine("Ошибка: {0}", ex.Message);
  83.       }
  84.       Console.WriteLine("FullName: {0}", fullName);
  85.       Console.WriteLine("Нажмите любую клавишу для выхода (AutoCAD " +
  86.               "так же завершит свою работу)...");
  87.       Console.ReadKey();
  88.       if (app != null)
  89.         // Завершаем работу приложения AutoCAD
  90.         appType.InvokeMember("Quit", System.Reflection.BindingFlags.InvokeMethod,
  91.                         null, app, new Object[] { });
  92.     }
  93.   }
  94. }

з.ы. средства динамического вызова не используются из религиозных  соображений?
Пример кода в студию, чтобы я тебя понял :)



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

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

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

  • ADN Club
  • ****
  • Сообщений: 473
  • Карма: 66
Пример кода в студию, чтобы я тебя понял
Я добрался до компьютера, даже специально сделал в C#:
Код - C# [Выбрать]
  1. dynamic acad = Marshal.GetActiveObject("Autocad.Application");
  2. acad.ActiveDocument.SendCommand("(princ \"Hello word via dyn\")\n");

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

  • ADN Club
  • *****
  • Сообщений: 2000
  • Карма: 163
  • Пишу программки...
    • Блог
  • Skype: Compositum78
Цитировать
- Иосиф Виссарионович, в деревне Ивановка живёт ваш двойник!
- Расстрелять.
- А может просто сбрить усы?
- Ну, или так...
:)