我有一个3ds Max的自定义插件,它与后端的一些托管代码进行交互。在某些情况下,我想沿着托管对象转发到MAXScript进行直接交互,即从我的一个函数返回一个包装对象。
MAXScript能够通过Max附带的另一个插件(msxdotNet)直接相对较好地操作托管对象(我正在使用3ds Max 2008)。它基本上包装了一个对象,并使用反射进行后期绑定调用,但它完全是自包含的,并且没有任何sdk暴露。插件dll本身也不会暴露任何超出Max所需的最小接口,以添加一些顶级脚本类。
脚本类允许通过构造函数
实例化一个新对象local inst = (dotNetObject "MyPlugin.MyClass" 0 0 "arg3")
在我的情况下,我已经有了一个我想要使用的对象实例。
有没有办法从我的插件中构建一个dotNetObject包装器的实例来返回Max?
理想情况下,我想要一个带有(C ++ / CLI)签名的辅助函数,类似于:
Value* WrapObject(System::Object ^obj);
我可以做出一些基本保证:
msxdotNet插件的源代码作为sdk示例包含在内,但出于管理/理智的考虑,修改它并重新编译它不是一种选择。
答案 0 :(得分:2)
我已经通过利用dotNetObject包装的任何CLR对象将自动用另一个包装器包装返回值(方法结果和属性值)这一事实解决了这个问题。这甚至适用于使用dotNetClass包装的CLR类型的静态方法和属性。
假设我的插件中已经有一个方法可以让我执行任意MAXScript:
Value* EvalScript(System::String ^script);
现在我只需要将一个对象序列化为一个字符串并再次返回一个活动对象(对同一个对象的引用,而不仅仅是一个副本!)。
我通过抓取对象的GCHandle
来执行此操作,使用GCHandle::ToIntPtr
将其转换为blittable并使用GCHandle::FromIntPtr
在不同的上下文中实现相同的对象。当然,我正在进行此过程(并在相同的应用程序域中),否则这将不起作用。
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();
}
}
我在实际代码中解释这个问题的评论是实际代码的10倍。