Как закрыть открытый не мной объект чертежа?

Автор Тема: Как закрыть открытый не мной объект чертежа?  (Прочитано 9558 раз)

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

Оффлайн WingАвтор темы

  • ADN Club
  • Сообщений: 17
  • Карма: 0
Здравствуйте.

В программе есть функция перерасчета размеров всех объектов чертежа под текущий масштаб отображения.
В ней содержится цикл перебора всех объектов чертежа, открытие каждого на запись, установление размера, и закрытие.
Потом вызывается ads_regen(), и команда завершается.
Между несколькими вызовами команд производится изменение масштаба отображения колесом мышки. Больше ничего.
AutoCAD 2010 sp3.

Проблема в том, что начиная c некоторого раза, один из объектов, а именно AcDbMText,
являющийся annotation object для AcDbLeader, без моего открытия становится уже открытым перед циклом.
Соответственно, с ним никакие операции не удается произвести.
Он становится зависшим объектом. С карты не удаляется.

В поисках решения поставил где мог проверки состояния открытости объектов после закрытия.
Лог не показывает, что закрытие объектов не отрабатывает, повторное закрытие не вызывается.

Вопрос, можно ли принудительно закрыть уже открытый объект по известному AcDbObjectId?
Что с ним вообще можно сделать? удалить?
Известно ли, почему эта ситуация возникает?

Вызовы pLeader->evaluateLeader() и pMText->close() пробовались в той и другой последовательности, проблему это не решило.
Подозреваю, что AcDbMText открывается внутри evaluateLeader(), что в рамках документации метода и объясняет непостоянный характер проявления ошибки,
причем открывается так, что isReadEnabled() и isWriteEnabled() этого не отображают.

Приведу код открытия, масштабирования, и закрытия объектов в цикле

Открытие:
Код - C++ [Выбрать]
  1.         AcDbEntity *pEnt;
  2.         AcDbObjectId objId;
  3.         Acad::ErrorStatus es;
  4.         es = acdbOpenAcDbEntity(pEnt, objId, AcDb::kForWrite);
  5.         if (!pEnt || es!=Acad::eOk)
  6.         {
  7.                 LgE(L"Ошибка открытия примитива. Код " + AS(es) + L". Пропущен.");
  8.                 continue;
  9.         }
// Здесь, начиная с какого-то вызова команды, возникает ошибка 82, иногда 83.

Установка размера надписи, проверка закрытия объекта:
Код - C++ [Выбрать]
  1.         AcDbMText *pMText;
  2.         Acad::ErrorStatus es = acdbOpenAcDbEntity( (AcDbEntity*&)pMText, pLeader->annotationObjId(), AcDb::kForWrite );
  3.         if (es == Acad::eOk)
  4.         {
  5.                 pMText->setTextHeight( scaler );
  6.                 pLeader->evaluateLeader();
  7.                 pMText->close();
  8.                         bool bRead = pMText->isReadEnabled();
  9.                         bool bWrite = pMText->isWriteEnabled();
  10.                         if (bRead || bWrite)
  11.                         {                      
  12.                                 Acad::ErrorStatus escl = pMText->close();
  13.                                 LgFAp( L", MText reclosed, with es: " + AS(int(escl)) + L", Read/Write: " + AS(int(bRead)) + L"/" + AS(int(bWrite)) + L" " );
  14.                         }
  15.         }
  16.         else
  17.         {
  18.                 LgE( L"AcDbLeader anno " + AS(int(pLeader->annotationObjId().asOldId())) + L" open failure, es: " + AS(int(es)) + L"." );
  19.         }

