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

ADN Club => AutoCAD .NET API => Тема начата: A77X7 от 21-01-2016, 15:18:55

Название: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 21-01-2016, 15:18:55
Добрый день!
Суть проекта - выполнение исходного кода на C# в AutoCAD (динамическая компиляция и всё такое...).
Есть сборка на C# с классом от IExtensionApplication. Грузится в AutoCAD при его запуске (через реестр).
В ней в Initialize и DocumentManager_DocumentCreated подписываемся на нужные события. Здесь ничего особенного...
В итоге имеем обработчики событий типа
 
Код - C# [Выбрать]
  1. doc.CommandEnded += Doc_CommandEnded;

В таких обработчиках код типа:
Код - C# [Выбрать]
  1. compile_and_load_module(ctx);
В ctx всякие параметры для компиляции - пути к исходникам, пути к ссылкам, тип, запускаемый метод и т.п.
 
compile_and_load_module выполняет компиляцию исходников (CSharpCodeProvider.CompileAssemblyFromFile), сохраняет сборку на диске, загружает её в текущий AppDomain (т.е. в AutoCAD), ищет тип (из ctx) и выполняет метод (из ctx).
Скомпилированный метод делает какую-либо полезную работу, например вызывает другие команды AutoCAD...
Код - C# [Выбрать]
  1. var dm = Application.DocumentManager;
  2. var doc = dm.MdiActiveDocument;
  3. var ed = doc.Editor;
  4. ctx.acad_dwg.Editor.WriteMessage("Перед Editor.Command() 5 ...");
  5. await dm.ExecuteInCommandContextAsync(async (obj) =>
  6.   {
  7.     ed.WriteMessage("10000000000000000...");
  8.     ed.Command("_ATTSYNC", "_N", n);
  9.     ed.WriteMessage("20000000000000000...");
  10.   },
  11.   null
  12. );
Вызов команды я пытался сделать и как await ed.CommandAsync и через ExecuteInCommandContextAsync и без неё.
Читал http://through-the-interface.typepad.com/through_the_interface/2014/03/autocad-2015-calling-commands.html (http://through-the-interface.typepad.com/through_the_interface/2014/03/autocad-2015-calling-commands.html) и http://through-the-interface.typepad.com/through_the_interface/2015/03/autocad-2016-calling-commands-from-autocad-events-using-net.html (http://through-the-interface.typepad.com/through_the_interface/2015/03/autocad-2016-calling-commands-from-autocad-events-using-net.html) и много чего ещё.
Там написано, что так всё должно работать и работает, но у меня не работает. Возможно это из-за более длинной цепочки компиляции, загрузки, выполнения и т.п. Раньше это всё у меня работало через acedCmd...
Может кто-нибудь подскажет куда копать?..
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Андрей Бушман от 21-01-2016, 15:25:18
В чём соль сего действа?
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 21-01-2016, 15:27:17
Оперативное изменение поведения модуля (AutoCADа). Т.е. если исходник изменится, то он перекомпилируется и выполнится новая его версия.
(Исходник берётся из сети, централизовано)
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 21-01-2016, 15:27:28
1. Начни с того, что отформатируй код по правилам форума (смотри у меня в подписи).
2. Это касается только команды _ATTSYNC или каких-то еще?
3. Почему вместо ed.Command не воспользоваться Document.SendStringToExecute? Синхронности ты всё-равно не добьешься.
Вообще запуск команды из обработчика события - это "жуткая жуть".
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 21-01-2016, 15:29:08
Оперативное изменение поведения модуля (AutoCADа). Т.е. если исходник изменится, то он перекомпилируется и выполнится новая его версия.
Ты уверен, что выполнилась именно новая версия? Я совершенно нет.
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Андрей Бушман от 21-01-2016, 15:34:00
Т.е. если исходник изменится, то он перекомпилируется и выполнится новая его версия.
Напоминает игру в бадминтон в кукурузе (https://www.youtube.com/watch?v=X_0B4MfRD4o). "Новая версия", говоришь... Но у тебя в рамках этой сессии в твой AppDomain уже загружена предыдущая версия твоей сборки. Это если бы в AutoCAD можно было бы использовать несколько AppDomain вместо одного общего - тогда да, а так то, что ты пытаешься прикрутить - достаточно сомнительная "конструкция" (имхо).
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 21-01-2016, 15:38:28
Да, я с начала сам сомневался, но по факту всё работает именно так. Выполняется последняя загруженная версия.
Это работает уже около 2-х лет как часы. И не только в среде AutoCAD.
Проблема не в этом...
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 21-01-2016, 15:44:39
A77X7
Повторюсь:
1. Начни с того, что отформатируй код по правилам форума (смотри у меня в подписи).
Ну и на остальные мои вопросы ты не ответил.
Выполняется последняя загруженная версия.
Многое зависит от простоты кода. Если в этом коде ты подписываешься на какие-то AutoCAD'овские события, то это верный путь к аварийному завершению AutoCAD.
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 21-01-2016, 15:45:00
1. Начни с того, что отформатируй код по правилам форума (смотри у меня в подписи).
2. Это касается только команды _ATTSYNC или каких-то еще?
3. Почему вместо ed.Command не воспользоваться Document.SendStringToExecute? Синхронности ты всё-равно не добьешься.
Вообще запуск команды из обработчика события - это "жуткая жуть".
1.сейчас...
2.ну пробовал _REGENALL, например, и ещё какие-то свои и встроенные (для отладки), результат один: или eInvalidInput или тишина.
3.собственно синхронность то и нужна и вроде как пишут, что можно этого добиться. Document.SendStringToExecute... ну это только как запасной выход, и то может и не получится то, что задумано
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 21-01-2016, 15:49:45
3.собственно синхронность то и нужна и вроде как пишут, что можно этого добиться.
Очень сильно сомневаюсь. Для синхронности я бы организовал в коде команду (модальную), которую запускал бы из обработчика при помощи  Document.SendStringToExecute, а внутри этой команды попробовал запускать команды уже синхронно.

В любом случае если ты уперся в стену и без помощи Autodesk ты не можешь решить эту задачу, тебе придётся сделать минимальный тестовый проект, который я проверю и если не найду решения, то отправляю в ADN DevHelp.
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 21-01-2016, 15:50:41
Многое зависит от простоты кода. Если в этом коде ты подписываешься на какие-то AutoCAD'овские события, то это верный путь к аварийному завершению AutoCAD.
События не трогаю, по крайней мере пока. В коде делаю модификацию вхождений блоков. И это всё работало нормально, до тех пор пока не потребовалось вызывать команды в 2016ом.
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 21-01-2016, 15:53:17
И это всё работало нормально, до тех пор пока не потребовалось вызывать команды в 2016ом.
А в 2015-ом? А наличие установленного ServicePack для 2016?
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 21-01-2016, 15:54:59
А в 2015-ом?
У нас только 2016й и 2014й (там через acedCmd работает)
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 21-01-2016, 15:57:15
А наличие установленного ServicePack для 2016?
Конкретно сейчас проверяю на AutoCAD 2016 MEP SP1
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 21-01-2016, 15:57:44
А acedCmdS вместо acedCmd в 2016?
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 21-01-2016, 16:01:40
А acedCmdS вместо acedCmd в 2016?
Ээээ... уже запутался... кажется это кончилось тем, что я не нашёл её определение, чтобы его DllImport... Хотя нет, вот же в документации ARX... сейчас попробую (возможно ещё раз)
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 21-01-2016, 16:14:35
Сделал так:
Код - C# [Выбрать]
  1.         [System.Security.SuppressUnmanagedCodeSecurity]
  2.         [DllImport(@"accore.dll", CallingConvention = CallingConvention.Cdecl)]
  3.         extern static int acedCmdS(IntPtr resbuf, bool stub1, IntPtr stub2);
...
Код - C# [Выбрать]
  1.         ResultBuffer rb = new ResultBuffer();
  2.         rb.Add(new TypedValue(5005, "_ATTSYNC"));
  3.         rb.Add(new TypedValue(5005, "_N"));
  4.         rb.Add(new TypedValue(5005, n));
  5.         int stat = acedCmdS(rb.UnmanagedObject, false, IntPtr.Zero);
  6.  
  7.         log("stat=" + stat.ToString() + "\r\n");
До последней строки (где log()) не доходит, исключений нет, тишина просто
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 21-01-2016, 16:19:18
P.S.: Кстати, интересная мысль. А если для проверки ты вместо того, чтобы что-то там компилировать, сохранять, загружать и выполнять - просто запустишь команду из обработчика CommandEnded через DocumentManager.ExecuteInCommandContextAsync, то команда отработает?
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 21-01-2016, 16:44:24
Извиняюсь. У меня паника уже... acedCmdS выполняется, всё отлично.
Счастье то какое!
Ещё перепроверю пару десятков раз...
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 21-01-2016, 16:45:08
До последней строки (где log()) не доходит, исключений нет, тишина просто

Тут я не понял. Если не доходит, то может ждёт чего-то от пользователя. Установи CMDECHO в 1 чтобы в командной строке что-нибудь печаталось.
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 21-01-2016, 16:50:18
Тут я не понял. Если не доходит, то может ждёт чего-то от пользователя. Установи CMDECHO в 1 чтобы в командной строке что-нибудь печаталось.
Всё доходит. Я просто новый файлик с исходником из студии не закинул туда где AutoCAD ждёт...
Прошу прощения
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 21-01-2016, 17:27:32
Подозреваю, что
Код - C# [Выбрать]
  1. extern static int acedCmdS(IntPtr resbuf, bool stub1, IntPtr stub2);
стоит заменить на
Код - C# [Выбрать]
  1. extern static int acedCmdS(IntPtr resbuf, [MarshalAs(UnmanagedType.U1)] bool stub1, IntPtr stub2);
(читать здесь: https://msdn.microsoft.com/en-us/library/ms182206.aspx)
Причина в разнице типов bool в C++ и C#. Впрочем, так как передаётся false (т.е. 0), то возможно разницы и не будет.
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 22-01-2016, 14:39:27
Спасибо за оперативную помощь. Всё теперь работает, проверил и в 2014м и в 2016м.
В итоге получилось вот что:
Код - C# [Выбрать]
  1. #if R17_2 || R18_0 || R18_1 || R18_2
  2.         [System.Security.SuppressUnmanagedCodeSecurity]
  3.         [DllImport(@"acad.exe", CallingConvention = CallingConvention.Cdecl)]
  4.         extern static int acedCmd(IntPtr resbuf);
  5. #elif R19_0 || R19_1
  6.         [System.Security.SuppressUnmanagedCodeSecurity]
  7.         [DllImport(@"accore.dll", CallingConvention = CallingConvention.Cdecl)]
  8.         extern static int acedCmd(IntPtr resbuf);
  9. #elif R20_0 || R20_1
  10.         [System.Security.SuppressUnmanagedCodeSecurity]
  11.         [DllImport(@"accore.dll", CallingConvention = CallingConvention.Cdecl)]
  12.         extern static int acedCmdS(IntPtr resbuf, bool stub1, IntPtr stub2);
  13.         static int acedCmd(IntPtr resbuf)
  14.         {
  15.             return acedCmdS(resbuf, false, IntPtr.Zero);
  16.         }
  17. #endif
  18.  
  19. //...
  20. acedCmd(rb.UnmanagedObject);
  21.  

P.S. По поводу новой версии кода. Да, вы правы, когда речь идёт о том, что мы загружаем (Assembly.Load...) новую версию уже загруженной сборки, если она была где-то скомпилирована. Если же она скомпилирована прямо тут (динамически через CodeProvider), она сразу грузится в AppDomain и изменения вступают в силу.
P.P.S. Не факт, что размер bool == 1 (ибо определяется реализацией), у MS, да == 1. Ну и + там всегда false. Честно говоря, я думал компилятор сам разберётся.
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 22-01-2016, 14:56:06
Я бы вместо условной компиляции написал бы P/Invoke трех функций и вызвал бы нужную в зависимости от версии AutoCAD (Application.Version). Но это на любителя... Что касается замены кода, то акцентирую внимание на то, что ты принудительно подменяешь код, который был ранее. Если в коде были обработчики событий, то (так как ты их не отключаешь), они будут ссылаться на старые, заменённые тобой функции. А это значит, что крах неизбежен. Так что будь очень осторожен.
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 22-01-2016, 15:02:51
P.P.S. Не факт, что размер bool == 1 (ибо определяется реализацией), у MS, да == 1. Ну и + там всегда false. Честно говоря, я думал компилятор сам разберётся.
Как раз нет. Почитай тему: http://adn-cis.org/forum/index.php?topic=1719.0 Там как раз ошибка вылезла именно из-за того, что использовался (по-умолчанию) UnmanagedType.Bool вместо UnmanagedType.I1.
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Дима_ от 22-01-2016, 20:53:22
Я бы вместо условной компиляции написал бы P/Invoke трех функций и вызвал бы нужную в зависимости от версии AutoCAD (Application.Version). Но это на любителя...
Если не секрет - в чем причина предпочтений, если только не из желания загружать одну и ту же сборку в разные версии.
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 22-01-2016, 21:02:49
Если не секрет - в чем причина предпочтений, если только не из желания загружать одну и ту же сборку в разные версии.
Это представь себе сколько конфигураций (и соотвественно количество сборок) нужно иметь для:
Код - C# [Выбрать]
  1. #if R17_2 || R18_0 || R18_1 || R18_2
хотя в большинстве случаев в этом нет необходимости.
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 23-01-2016, 11:30:56
Я бы вместо условной компиляции написал бы P/Invoke трех функций и вызвал бы нужную в зависимости от версии AutoCAD (Application.Version)
Хорошая идея! Я замучался уже делать сборки под разные версии начиная с 2009й и по 2016ю. Я до недавнего времени думал, что если импортированной функции нет по факту, то программа просто не запустится...
Ещё бы вот здесь как-то избежать условной компиляции:
Код - C# [Выбрать]
  1. #if R17_2 || R18_0 || R18_1 || R18_2
  2. using Autodesk.AutoCAD.ApplicationServices;
  3. using Autodesk.AutoCAD.EditorInput;
  4.  
  5. #elif R19_0 || R19_1 || R20_0 || R20_1
  6. extern alias AcCoreMgd;
  7. using AcCoreMgd.Autodesk.AutoCAD.ApplicationServices;
  8. using AcCoreMgd.Autodesk.AutoCAD.ApplicationServices.Core;
  9. using AcCoreMgd.Autodesk.AutoCAD.EditorInput;
  10.  
  11. #endif
Без этого компилятор ругается на Document, что он определён и в CoreMgd и в Mgd...
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 23-01-2016, 12:41:48
А это значит, что крах неизбежен
Изначальная идея была, чтобы можно было оперативно менять только то, что часто меняется (особенно вначале внедрения чего-то нового), т.к. просто подменить dll-ку теперь нельзя (как раньше FreeLibrary), нужно перезапускать AutoCAD (или ещё что-то). Возиться с AppDoamin + IPC тоже не хочется из-за этого почему-то.
На счёт событий проверю, мне интересно стало просто. Отпишусь потом.
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 23-01-2016, 14:38:46
Ещё бы вот здесь как-то избежать условной компиляции:
Сомневаюсь, что это возможно. Во всяком случае не видел готовой реализации. Но уменьшить количество конфигураций явно можно (до 2012 включительно и после).
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: A77X7 от 25-01-2016, 07:41:14
По поводу перекомпиляции с подключенными событиями...

Подписываемся на событие (например, Application.DocumentManager.DocumentLockModeChanged += DocumentManager_DocumentLockModeChanged;).
Событие отрабатывает нормально.
Меняем код, перекомпилируем.
Отписаться от старого события уже не можем (Application.DocumentManager.DocumentLockModeChanged -= DocumentManager_DocumentLockModeChanged;), ничего не происходит (исключение, например).
Подписываемся на новую версию события (Application.DocumentManager.DocumentLockModeChanged += DocumentManager_DocumentLockModeChanged;)
Получаем отработку двух версий одного события.
Отписываемся от события (Application.DocumentManager.DocumentLockModeChanged -= DocumentManager_DocumentLockModeChanged;
Получаем отработку только старой версии события.
Ещё раз отписываемся (Application.DocumentManager.DocumentLockModeChanged -= DocumentManager_DocumentLockModeChanged;), ничего не меняется, работает старая версия события.

Если в старом событии есть вызов метода f1 в этом же типе, то при его (метода f1) изменении работает старая версия метода f1.

Итого получается после перекомпиляции старая реализация не заменяется. Новая реализация "лежит" рядом. Если были ссылки на методы (и, думаю,  другое) в старой реализации они остаются и корректно ссылаются на старую реализацию. При получении ссылок после перекомпиляции, "выдаются" ссылки на новую реализацию.

Ничего не обрушилось. Наверное где-то в МСДН об этом написано. Всё ведь делается штатными средствами, без всякого хака.
Название: Re: Document.Editor.Command(...) и т.п. = eInvalidInput на AutoCAD 2016
Отправлено: Александр Ривилис от 25-01-2016, 21:43:35
Отписаться от старого события уже не можем
Плохо.
Получаем отработку двух версий одного события.
Очень плохо. Ведь мы этого не ожидали.
То что ничего не обрушилось - прекрасно. Но поведение программы стало уже совсем не однозначным.