3D Studio Max SDK .NET / C++ 通过 NativePointer 和 Marshal 的互操作性

3D Studio Max SDK .NET / C++ interoperability via NativePointer and Marshal

我正在为 3D Studio Max 开发一个插件。它的核心是用C++编写的,用户界面是用WPF编写的。 Autodesk 提供了其 C++ 类型的 .NET 包装器,每个包装器都实现了一个 INativePointer 接口,提供了一个 System::IntPtr NativePointer 属性。

我曾希望使用 C# 和 C++/CLI 将对这些对象的引用从 C++ 传递到 .NET。但是,NativePointer 返回的地址很接近,但不太正确。例如:

// create instance of a CustAttribute, which max exposed to .NET as ICustAttribute
object instance = CoreInterface.CreateInstance(SClassID.CustAttribute, myCustomAttributeClassID );

// The C++ constructor of my custom attribute runs and the address of the
// created object is 0x45001000 for example.

// cast to ICustAttrib (successfully) - all methods on attrib work.
Autodesk.Max.ICustAttrib attrib = (Autodesk.Max.ICustAttrib)instance;

// get the native pointer
System.IntPtr ptr = attrib.NativePointer;

// Strangely, ptr is equal to 0x45001009

// call my C++/CLI function which casts ICustAttrib to CustAttrib and modifies object
Support.ModifyAttribute( ptr );

Crash! ptr is off by 9 bytes.

奇怪的是,我的 Support.ModifyAttribute( ) 方法(将地址转换为 CustAttrib)崩溃了。地址的值比创建的对象的实际值大 9 个字节。这对于我测试过的所有 3D Studio Max 包装器类型都是一致的。结果我的代码崩溃了。

我的问题是:如何成功地将 3D Studio Max .NET 包装器对象传递回 C++ 以实现互操作性? 3D Studio Max SDK 没有通过示例或文档说明如何将对象的 .NET 实例转换为 C++ 实例,尽管这必须有效,因为它已在整个 SDK 中完成。还有其他人对此有好运吗?

我注意到 this 问题相关但未得到解答。

我正在使用 3D Studio Max 2016 和 VS2013。

在查看 3D Studio Max 提供的可用托管程序集后,我发现 Autodesk.Max.Wrappers.DLL 程序集包含一个名为 CustomMarshaler__typename 的 类 集合,每个类型对应一个通过包装器公开的类型.我能够使用它们来满足我获取本机地址的要求:

// create instance of a CustAttribute, which max exposed to .NET as ICustAttribute
object instance = CoreInterface.CreateInstance(SClassID.CustAttribute, myCustomAttributeClassID );

// The C++ constructor of my custom attribute runs and the address of the
// created object is 0x45001000 for example.

// cast to ICustAttrib (successfully) - all methods on attrib work.
Autodesk.Max.ICustAttrib attrib = (Autodesk.Max.ICustAttrib)instance;

// Get the marshaler for the type - not sure what the string is for
ICustomMarshaler marshaler = Autodesk.Max.Wrappers.CustomMarshalerCustAttrib.GetInstance("what goes there?");


// get the native pointer via the custom marshaler - it isn't clear if there is any type checking internally since MarshalManagedToNative takes on object
System.IntPtr ptr = marshaler.MarshalManagedToNative(attrib);

// Hurrah - ptr is equal to 0x45001000

// call my C++/CLI function which casts intptr to native ::CustAttrib type and modifies it via native methods.
Support.ModifyAttribute( ptr );

// Huzzah - attribute is correctly modified in native C++

效果不错,太棒了。它适用于所有提供的包装器类型:

Autodesk.Max.Wrappers.CustomMarshalerINode.GetInstance("what goes there?");
Autodesk.Max.Wrappers.CustomMarshalerModifier.GetInstance("what goes there?");
Autodesk.Max.Wrappers.CustomMarshalerGUP.GetInstance("what goes there?");

唯一令人烦恼的问题是 CustomMarshalerCustAttrib.GetInstance( String ) 中的字符串的用途。

希望有人会觉得这有用!