Руководство программиста
Глава 2. Создание моделей блоков
§2.4. Инициализация и очистка данных блока
Описывается событие инициализации RDS_BFM_INIT – самое первое событие в «жизни» модели блока, и событие очистки RDS_BFM_CLEANUP – самое последнее. Приводится пример модели, отводящей себе память под личные нужды при инициализации и освобождающей ее при очистке.
Самое первое событие, на которое реагирует блок – это событие инициализации RDS_BFM_INIT. Оно возникает в момент подключения модели к блоку, то есть при загрузке блока из файла в составе схемы, при вставке блока из буфера обмена, при указании новой модели в окне параметров блока и т.п. Как только конкретному блоку схемы ставится в соответствие конкретная функция модели, эта функция вызывается для данного блока с параметром RDS_BFM_INIT. Обычно этот вызов используется для создания личной области данных блока – области памяти, используемой моделью по своему усмотрению. В C личная область данных чаще всего представляет собой структуру, а в C++ – объект какого-либо класса. В личной области хранятся параметры блока, которые неудобно или невозможно хранить в статических переменных – строки, вспомогательные объекты, информация о динамических переменных, с которыми работает блок, и т.п. При инициализации модель должна отвести память под личную область данных (например, функцией malloc или оператором new) и присвоить указатель на созданную область полю BlockData структуры данных блока RDS_BLOCKDATA, указатель на которую передается в функцию модели через параметр BlockData при каждом вызове.
Событие очистки RDS_BFM_CLEANUP – это самое последнее событие, на которое может среагировать блок. Оно возникает тогда, когда по какой-либо причине модель отключается от блока: при удалении блока пользователем, при очистке памяти в момент завершения RDS или при загрузке новой схемы, перед подключением к блоку новой модели и т.п. Если при реакции на RDS_BFM_INIT была создана личная область данных, при реакции на RDS_BFM_CLEANUP она должна быть удалена.
В качестве примера рассмотрим модель блока, работающую с личной областью, в которой будут храниться два параметра: целый IParam и вещественный DParam (пример не имеет практического применения, он просто иллюстрирует реакцию блока на события инициализации и очистки). В этом примере будет приведена и главная функция DLL, и модель блока. В дальнейшем в приводимых примерах главная функция DLL чаще всего будет опущена.
#include <windows.h> #include <RdsDef.h> // Подготовка описаний сервисных функций #define RDS_SERV_FUNC_BODY GetInterfaceFunctions #include <RdsFunc.h> //========== Главная функция DLL ========== int WINAPI DllMain(HINSTANCE /*hinst*/, unsigned long reason, void* /*lpReserved*/) { if(reason==DLL_PROCESS_ATTACH) // Загрузка DLL { // Получение доступа к функциям if(!GetInterfaceFunctions()) RDS_SERV_ERROR_MSGW // Сообщение: старая версия RDS } return 1; } //========= Конец главной функции ========= //====== Класс личной области данных ====== class TTest1Data { public: int IParam; // Целый параметр double DParam; // Вещественный параметр TTest1Data(void) // Конструктор класса { IParam=0; DParam=0.0; rdsMessageBoxW(L"Область создана",L"TTest1Data",MB_OK); }; ~TTest1Data() // Деструктор класса { rdsMessageBoxW(L"Область удалена",L"TTest1Data",MB_OK);}; }; //========================================= //============= Модель блока ============== extern "C" __declspec(dllexport) int RDSCALL Test1(int CallMode, RDS_PBLOCKDATA BlockData, LPVOID ExtParam) { TTest1Data *data; switch(CallMode) { case RDS_BFM_INIT: // Инициализация BlockData->BlockData=new TTest1Data(); break; case RDS_BFM_CLEANUP: // Очистка data=(TTest1Data*)(BlockData->BlockData); delete data; break; } return RDS_BFR_DONE; } //=========================================
Перед функцией модели блока находится описание класса TTest1Data, объект которого используется блоком в качестве личной области данных. В конструкторе и деструкторе класса (функциях, автоматически вызываемых при создании и уничтожении объекта соответственно) вызывается сервисная функция rdsMessageBoxW, выводящая сообщение о создании и удалении объекта. Функция модели Test1 реагирует всего на два события: инициализации RDS_BFM_INIT и очистки RDS_BFM_CLEANUP. При вызове функции с параметром RDS_BFM_INIT создается объект TTest1Data, указатель на который присваивается полю структуры данных блока BlockData. При вызове функции с параметром RDS_BFM_CLEANUP значение BlockData->BlockData приводится к типу «указатель на TTest1Data» (для ясности примера в функцию модели введена вспомогательная переменная data), после чего созданный при инициализации объект уничтожается.
Чтобы проверить работоспособность этого примера необходимо скомпилировать DLL с этой моделью, запустить RDS, создать новую схему и создать в ней новый простой блок (пункт «» в контекстном меню или в меню редактирования). Затем необходимо открыть окно параметров этого блока (пункт «» в контекстном меню блока), выбрать в окне вкладку «», указать путь к скомпилированной DLL и имя экспортированной функции в ней (рис. 15). Именем экспортированной функции, в данном случае, будет «Test1@12», поскольку использовался тридцатидвухбитный компилятор GCC. В других компиляторах именем экспортированной функции может быть просто «Test1» или «_Test1@12» – способ формирования экспортированного имени из имени функции обычно указывается в описании компилятора. При нажатии кнопки «» модель будет подключена к созданному блоку, при этом она будет вызвана с параметром RDS_BFM_INIT и должна вывести сообщение «Область создана». Если после этого удалить созданный блок, перед удалением модель будет отключена от него, что приведет к вызову функции с параметром RDS_BFM_CLEANUP и выводу сообщения «Область удалена». Это же сообщение будет выведено, если закрыть RDS не удаляя блок (в момент отключения модели от блока при очистке памяти перед завершением программы).
Рис. 15. Подключение модели к блоку – после нажатия «» к блоку будет подключена модель «Test1»
из библиотеки «testdll.dll», находящейся в стандартной папке DLL