The Global ARCHICAD Community

Stay informed. Get help. Share your knowledge.

Discussions about using GRAPHISOFT's tools (API DevKit) for independent software developers

Moderators: ejrolon, Barry Kelly, Karl Ottenstein, LaszloNagy, gkmethy, Mihály Palenik, Tibor Lorántfy, MOREH Tamas, Akos Somorjai, Ed Brown

#271507
Greetings, I'm having problems when using two Notification handlers. Basically it's a palette that uses SelectionChangeHandler to detect selected element (if wall is selected, it will show stuff) then ElemObserverHandler to detect changes from that element so the palette can update automatically. I attach elems to the observer in the SelectionChangeHandler function if it detects valid selection.

The crash happens when I do the following:
1) Open up the palette + Init handlers
2) Select an elem
3) Delete the selected elem
4) Select another elem
5) Undo-delete (Ctrl+Z). Archicad crashes immediately right after undo is pressed

Further testing:
1-3) Do actions
4) If I did not select an elem after deletion and went directly to undo-delete = no crash

Another scenario:
1-3) Do actions
4) If I multi-select elems after deletion then pressed undo = no crash

Another scenario:
1-4) Do actions
5) Un-select it, select another elem (repeated x times)
6) Undo-delete:
6a) If it has a selected elem = crash
6b) If it has no selected = no crash

Below is the code for the handlers, I've emptied the Elem Observer and palette (its created but no dialog items) so I know the SelectionChangeHandler has the problem.
Code: Select allAPI_Guid   g_guidPrev = APINULLGuid;
bool      g_hasSelected = false;

GSErrCode Observer_DetachAllElems()
{
   API_Elem_Head**     elemHead;
   Int32               nElems = 0;
   GSErrCode err = ACAPI_Element_GetObservedElements(&elemHead, &nElems);
   if (err != NoError)
   {
      WriteReport("["__FUNC__"] ERROR %s - ACAPI_Element_GetObservedElements", ErrId2Name(err));
      return err;
   }
   for (Int32 i = 0; i < nElems; i++)
   {
      err = ACAPI_Element_DetachObserver(&(*elemHead)[i]);
      if (err != NoError) WriteReport("["__FUNC__"] ERROR %s - ACAPI_Element_DetachObserver()", ErrId2Name(err));
   }
   BMKillHandle((GSHandle*)&elemHead);

   return err;
}

GSErrCode Observer_AttachElem(API_Guid guid)
{
   // Get element head
   API_Elem_Head head;
   BNZeroMemory(&head, sizeof(head));
   head.guid = guid;
   GSErrCode err = ACAPI_Element_GetHeader(&head, APIElemMask_FloorPlan);
   if (err != NoError)
   {
      WriteReport("["__FUNC__"] ERROR %s - ACAPI_Element_GetHeader()", ErrId2Name(err));
      return err;
   }
   // Attach elem to observer
   err = ACAPI_Element_AttachObserver(&head, NULL);
   if (err != NoError)
      WriteReport("["__FUNC__"] ERROR %s - ACAPI_Element_AttachObserver()", ErrId2Name(err));
   return err;
}

static GSErrCode __ACENV_CALL   ElemObserverEventHandler(const API_NotifyElementType* /*elemType*/)
{
   // Empty function
   return NoError;
}   // ElemObserverEventHandler

GSErrCode InitializeElemObserverHandler()
{
   const GSErrCode err = ACAPI_Notify_InstallElementObserver(ElemObserverEventHandler);
   if (err != NoError) DBGPrintlnf("[Error] %s "__FUNC__" - ACAPI_Notify_InstallElementObserver(ElemObserverEventHandler)", ErrId2Name(err));
   return err;
}


