Is there a way to return the dotNetObject value from a custom 3ds Max plugin?

I have a custom plugin for 3ds Max that interacts with some managed code on the back. In some cases, I would like to forward the managed object to MAXScript for direct interaction, i.e. Return a wrapped object from one of my functions.

MAXScript is able to manipulate managed objects directly relative to another through another plugin (msxdotNet) included with Max (I'm using 3ds Max 2008). It basically wraps an object and uses reflection for late bound calls, but it is completely self-contained and has no sdk impact. The dll plugin itself also does not reveal anything more than the minimum interface required by Max to add multiple top-level script classes.

Scripted classes allow you to create a new object using the constructor

local inst = (dotNetObject "MyPlugin.MyClass" 0 0 "arg3")

      

In my case, I already have an instance of an object that I would like to use.

Is there a way to instantiate the dotNetObject wrapper from my plugin to get back to Max?


Idealy, I would like to have a helper function with a signature (C ++ / CLI) like:

Value* WrapObject(System::Object ^obj);

      

Some basic guarantees I can make:

  • The msxdotNet plugin is already loaded.
  • The msxdotNet plugin and my managed assemblies are in the same AppDomain.

The source for the msxdotNet plugin is included as a sample sdk, but for control / sanity, change and recompilation, this is not an option.

0


source to share


1 answer


I solved this by exploiting the fact that any CLR object wrapped by dotNetObject will automatically wrap return values ​​(method results and property values) with another wrapper. This even applies to static methods and properties of CLR types wrapped in dotNetClass.

Let's say I already have a method in my plugin that allows me to execute arbitrary MAXScript:

Value* EvalScript(System::String ^script);

      

Now I just need to serialize the object to a string and return to the active object (a reference to the same object, not just a copy!).



I do this by grabbing an GCHandle

object using GCHandle::ToIntPtr

to convert it to something blittable and use GCHandle::FromIntPtr

to materialize the same object in a different context. Of course I do this in process (and in the same application domain), it won't work otherwise.

Value* WrapObject(System::Object ^obj)
{
    GCHandle handle = GCHandle::Alloc(obj)
    try
    {
        return EvalScript(System::String::Format(
            L"((dotNetClass \"System.Runtime.InteropServices.GCHandle\").FromIntPtr (dotNetObject \"System.IntPtr\" {0})).get_Target()",
            GCHandle::ToIntPtr(handle));
    }
    finally
    {
        handle.Free();
    }
}

      

The comment I am explaining to this in real code is over 10 times before the actual code.

+2


source







All Articles