Есть ли способ вернуть значение dotNetObject из пользовательского плагина 3ds Max? - PullRequest
0 голосов
/ 07 января 2009

У меня есть собственный плагин для 3ds Max, который взаимодействует с некоторым управляемым кодом на серверной части. В некоторых случаях я хотел бы пересылать управляемый объект в MAXScript для прямого взаимодействия, то есть возвращать обернутый объект из одной из моих функций.

MAXScript способен относительно неплохо манипулировать управляемыми объектами напрямую через другой плагин (msxdotNet), включенный в Max (я использую 3ds Max 2008). Он в основном оборачивает объект и использует отражение для поздних связанных вызовов, но он полностью самодостаточен и не имеет никакого воздействия SDK. Сам плагин dll также не предоставляет ничего, кроме минимального интерфейса, требуемого Максом для добавления нескольких классов сценариев верхнего уровня.

Скриптовые классы позволяют создавать новый объект с помощью конструктора

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

В моем случае у меня уже есть экземпляр объекта, который я хотел бы использовать.

Есть ли способ создать экземпляр оболочки dotNetObject из моего плагина, чтобы вернуться к Максу?


В идеале, я хотел бы иметь вспомогательную функцию с сигнатурой (C ++ / CLI), похожую на:

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

Некоторые основные гарантии, которые я могу дать:

  • Плагин msxdotNet уже загружен.
  • Плагин msxdotNet и мои управляемые сборки находятся в одном домене приложений.

Источник для плагина msxdotNet включен в качестве примера sdk, но для управления / здравомыслия его модификация и перекомпиляция невозможны.

1 Ответ

2 голосов
/ 14 апреля 2009

Я решил это, используя тот факт, что любой объект CLR, обернутый dotNetObject, будет автоматически переносить возвращаемые значения (результаты метода и значения свойств) с помощью другой оболочки. Это относится даже к статическим методам и свойствам типов CLR, заключенных в dotNetClass.

Допустим, у меня уже есть метод в моем плагине, который позволяет мне выполнять произвольный MAXScript:

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

Теперь мне просто нужно сериализовать объект в строку и вернуться обратно к активному объекту (ссылка на тот же объект, а не просто копия!).

Я делаю это, захватывая GCHandle объекта, используя GCHandle::ToIntPtr, чтобы преобразовать его во что-то легкое, и используя 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 раз превосходит фактический код.

...