static GSErrCode __ACENV_CALL   SelectionChangeHandler(const API_Neig *selElemNeig)
{
   if (selElemNeig == nullptr)
      return NoError;
   if (selElemNeig->neigID == APINeig_None)
   {
      WriteReport("All elements deselected");
      if (g_hasSelected)   // Deselected (currently no selection & previously has selection)
      {
         g_hasSelected = false;
         // Detach previous elem
         Observer_DetachAllElems();
      }
      return NoError;
   }

   API_Neig** selNeigs;
   API_SelectionInfo selectionInfo;
   BNZeroMemory(&selectionInfo, sizeof(selectionInfo));
   GSErrCode err = ACAPI_Selection_Get(&selectionInfo, &selNeigs, true);
   BMKillHandle((GSHandle*)&selectionInfo.marquee.coords);
   if (err == APIERR_NOSEL)
   {
      WriteReport("["__FUNC__"] ERROR %s - ACAPI_Selection_Get()", ErrId2Name(err));
      err = NoError;
      if (g_hasSelected)   // Deselected (currently no selection & previously has selection)
      {
         g_hasSelected = false;
         // Detach previous elem
         Observer_DetachAllElems();
         BMKillHandle((GSHandle*)&selNeigs);
         return err;
      }
   }
   if (err != NoError)
   {
      WriteReport("["__FUNC__"] ERROR %s - ACAPI_Selection_Get()", ErrId2Name(err));
      BMKillHandle((GSHandle*)&selNeigs);
      return err;
   }

   if (selectionInfo.typeID != API_SelEmpty)
   {
      // collect indexes of selected dimensiosn
      const UInt32 nSel = BMGetHandleSize((GSHandle)selNeigs) / sizeof (API_Neig);
      if (nSel != 1)
      {   // exit since invalid selection, accept only single select
         BMKillHandle((GSHandle*)&selNeigs);
         return err;
      }
      // Valid selection: single select
      API_Guid guid = (*selNeigs)[0].guid;
      BMKillHandle((GSHandle*)&selNeigs);
      if (g_guidPrev == guid) // Exit now if selected elem did not change
         return NoError;

      // Detach previous elem
      Observer_DetachAllElems();

      g_guidPrev = guid;
      g_hasSelected = true;

      // Check if elem is already being observed
      API_Elem_Head**     elemHead;
      Int32               nElems = 0;
      err = ACAPI_Element_GetObservedElements(&elemHead, &nElems);
      if (err != NoError)
      {
         WriteReport("["__FUNC__"] ERROR %s - ACAPI_Element_GetObservedElements", ErrId2Name(err));
         return err;
      }
      for (Int32 i = 0; i < nElems; i++)
      {
         if ((*elemHead)[i].guid == guid) // Exit if already observed
         {
            BMKillHandle((GSHandle*)&elemHead);
            return NoError;
         }
      }
      BMKillHandle((GSHandle*)&elemHead);

      // Attach elem to observer
      Observer_AttachElem(guid);
   }

   return NoError;
}   // SelectionChangeHandler

GSErrCode InitializeSelectionChangeHandler()
{
   const GSErrCode err = ACAPI_Notify_CatchSelectionChange(SelectionChangeHandler);
   if (err != NoError) DBGPrintlnf("[Error] %s "__FUNC__" - ACAPI_Notify_CatchSelectionChange(SelectionChangeHandler)", ErrId2Name(err));
   return err;
}
Last edited by Erenford on Wed Sep 13, 2017 2:53 am, edited 1 time in total.
#271521
Erenford wrote:Greetings, I'm having problems when using two Notification handlers. Basically it's a palette that uses SelectionChangeHandler to detect selected element (if wall is selected, it will show stuff) then ElemObserverHandler to detect changes from that element so the palette can update automatically. I attach elems to the observer in the SelectionChangeHandler function if it detects valid selection.

Have you debugged into this code to determine where the crash occurs? That might yield valuable clues to the source of the problem.
#271548
Ralph Wessel wrote:Have you debugged into this code to determine where the crash occurs? That might yield valuable clues to the source of the problem.

Yes I forgot to mention that. I've tried but the crash occurs even before entering the SelectionChangeHandler function. I inserted a debug print at the very start of the function, did the actions above, and it crashes before printing it, immediately right after pressing undo.

Also I've attached the BugReport dump maybe it may help. I've noticed that my addon isn't listed in the call stack, usually its there and I can pinpoint which part of my code it last ran.

This is in AC 20 6005 by the way.
Attachments
(9.33 KiB) Downloaded 30 times
#271607
[WORKAROUND]
Ok I did a workaround for this issue by removing the Elem Observer entirely. I noticed that SelectionChangeHandler gets called anyway at probably most user action so I figured I can check the elem change in there instead of a separate handler. I tested it and it seems to work fine.

I still don't know what the cause of the crashes though.
#271609
Erenford wrote:[WORKAROUND]
Ok I did a workaround for this issue by removing the Elem Observer entirely. I noticed that SelectionChangeHandler gets called anyway at probably most user action so I figured I can check the elem change in there instead of a separate handler. I tested it and it seems to work fine.

I still don't know what the cause of the crashes though.

What was the element observer doing? It wasn't trying to work on a deleted element by any chance?
#271640
Ralph Wessel wrote:What was the element observer doing? It wasn't trying to work on a deleted element by any chance?

No I only used the APINotifyElement_Change event to check new values of elem. Both event handlers were calling the same function (get elem from database then update the palette). Don't worry though as I always check if it exists or has no error otherwise it will exit the function / handler.

After I came upon the crashes I emptied the ElemObserverEventHandler to test if its the cause, but it still crashes.
#271682
Mihály Palenik wrote:Hello Erenford,

Thank you your comment. Unfortunately this is an ArchiCAD bug which is already in our bug database. We will try to do the best to fix it and place into the next update.

Great thanks for the notification, I'll mark this as solved then. :D