Add UserData: Object Reference not set to the instance of an object

Hi, I got this error when add UserData to the attributes of the object.

System.NullReferenceException: Object reference not set to an instance of an object.
   at Rhino.DocObjects.Custom.UserData.NonConstPointer(Boolean createIfMissing)
   at Rhino.DocObjects.Custom.UserDataList.Add(UserData userdata)

I am following this sample

MyUserData class is inherited from DocObjects.Custom.UserData

I am adding user data directly to the attributes of RhinoObject:
robj.Attributes.UserData.Add(MyUserData)

Where can be the problem?

I see it leads to UserData.NonConstPointer but no idea what is wrong there.

Thanks,
Dmitriy

Just digging a bit more i have found the following topic from @menno

Bug has been registered under: https://mcneel.myjetbrains.com/youtrack/issue/RH-23446
And I see the state is open.

@stevebaer, can you please comment? How to overcome this?
I am adding UserData from GH custom component.

This won’t be implemented any time soon. It will take quite a bit of core changes to allow for custom UserData to be defined by something other than a plug-in. I would recommend using the UserDictionary if at all possible.

Thank you, @stevebaer
It is a pity that it is even not planned so far as it limits possibility to write custom classes.

Is it any example of using UserDictionary for some Custom class which is a bit more complicated than key-string value?

Thanks,
Dmitriy

UserDictionary supports a lot more than just string values
http://developer.rhino3d.com/api/RhinoCommon/html/T_Rhino_Collections_ArchivableDictionary.htm

UserString on the other hand is just a dictionary of key/value pairs where all of the values are strings.

Thanks, @stevebaer

UserDictionary works quite good. The easiest solution I found is to serialize/deserialize custom class into/from Byte array.

This won’t be implemented any time soon. It will take quite a bit of core changes to allow for custom UserData to be defined by something other than a plug-in

Hi @stevebaer do you have any updates/plans about addressing this issue?

1 Like

Hi @mingo1214,

It is highly doubtful this will change for Rhino 7. Can you use a user dictionary?

– Dale

@dale Thanks for letting me know, now it seems I have to use user dictionary then:>

Hi @dale, is there any way that I can do some cleaning up with ArchivableDictionary before it is being duplicated/Serialized ?

In UserData, I can override OnDuplicate/Serialize to do so, but this is not an option with ArchivableDictionary.

Thanks,
Mingbo

Hi @mingo1214,

Can you do this clean up in a RhinoDoc.BeginSaveDocument handler?

– Dale

@dale thanks, this will work.

Hi @dale,

There is one issue with this RhinoDoc.BeginSaveDocument, is there anyway to identify if this BeginSaveDocument is triggered before closing document?

Hi @stevebaer and @dale,

I just found out
MyUserData: Rhino.DocObjects.Custom.UserDictionary works within a separated dll library other than rhino plugin.

This surprises me because:
Rhino.DocObjects.Custom.UserDictionary inherits from Rhino.DocObjects.Custom.UserData, but MyUserData2: Rhino.DocObjects.Custom.UserData doesn’t work in a separated dll library (null reference exception when adding it to a geometry).

What’s main difference that makes this Rhino.DocObjects.Custom.UserDictionary useable, while Rhino.DocObjects.Custom.UserData doesn’t?

Is it safe to say: I can move from geometry.UserDictionary back to this custom MyUserData: Rhino.DocObjects.Custom.UserDictionary?

Thanks,
Mingbo

No, sorry that is not safe.

RhinoCommon has the full definition of what a UserDictionary is and knows how to perform all of the serialization of this data into an out of a 3dm file. This is not the case for your own custom userdata derived class. Custom user data has a Guid associated with it written in the 3dm file that tells it what plug-in defines that custom user data class, but for the actual serialization it is up to the plug-in to perform this task. We don’t have this concept in place for non-plugin support library DLLs

Thanks @stevebaer,

I did a little bit more tests today. Yes, there is no issue with adding MyUserData: Custom.UserDictionary to geometry, even this MyUserData is in non-plugin dll.

But as you said, it has issues that saving to 3dm file. The override Write of MyUserData: Custom.UserDictionary is called, I assume the data is saved to 3dm. But Read is never called, and cannot find any data anymore after reopen the file.

Thanks for your confirmation.
I guess I eventually have to give up on Custom.UseData.

1 Like

Sorry for reviving this thread, but I wanted to let those interested know that there is a work around to this: define an Interface.

Define the interface in your common library, and implement it in your plugin. You then can iterate through the UserDataList on an object looking for UserData that implements your interface.

@stevebaer or @dale do you see any pitfalls with using this approach?
Thanks

For Example:
In Common Library

public interface ICustomUserData
{
    public string Notes {get; set;}
    public string Description {get;}
}

In Rhino Plugin

//This GUID is empty, use your own!
[Guid("00000000-0000-0000-0000-000000000000")]
public class CustomUserDataImpl : UserData, ICustomUserData
{
/// Standard Implementation from SampleCSUserData Project ///
}

In Grasshopper Plugin

protected override void SolveInstance(IGH_DataAccess DA)
 {
    var objGuid = Guid.Empty;
    if (!DA.GetData(0, ref objGuid)) return;
    var ro = RhinoDoc.ActiveDoc.Objects.Find(objGuid);
    if (ro == null)
    {
        AddRuntimeMessage(GH_RuntimeMessageLevel.Error, "The Id is not for a Rhino Object");
        return;
    }
    foreach (var ud in ro.Attributes.UserData)
    {
        if (ud is ICustomUserData csd)
        {
            DA.SetData(0, csd.Notes);
            return;
        }
    }
    AddRuntimeMessage(GH_RuntimeMessageLevel.Warning, "The Object Did not contain Custom User Data");
    return; 
}

I don’t see any pitfalls. That seems like a reasonable approach to me.

Hi @mingbo ,

experiencing same issues with UserData.Add(); throwing a null error even though there are no nulls.

What did you end up with as a workaround?

EDIT: We moved our logic of retrieving the data into our main plugin assembly (and not in a referred dll), now it works, as in the post in the top…