Закрытие объекта и проверка закрытия:
Код - C++ [Выбрать]
  1.                 bool bMText = pEnt->isKindOf(mtxtDesc);
  2.                 pEnt->close();
  3.                 LgFAp( L", closed.");
  4.  
  5.                 if (bMText)
  6.                 {
  7.                         LgFAp( L", reclose" );
  8.                         bool bRead = pEnt->isReadEnabled();
  9.                         bool bWrite = pEnt->isWriteEnabled();
  10.                         if (bRead || bWrite)
  11.                         {                      
  12.                                 Acad::ErrorStatus escl = pEnt->close();
  13.                                 LgFAp( L" ended with es: " + AS(int(escl)) + L", Read/Write: " + AS(int(bRead)) + L"/" + AS(int(bWrite)) + L" " );
  14.                         }
  15.                 }

В логе картина отсутствия проблем с закрытием.
На предыдущей итерации:
... Объект 8 типа AcDbLeader acadId -4207152. Открыт. ... Processed, closed.
... Объект 9 типа AcDbMText acadId -4207168. Открыт. ... Processed, closed., reclose
На следующей итерации:
... Объект 8 типа AcDbLeader acadId -4207152. ....
    AcDbLeader anno -4207168 open failure, es: 82. Processed, closed.
... Объект 9 типа AcDbMText acadId -4207168.
    Ошибка открытия примитива. Код 82. Пропущен.

Спасибо.
« Последнее редактирование: 19-01-2016, 20:52:42 от Александр Ривилис »

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
Вопрос, можно ли принудительно закрыть уже открытый объект по известному AcDbObjectId?
Нет. Ты его даже открыть не можешь, так что и закрыть его нельзя.
Код не анализировал. Ты почему-то поленился его нормально отформатировать (см. мою подпись).
Рекомендую использовать интеллектуальные указатели (AcDbObjectPointer<...>) - проблем с открытием/закрытием не будет.
Обрати еще внимание на метод AcDbObject::isReallyClosing - прочитай его описание.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

Оффлайн Николай Горлов

  • ADN
  • *
  • Сообщений: 238
  • Карма: 34
смущает лог файл. во втором куске кода мтекст закрывается и его if (bRead || bWrite) не отрабатывает, т.к. его открывали через завязку на pLeader. а в третьем куске у нас судя по всему тот же самый мтекст, но уже открытый из первого куска ))). сложно что-то сказать по обрывкам кода. лучше какой-то тестовый упрощенный, но целый код, чтоб можно было проследить всю цепочку открытия-закрытия объектов.

Рекомендую использовать интеллектуальные указатели (AcDbObjectPointer<...>) - проблем с открытием/закрытием не будет.
ну, с указателями тоже случаются чудеса. я пару дней поигрался с указателями, плюнул и переделал на древний вариант открытия объекта. вот код. специально вынес в отдельную функцию (если пользоваться вариантом 1, то всё работает, а вот с указателями не хочет):
Код - C++ [Выбрать]
  1. static void LCTSoftpointertest ()
  2. {
  3.         AcDbObjectId eId;
  4.         //AcDbCurve * pCurve;//вариант 1
  5.         AcDbObjectPointer<AcDbCurve> pCurve;//вариант 2
  6.         ads_name ent;
  7.         AcGePoint3d ptFrom, ptTo;
  8.         ACHAR mode[133];
  9.         bool res = true;
  10.         while (res)
  11.         {
  12.                 acedInitGet(RSG_NONULL,NULL);
  13.                 if(acedEntSel(_T("\nУкажите кривую: "),ent,asDblArray(ptFrom))!=RTNORM) return;
  14.  
  15.                 AcGeMatrix3d matWcs;
  16.                 matWcs.setCoordSystem(AcGePoint3d(0.0,0.0,0.0),AcGeVector3d::kXAxis,AcGeVector3d::kYAxis,AcGeVector3d::kZAxis);
  17.                 acedSetCurrentUCS(matWcs);//WCS
  18.  
  19.                 if(acdbGetObjectId(eId, ent)!= Acad::eOk) return;
  20.                 //->вариант 1
  21.                 //if (acdbOpenObject(pCurve,eId,AcDb::kForRead) != Acad::eOk)
  22.                 //<-
  23.  
  24.                 //->вариант 2
  25.                 pCurve.open(eId,AcDb::kForRead);
  26.                 if (pCurve.openStatus() != Acad::eOk)
  27.                 //<-
  28.                         acedAlert(_T("Это не кривая"));
  29.                 else
  30.                         res = false;
  31.         }
  32.  
  33.         while(true)
  34.         {
  35.                 acedInitGet (RSG_OTHER, _T("Р Д Т Ч"));
  36.                 switch (acedGetKword(_T("\n[<Раз>/Два/Три/Четыре]: "), mode))
  37.                 {
  38.                         case RTCAN:
  39.                         case RTERROR:
  40.                                 acedRestorePreviousUCS();
  41.                                 return;
  42.  
  43.                         case RTNONE:
  44.                                 _tcscpy(mode,_T("Р"));
  45.                                 break;                                 
  46.                 }
  47.  
  48.                 acedInitGet(RSG_NONULL,NULL);
  49.                 if(acedGetPoint(NULL,_T("\nПервая точка: "),asDblArray(ptFrom))!=RTNORM)break;
  50.                 pCurve->getClosestPointTo(ptFrom,ptFrom,Adesk::kFalse);
  51.  
  52.                 acedInitGet(RSG_NONULL,NULL);
  53.                 if(acedGetPoint(asDblArray(ptFrom),_T("\nВторая точка: "),asDblArray(ptTo))!=RTNORM)break;
  54.                 pCurve->getClosestPointTo(ptTo,ptTo,Adesk::kFalse);
  55.         }
  56.         acedRestorePreviousUCS();
  57. }
  58.  
  59. ACED_ARXCOMMAND_ENTRY_AUTO(CRullerApp, LCTSoft, pointertest, pointertest, /*ACRX_CMD_TRANSPARENT*/ACRX_CMD_MODAL, NULL) // ни тот ни другой флаг не работают
  60.  
