3D Studio Max: Перезагружаемый плагин
Автор: Роман Байков
При разработке плагина для 3D Studio Max необходимо часто перезапускать студию. Перегружать студию, а тем более в режиме дебага, довольно долго. Было бы удобно перезагружать не студию, а плагин. К сожалению, 3D Studio Max не имеет такой возможности. Если она загрузила плагин, то это раз и навсегда. Описанный здесь способ был подсказан кем-то из участников данного ресурса. Так что авторство идеи не за мной, я лишь решил оформить это в виде подсказки.
Суть метода заключается в следующем. Мы создаём каркас плагина для 3D Studio Max, а весь функционал выносим в отдельную DLL, которую подгружаем непосредственно во время работы плагина, либо другими средствами (через интерфейс или команду).
В качестве примера буду использовать плагин экспорта. Саму разработку плагина с нуля рассматривать не буду. Итак имеем уже готовый плагин экспорта. Во время экспорта студия вызывает метод
int YourExport::DoExport(const TCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPromts, DWORD options) { return IMPEXP_FAIL; }
В этом методе мы и делаем все преобразования из студии в свои и сохраняем файл.
Как мы делаем в классическом случае (обход дерева я не юзаю, поэтому EnumTree вы не увидите):
int YourExport::DoExport(const TCHAR *name, ExpInterface *ei, Interface *i, BOOL suppressPromts, DWORD options) { RetrieveMesh( i); RetrieveMaterial( i); SaveYourFile( name); return IMPEXP_FAIL; }
Когда вы что-либо меняете, вы пересобираете плагин, перезапускаете студию. Меняем мы только наш функционал. То есть функции RetrieveMesh, RetrieveMaterial, SaveYourFile. Выносим эти функции в отдельную DLL. Создаем проект типа DLL, которая только экспортирует функции. В этом проекте реализуем эти функции RetrieveMesh, RetrieveMaterial, SaveYourFile.
Также реализуем одну функцию на экспорт:
extern "C" { __declspec(dllexport) void __cdecl Process( ExpInterface*, Interface*, const TCHAR*, int*, std::wstring&); } // error = возвращаемый код ошибки // errStr = сопроводительное сообщение об ошибке __declspec( dllexport) void __cdecl Process( ExpInterface* peif, Interface* pmif, const TCHAR* file, int* error, std::wstring& errStr) { RetrieveMesh( i); RetrieveMaterial( i); SaveYourFile( name); *error = IMPEXP_SUCCESS; }
Обработка ошибок и прочие мелочи на ваше усмотрение. Компилируем DLL и кладем ее рядом с нашим плагином.
Теперь нам надо загружать эту библиотеку при запросе на экспорт. Я делаю это с помощью следующего индусского кода:
// задача найти нашу библиотеку во всех возможных папках плагинов, // вы можете тут захардкодить c:\\program files..... blah blah // получаем кол-во записей с путями к плагинам в ини файле студии int pluginsDirCount = i->GetPlugInEntryCount(); HMODULE hLib = NULL; // для каждой записи пытаемся загрузить библиотеку for ( int n=0; n<pluginsDirCount; n++) { std::wstring pluginDirectory = i->GetPlugInDir( n); std::wstring libraryPath = pluginDirectory + L"\\YourExportLibrary.dll"; hLib = LoadLibrary( libraryPath.c_str( )); if( hLib ) // библиотека найдена, прерываем цикл break; } // у меня нет обработки случая, когда библиотека не найдена вообще
Затем получим адрес функции из библиотеки. Сначала объявим указатель на неё: