addin works perfectly when debbugin via Visual Studio although breaks after compiled to .addin and .dll

addin works perfectly when debbugin via Visual Studio although breaks after compiled to .addin and .dll

mateus.komarchesqui
Enthusiast Enthusiast
4,535 Views
13 Replies
Message 1 of 14

addin works perfectly when debbugin via Visual Studio although breaks after compiled to .addin and .dll

mateus.komarchesqui
Enthusiast
Enthusiast

Basically I was facing a System.NullReferenceException and my addin was crashing, so I openned it in VS Community and ran it as debbug, the error simply doesn't happen when in debbug mode. So I thought I had it solved and recompiled it. The error popped again. 

 

To give it a little context, this tools thats broken takes one or more instances of assemblies (representation of tilt ups) and align its origins in order to facilitate seeing it from different angles. The error I mentioned happens when I try to select these instances from my .rvt file. Again, it is very odd and happens only when I inject the addin.dll and addin.addin into the revit folder, while running as debbug it runs smoothly.

0 Likes
Accepted solutions (1)
4,536 Views
13 Replies
Replies (13)
Message 2 of 14

sragan
Collaborator
Collaborator

Without seeing your code I don't think there is any way to guess what might be wrong.

 

0 Likes
Message 3 of 14

mateus.komarchesqui
Enthusiast
Enthusiast