ну и как воспроизвести чудеса )))
1. запускаем команду
2. выбираем полилинию
3. выбираем любую из опций
4. клацаем первую точку, как просит командная строка
5. правой кнопкой мышки клацаем по кнопке "объектная привязка" и в контекстном меню, выпавшем в ответ на клацанье, выбираем пункт "Настройка..."
6. открывается окно "Режимы рисования" с текущей закладкой "Объектная привязка". просто закрываем его нажав "Ok"
7. клацаем вторую точку, как просит команда. и вуаля, вылет.

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
7. клацаем вторую точку, как просит команда. и вуаля, вылет.
Нету вылета (или я чего-то не понял):



Хотя как по мне код "грязный":
1. Я всегда предпочитаю в момент обращения к пользователю иметь все примитивы "закрытыми".
2. Я не пользуюсь методом open для AcDbObjectPointer, открываю его в конструкторе.
3. Какую UCS ты восстанавливаешь, если в первом цикле можешь десять раз установить WCS?

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

Оффлайн WingАвтор темы

  • ADN Club
  • Сообщений: 17
  • Карма: 0
"смущает лог файл. во втором куске кода мтекст закрывается и его if (bRead || bWrite) не отрабатывает, т.к. его открывали через завязку на pLeader"
это проверка проверить состояние мтекст после закрытия. мало ли, указатель в это время еще валиден, и если там счетчик открытий, это может быть показано через bRead и bWrite
а открытия собственно могли быть только в evaluateLeader, больше мне нечего подозревать
других средств контроля все равно не видно, только если повторно открывать, это я пробовал раньше, тоже вообще не было ошибок, поэтому сюда этот кусок кода не постил

" лучше какой-то тестовый упрощенный, но целый код, чтоб можно было проследить всю цепочку открытия-закрытия объектов."
в том и дело, что постоянной утечки объектов нет. она появляется на каком-то вызове команды, иногда на 3м, иногда на 5м, поэтому в целом вопрос не к цепочке

"Нет. Ты его даже открыть не можешь, так что и закрыть его нельзя."
спасибо

"Ты почему-то поленился его нормально отформатировать"
не знал о новых правилах

"Вызовы pLeader->evaluateLeader() и pMText->close() пробовались в той и другой последовательности, проблему это не решило."
написал это, засомневался, но время заканчивалось и запостил, потом решил еще раз местами их поменять, и проблема перестала проявляться
но ведь так и было изначально. но ладно, главное, что не проявляется
получается, что AcDbMText должен быть открыт во время вызовов его AcDbLeader?
начал в других местах менять по этому принципу, и еще сюрприз:
AcDbLeader::attachAnnotation() при открытом AcDbMText возвращает 83 (ошибка из-за открытого на запись объекта)
получается, для одних вызовов AcDbLeader его AcDbMText должен быть открыт, для других закрыт?
жаль, что в документации на методы AcDbLeader Автокада 2010 об этом ничего нет
а сейчас в 2016м наверное и в самом Автодеске никто не скажет, что там в коде было в 2008м или 2009м году

из хелпа:
virtual Acad::ErrorStatus evaluateLeader();
Description
"Evaluate the relation of the leader to its associated annotation, and update the leader geometry if necessary"
я не знаю, при каких условиях у него наступает это "necessary", поэтому быстро написать некий тестовый код не вижу как
и у меня нет готового кода по выставлению нужного масштаба отображения на основе имеющихся залогированных значений
поэтому пока остается путь анализа кодов ошибок и экспериментов

Оффлайн Николай Горлов

  • ADN
  • *
  • Сообщений: 238
  • Карма: 34
Нету вылета (или я чего-то не понял):
Александр, а Вы воду на расстоянии не заряжаете? :):):) вчера, когда писал инструкцию и сам тестовый пример, проделал всё это в автокаде. получил окно с сообщением m_ptr = NULL || m_ptr->isReadEnabled()...(что там дальше не помню) сегодня посмотрел видео и решил перепроверить в 2016 автокаде (просто я тестировал в 2014). перекомпилил пример, запустил, работает. потом то же самое сделал для 2015-2012. всё везде работает. а вчера НЕ работало. комп не перегружал, святой водой не кропил ))). В общем, если проявится еще - сниму видео.

Какую UCS ты восстанавливаешь, если в первом цикле можешь десять раз установить WCS?
согласен ))), промашка вышла. нужно изменение ПСК ставить после первого while.


)))получилось повторить. сборка Debug. автокад 2014. в 2016 работает хорошо

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
получается, для одних вызовов AcDbLeader его AcDbMText должен быть открыт, для других закрыт?
В 99.9% случаев если методу передаётся AcDbObjectId, то объект, на который указывает этот AcDbObjectId, должен быть закрыт.
Кстати, пользуйся функцией acadErrorStatusText для печати информации о коде ошибки. А то каждый раз лазить в acadstrc.h чтобы по коду ошибки понять что она значит достаточно неудобно.
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

  • Administrator
  • *****
  • Сообщений: 13882
  • Карма: 1787
  • Рыцарь ObjectARX
  • Skype: rivilis
)))получилось повторить. сборка Debug. автокад 2014. в 2016 работает хорошо
Вечером проверю. Хотя мне кажется, что там (в .h-файле) лишняя проверка:
Код - C++ [Выбрать]
  1.     DbObjPtr_Assert(m_ptr == NULL || m_ptr->isReadEnabled());
Не забывайте про правильное Форматирование кода на форуме
Создание и добавление Autodesk Screencast видео в сообщение на форуме
Если Вы задали вопрос и на форуме появился правильный ответ, то не забудьте про кнопку Решение

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

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

Оффлайн WingАвтор темы

  • ADN Club
  • Сообщений: 17
  • Карма: 0
"В 99.9% случаев если методу передаётся AcDbObjectId, то объект, на который указывает этот AcDbObjectId, должен быть закрыт.
Кстати, пользуйся функцией acadErrorStatusText для печати информации о коде ошибки. А то каждый раз лазить в acadstrc.h чтобы по коду ошибки понять что она значит достаточно неудобно."

спасибо