I attached three pictures to the post, the "system null exception" is what I get as error and the "problematic selection" is the problematic piece of code (it's kinda bad written in terms of redundancy but consider that I was debugging it by recompiling the code and pasting .addin and .dll inside revit folder, due to what I explained before).

 

The tool it self can be seen in the last attachment.
 

When the assembly is selected from this list of names I get the error attached, when selected in model I catch the same error in line 79.

0 Likes
Message 4 of 14

ricaun
Advisor
Advisor

Looks like the problem is on the GetListBoxItems method on line 95.

 

Maybe the listBoxItem is null sometimes for some reason.

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

0 Likes
Message 5 of 14

mateus.komarchesqui
Enthusiast
Enthusiast

So I figured... But it doesn't make any sense, actually, I tried this horrible fixup

var listBoxItemTemp = window.AssembliesListBox.ItemContainerGenerator.ContainerFromItem(item);
if (listBoxItemTemp == null) { continue; }
ListBoxItem listBoxItem = listBoxItemTemp as ListBoxItem;
var assemblyType = listBoxItem.Content as AssemblyType;

and I mean... it doesn't crash anymore, no change is aplied to my assembly whatsoever due to "continue" once its not appended to the return list.

Any ideas? 

0 Likes
Message 6 of 14

mateus.komarchesqui
Enthusiast
Enthusiast

I've just put a debug MessageBox inside listBoxItemTemp == null conditional scope and apparently every item from window.AssembliesListBox.Items is null. Looks like ItemContainerGenerator.ContainerFromItem(item) is not finding item, even though it exists...

 

Am I missing something?

0 Likes
Message 7 of 14

ricaun
Advisor
Advisor

Your code look really complex, I have no idea what's happening...

 

You should try to implemented some MVC, using a WPF view with some binding.

Luiz Henrique Cassettari

ricaun.com - Revit API Developer

AppLoader EasyConduit WireInConduit ConduitMaterial CircuitName ElectricalUtils

0 Likes
Message 8 of 14

mateus.komarchesqui
Enthusiast
Enthusiast

For starters I'm an intern and just started coding using revit's API. What I have in hands is a code from a previous employee in which this Null bug was found by a civil engineer whom uses this addin.

 

This specific tool populates a listBox with all assemblies from the current file and disposes it ordered by name. The method GetListBoxItems() returns these assemblies that populated my listBox. In my understanding it's done this way to enable selecting any assembly by name from the list or select it in the model. Doing this when finished selecting, regardless the way it was selected, every element that we want to apply the tool functionality is marked as selected in the listBox list. Is it clear?

 

Allright, after selecting the tool calls other methods that will do some vector transformations in order to align each assembly. Not going into details because this part is working. 

 

I tested in other models, in revit 2020, 2021 and 2022, and this null problem when I try to get the container from each listoBoxItem is kinda unstable... From a models with, lets say, 200 assemblies 100 are considered null as said. For those not null the tool applies changes normally...

Other thing, when I'm debugging from VS Comunity those null references just vanish, and every assembly is found and changeble...

0 Likes
Message 9 of 14

mateus.komarchesqui
Enthusiast
Enthusiast

I might be getting somewhere. The ListBox has an attribute called ItemsSource which binds the list of assemblies to the ViewModel. So, I use a FilteredElementCollector of the document of class AssemblyTypes to get every assembly from the document and afterwards I bind these assemblies to my ViewModel.ItemsSource, so everytime I call ViewModel.Items I get the "span" of my list box (all the assemblies from the file).

What's interesting now is: Everytime I load the file, populate my list box (scrollable list of names of the view model refered as tool.png on message 3) and select from the model (by selecting the assemblies clicking on them, using uidoc.Selection.PickObjects(ObjectType.Element, assemblyFilter, "Select Assemblies") ) only those items that appeared on my viewmodel (I mean those visible in the list box before you scroll it) can be selected and treated as non-null, leaving my non-null count always to 20 items (exact amount of assemblies that can be seen in the picture without scrolling the list box).

If I select every element individually from the list box (holding control key and clicking each one at the time) all the items I clicked on can be used and treated as non-null, the rest ends up beeing null. Sometimes the number of non-null items varies by just scrolling the list box untill the end.

Is this due to some kind of event related to actually clicking each item of my list box? How can I assure every element is non-null?

0 Likes
Message 10 of 14

RPTHOMAS108
Mentor
Mentor

Sounds like the API elements are being garbage collected i.e. those visible (still referenced) are held those not displayed are not. You are dealing with unmanaged objects that aren't held by their managed counterparts indefinitely. It's a common theme for a bunch of API objects you previously collected to turn into a bunch of nulls for that reason. Often it starts with their members returning nulls or exceptions.

 

When you first populate the list how do you do this, do you exit the Revit API context after that point? Is this a modal or modeless dialogue interaction? Have you implemented IExternalEventHandler for modeless? These are likely the mundane questions that'll lead to a quicker solution.

 

I've not read anything that leads me to believe it is debug related. Perhaps it is the case that debug is stopping the natural death of the objects you would normally see if you didn't freeze the moment.

0 Likes
Message 11 of 14

mateus.komarchesqui
Enthusiast
Enthusiast

Ok, the list is populated using 2 methods that are called right after Execute.ShowWindow(). I attached a picture showing those methods. I don't think I exit the context after that... Exiting the context would be closing the document, model, returning Result.Succeeded or something like that? I belive it's a modeless dialoge seen that my class is a implementation of IExternalEventHandler. I did some research and modeless dialogs are those that we can interact with other elements of the model while this external tool is beein run, right? So I think it's modeless because I can hide the viewmodel inorder to select multiple assemblies.

Sorry for beeing kind of lost, I am starting now programming using API...

0 Likes
Message 12 of 14

mateus.komarchesqui
Enthusiast
Enthusiast

Don't know if it's the best way to do it, but what holds the reference to all of the items is the view model list box (window.AssembliesListBox.Items). In the method attached bellow I select items from the model directly. 

pickedReferences holds every item selected (working correctly, holding the right amount os references), listBoxToSelect in the other hand holds all the picked references from the list held in AssembliesListBox (it's determined by the method previously shown in this topic called GetListBoxItems which selects only those not nulls, beeing only items that are visible in listBox at the time the button "Select in model" is clicked. If scrolled, the amount of not null references gets greater). 

0 Likes
Message 13 of 14

RPTHOMAS108
Mentor
Mentor

Modal vs Modeless can be distinguished by ShowDialog and Show methods respectively i.e. how you show the window. ShowDialog stops the program flow at that point waiting for user response whilst Show just shows the Window and continues on, often exiting the context (which is the natural conclusion to all Revit API interaction).

 

You exit the API context if you leave IExternalCommand.Execute, IExternalEventHandler.Execute etc. If you collect items from a document during one of these methods and store them before exiting then they often become unreliable later (from a subsequent context). That is because the managed environment treats them as no longer required and releases them for GC (you should recollect them at the point of use). For that reason I'll often make my own classes representing shallow copies of API objects to go in content controls within modeless dialogues rather than put full API object in content controls. It is often not required to put actual API objects in content controls, a lot of the time all you need is a Name (string) and and ElementId (to link to a recollected element later). Would use UniqueId in work shared scenario.

 

The second thing is to not use Linq directly as a ItemSource since these happen via deferred execution. Every time the ItemSource is referred to it has to execute the query and collect the items from the document. However it doesn't know that it has a valid context at such a point since it is just reacting to what the control is asking in response to redraw, scroll etc. Is better to yield the results to a fixed collection and then repopulate it when a state change is detected in Revit (ensure the re-collection of elements and repopulation of content happens in a valid context). However as I say I would often convert such a fixed collection to stable shallow copies that can exist outside the context.

 

What you are trying to achieve is actually quite simple but the code seems overly complicated.

 

Also ListBoxItem is something that shouldn't actually exist in a world of WPF DataTemplates and data binding. I think the exitance of these object types sends people down the wrong route to start with. I've never used a ListBoxItem in a ListBox or a ComboBoxItem in a ComboBox etc. Perhaps these objects need to exist for inner workings of such controls (I don't know) but using them is a mistake definitely. It is like saying 'I create a new FridgeItem to represent a bottle of milk then I put the FridgeItem representing the bottle of milk in the Fridge'. You find in the end that you can put the object directly in the container (or collection ItemSource points to), create a DataTemplate to define how it looks in that container as an item and never think of it as a ListBoxItem ever again.

 

 

0 Likes
Message 14 of 14

mateus.komarchesqui
Enthusiast
Enthusiast
Accepted solution

Thank you for all the effort and time dedicated to help me, I learned a lot! Even more considering that I am just starting programming using Revit's API. This code was created by the previous employee as mentioned and I had as my first assignment fix this annoying bug.

However, what sorted the problem was a little simpler than what we all thought... I attached a picture to this reply showing what I changed in the .XAML code. Apparently within the code block there is a property called "VirtualizingPanel.IsVirtualizing" with boolean value. When set to true it improves performance occasionally generating certain bugs with memory, setting null to those items that doesn't appear immediately on the screen. This explains how the number of null items varies as I scrolled the list box in a unstable way, getting garbage collected probably...

 

Hope this helps someone facing the same issue latter on.

0 Likes