Полный исходный текст на языке C++ для библиотеки (DLL) с примером модуля автокомпиляции моделей блоков для языка C. В пример добавленв возможность подключения моделей к блокам и редактирование моделей. Компиляция пока не реализована.
Изменения относительно §3.2 выделены цветом.
#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 { // Получение доступа к функциям RDS if(!GetInterfaceFunctions()) RDS_SERV_ERROR_MSGW // Сообщение: старая версия RDS } return 1; } //========= Конец главной функции =========// Имена секций файла модели // Начало файла #define TCTEXTSECTION_START "$TESTCMODEL" // Секция переменных #define TCTEXTSECTION_VARS "$VARS" // Секция исходного текста #define TCTEXTSECTION_PROG "$PROG" //=========================================// Класс личной области данных модуля автокомпиляции class TCAutoCompData { private: // Пути char *CompPath; // к компилятору char *LinkPath; // к редактору связей (link) char *IncludePath; // к файлам заголовков char *LibPath; // к библиотекам // Нужно ли добавлять путь к exe-файлу компилятора и редактора связей BOOL CompSetPath,LinkSetPath; // Параметры командной строки char *CompParams; // компилятора char *LinkParams; // редактора связей // Параметры исходного текста char *DllMainName; // имя главной функции DLL char *ModelFuncHdr; // заголовок функции модели char *Exported; // экспортированное имя функции // Освободить все динамические строки void FreeAllStrings(void) { rdsFree(CompPath); rdsFree(LinkPath); rdsFree(IncludePath); rdsFree(LibPath); rdsFree(CompParams); rdsFree(LinkParams); rdsFree(DllMainName); rdsFree(ModelFuncHdr); rdsFree(Exported); }; public: // Чтение параметров модуля из INI-файла void ReadFromIni(const char *IniFileName); // Запись параметров модуля в INI-файл void WriteToIni(const char *IniFileName); // Настройка параметров модуля void Setup(const char *IniFileName);// Создать новую пустую модель void CreateEmptyModel(void); // Выбрать существующий файл модели void ConnectExistingModel(const char *oldmodel); // Проверить возможность подключения модели к блоку int CanAttachBlock(RDS_COMPCANATTACHBLKDATA *param); // Открыть редактор модели void OpenEditor(RDS_OPENEDITORDATA *param);// Конструктор класса TCAutoCompData(void) { IncludePath=LibPath=NULL; CompSetPath=LinkSetPath=TRUE; #ifdef RDS_WIN64 CompPath=rdsDynStrCopy( "$RDS$\\mingw-w64\\bin\\g++.exe"); LinkPath=rdsDynStrCopy(CompPath); CompParams=rdsDynStrCopy( "-Wall -m64 -g -I\"$RDSINCLUDE$\" -c \"$TEMP$\\$NAME$.cpp\" -o \"$TEMP$\\$NAME$.o\"" ); LinkParams=rdsDynStrCopy( "-shared -static -static-libgcc -static-libstdc++ -Wl,--dll \"$TEMP$\\$NAME$.o\" -o \"$TEMP$\\$NAME$.dll\" -s -m64 -luser32" ); DllMainName=rdsDynStrCopy("extern \"C\" __declspec(dllexport) BOOL APIENTRY DllMain"); ModelFuncHdr=rdsDynStrCopy( "extern \"C\" __declspec(dllexport) int RDSCALL autocompModelFunc" ); Exported=rdsDynStrCopy("autocompModelFunc"); #else CompPath=rdsDynStrCopy( "$RDS$\\tdm-gcc\\bin\\g++.exe"); LinkPath=rdsDynStrCopy(CompPath); CompParams=rdsDynStrCopy( "-shared -fpermissive -c -I\"$RDSINCLUDE$\" \"$TEMP$\\$NAME$.cpp\"" ); LinkParams=rdsDynStrCopy( "-shared -static -static-libgcc -static-libstdc++ -o \"$TEMP$\\$NAME$.dll\" \"$TEMP$\\$NAME$.o\"" ); DllMainName=rdsDynStrCopy("extern \"C\" BOOL WINAPI DllMain"); ModelFuncHdr=rdsDynStrCopy( "extern \"C\" __declspec(dllexport) int RDSCALL autocompModelFunc"); Exported=rdsDynStrCopy("autocompModelFunc@12"); #endif }; // Деструктор класса ~TCAutoCompData(){ FreeAllStrings(); }; }; //========================================= // Запись параметров модуля в INI-файл void TCAutoCompData::WriteToIni(const char *IniFileName) { RDS_HOBJECT ini=rdsINICreateTextHolder(TRUE); // Загружаем старый текст из файла во вспомогательный объект, если он есть if(rdsFileExists(IniFileName,NULL,NULL,NULL)) rdsSetObjectStr(ini,RDS_HINI_LOADFILE,0,IniFileName); // Создаем секцию [Paths...] rdsSetObjectStr(ini,RDS_HINI_CREATESECTION,0,"Paths" RDS_PLATFORMNAME); // Записываем в нее пути rdsINIWriteString(ini,"Compiler",CompPath); rdsINIWriteString(ini,"Linker",LinkPath); rdsINIWriteString(ini,"Include",IncludePath); rdsINIWriteString(ini,"Lib",LibPath); rdsINIWriteBool(ini,"CompilerToPaths",CompSetPath); rdsINIWriteBool(ini,"LinkerToPaths",LinkSetPath); // Создаем секцию [Params...] rdsSetObjectStr(ini,RDS_HINI_CREATESECTION,0,"Params" RDS_PLATFORMNAME); // Записываем в нее параметры командной строки rdsINIWriteString(ini,"Compiler",CompParams); rdsINIWriteString(ini,"Linker",LinkParams); // Создаем секцию [Func...] rdsSetObjectStr(ini,RDS_HINI_CREATESECTION,0,"Func" RDS_PLATFORMNAME); // Записываем в нее остальные параметры rdsINIWriteString(ini,"DllMain",DllMainName); rdsINIWriteString(ini,"ModelFunc",ModelFuncHdr); rdsINIWriteString(ini,"Exported",Exported); // Сохраняем получившееся в файл rdsSetObjectStr(ini,RDS_HINI_SAVEFILE,0,TCAUTOCOMP_INI); // Уничтожение вспомогательного объекта rdsDeleteObject(ini); } //========================================= // Чтение параметров модуля из INI-файла void TCAutoCompData::ReadFromIni(const char *IniFileName) { RDS_HOBJECT ini=rdsINICreateTextHolder(TRUE); // Загружаем текст из файла во вспомогательный объект rdsSetObjectStr(ini,RDS_HINI_LOADFILE,0,IniFileName); // Проверяем, нет ли ошибок if(rdsCommandObject(ini,RDS_HINI_GETLASTERROR)) // Ошибка return; // Читаем строки из секции [Paths...] if(rdsINIOpenSection(ini,"Paths" RDS_PLATFORMNAME)) { char *CompPath_old=CompPath, // Старые значения *LinkPath_old=LinkPath, *IncludePath_old=IncludePath, *LibPath_old=LibPath; // Загружаем новые строки CompPath=rdsDynStrCopy(rdsINIReadString(ini,"Compiler",CompPath_old,NULL)); LinkPath=rdsDynStrCopy(rdsINIReadString(ini,"Linker",LinkPath_old,NULL)); IncludePath=rdsDynStrCopy(rdsINIReadString(ini,"Include",IncludePath_old,NULL)); LibPath=rdsDynStrCopy(rdsINIReadString(ini,"Lib",LibPath_old,NULL)); // Освобождаем старые строки rdsFree(CompPath_old); rdsFree(LinkPath_old); rdsFree(IncludePath_old); rdsFree(LibPath_old); // Добавление в пути CompSetPath=rdsINIReadBool(ini,"CompilerToPaths",CompSetPath); LinkSetPath=rdsINIReadBool(ini,"LinkerToPaths",LinkSetPath); } // Читаем строки из секции [Params...] if(rdsINIOpenSection(ini,"Params" RDS_PLATFORMNAME)) { char *CompParams_old=CompParams, // Старые значения *LinkParams_old=LinkParams; // Загружаем новые строки CompParams=rdsDynStrCopy(rdsINIReadString(ini,"Compiler",CompParams_old,NULL)); LinkParams=rdsDynStrCopy(rdsINIReadString(ini,"Linker",LinkParams_old,NULL)); // Освобождаем старые строки rdsFree(CompParams_old); rdsFree(LinkParams_old); } // Читаем строки из секции [Func...] if(rdsINIOpenSection(ini,"Func" RDS_PLATFORMNAME)) { char *DllMainName_old=DllMainName, // Старые значения *ModelFuncHdr_old=ModelFuncHdr, *Exported_old=Exported; // Загружаем новые строки DllMainName=rdsDynStrCopy(rdsINIReadString(ini,"DllMain",DllMainName_old,NULL)); ModelFuncHdr=rdsDynStrCopy(rdsINIReadString(ini,"ModelFunc",ModelFuncHdr_old,NULL)); Exported=rdsDynStrCopy(rdsINIReadString(ini,"Exported",Exported_old,NULL)); // Освобождаем старые строки rdsFree(DllMainName_old); rdsFree(ModelFuncHdr_old); rdsFree(Exported_old); } // Вспомогательный объект больше не нужен rdsDeleteObject(ini); } //========================================= // Настройка модуля автокомпиляции // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" void TCAutoCompData::Setup(const char *IniFileName) { RDS_HOBJECT window; // Объект-окно const char exefilter[]= // Фильтр для диалога выбора файла "Исполняемые файлы (*.exe)|*.exe\nВсе файлы|*.*"; // Создание окна window=rdsFORMCreate(TRUE,-1,-1,"Параметры модуля"); // Создание вкладки rdsFORMAddTab(window,0,"Пути"); // Поле ввода выбора EXE-файла компилятора rdsFORMAddEdit(window,0,1,RDS_FORMCTRL_OPENDIALOG, "Компилятор:",300); rdsSetObjectStr(window,1,RDS_FORMVAL_LIST,exefilter); rdsSetObjectStr(window,1,RDS_FORMVAL_VALUE,CompPath); // Нужно устанавливать путь? rdsFORMAddEdit(window,0,100,RDS_FORMCTRL_CHECKBOX, "Добавлять папку в путь",300); rdsSetObjectInt(window,100,RDS_FORMVAL_VALUE,CompSetPath); // Поле ввода выбора EXE-файла редактора связей rdsFORMAddEdit(window,0,2,RDS_FORMCTRL_OPENDIALOG, "Редактор связей:",300); rdsSetObjectStr(window,2,RDS_FORMVAL_LIST,exefilter); rdsSetObjectStr(window,2,RDS_FORMVAL_VALUE,LinkPath); // Нужно устанавливать путь? rdsFORMAddEdit(window,0,200,RDS_FORMCTRL_CHECKBOX, "Добавлять папку в путь",300); rdsSetObjectInt(window,200,RDS_FORMVAL_VALUE,LinkSetPath); // Выбор папки файлов заголовков rdsFORMAddEdit(window,0,3,RDS_FORMCTRL_DIRDIALOG, "Папка заголовков:",300); rdsSetObjectStr(window,3,RDS_FORMVAL_VALUE,IncludePath); // Выбор папки файлов библиотек rdsFORMAddEdit(window,0,4,RDS_FORMCTRL_DIRDIALOG, "Папка библиотек:",300); rdsSetObjectStr(window,4,RDS_FORMVAL_VALUE,LibPath); // Создание вкладки rdsFORMAddTab(window,1,"Параметры"); // Параметры командной строки компилятора rdsFORMAddEdit(window,1,5,RDS_FORMCTRL_MULTILINE, "Параметры компилятора:",300); rdsSetObjectInt(window,5,RDS_FORMVAL_MLHEIGHT,3*24);// Высота rdsSetObjectStr(window,5,RDS_FORMVAL_VALUE,CompParams); // Параметры командной строки редактора связей rdsFORMAddEdit(window,1,6,RDS_FORMCTRL_MULTILINE, "Параметры редактора связей:",300); rdsSetObjectInt(window,6,RDS_FORMVAL_MLHEIGHT,3*24);// Высота rdsSetObjectStr(window,6,RDS_FORMVAL_VALUE,LinkParams); // Создание вкладки rdsFORMAddTab(window,2,"Описания"); // Имя главной функции rdsFORMAddEdit(window,2,7,RDS_FORMCTRL_EDIT, "Имя главной функции DLL:",300); rdsSetObjectStr(window,7,RDS_FORMVAL_VALUE,DllMainName); // Заголовок модели rdsFORMAddEdit(window,2,8,RDS_FORMCTRL_MULTILINE, "Заголовок функции модели:",300); rdsSetObjectInt(window,8,RDS_FORMVAL_MLHEIGHT,2*24);// Высота rdsSetObjectStr(window,8,RDS_FORMVAL_VALUE,ModelFuncHdr); // Экспортированное имя rdsFORMAddEdit(window,2,9,RDS_FORMCTRL_EDIT, "Экспортированное имя:",300); rdsSetObjectStr(window,9,RDS_FORMVAL_VALUE,Exported); // Открытие окна if(rdsFORMShowModalEx(window,NULL)) // Нажата OK { // Освобождаем старые строки параметров FreeAllStrings(); // Делаем динамические копии всех строк из полей ввода // и присваиваем их полям класса #define GETDYNRELPATH(x) rdsGetRelFilePath(x,rdsGetSystemPath(RDS_GSPAPPPATH),NULL) CompPath=GETDYNRELPATH(rdsGetObjectStr(window,1,RDS_FORMVAL_VALUE)); LinkPath=GETDYNRELPATH(rdsGetObjectStr(window,2,RDS_FORMVAL_VALUE)); IncludePath=GETDYNRELPATH(rdsGetObjectStr(window,3,RDS_FORMVAL_VALUE)); LibPath=GETDYNRELPATH(rdsGetObjectStr(window,4,RDS_FORMVAL_VALUE)); #undef GETDYNRELPATH CompParams=rdsDynStrCopy(rdsGetObjectStr(window,5,RDS_FORMVAL_VALUE)); LinkParams=rdsDynStrCopy(rdsGetObjectStr(window,6,RDS_FORMVAL_VALUE)); DllMainName=rdsDynStrCopy(rdsGetObjectStr(window,7,RDS_FORMVAL_VALUE)); ModelFuncHdr=rdsDynStrCopy(rdsGetObjectStr(window,8,RDS_FORMVAL_VALUE)); Exported=rdsDynStrCopy(rdsGetObjectStr(window,9,RDS_FORMVAL_VALUE)); CompSetPath=rdsGetObjectInt(window,100,RDS_FORMVAL_VALUE); LinkSetPath=rdsGetObjectInt(window,200,RDS_FORMVAL_VALUE); // Запись изменившихся параметров в INI-файл WriteToIni(IniFileName); } // Уничтожение окна rdsDeleteObject(window); } //=========================================// Коды ошибок #define MEC_SIMPLEBLOCK 1 // Только для простых блоков #define MEC_READERROR 2 // Ошибка чтения файла #define MEC_NOTAMODEL 3 // Файл - не модель #define MEC_NOSECTIONS 4 // Нет нужных разделов в модели #define MEC_MODELWRITEERROR 5 // Ошибка записи файла модели //========================================= // Сообщение об ошибке в модели // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" void TCAutoCompData::ModelErrorMsg( const char *modelname,int errorcode) { const char *errortext; // Сообщение по коду ошибки switch(errorcode) { case MEC_SIMPLEBLOCK: errortext="Модель может подключаться только к простому блоку"; break; case MEC_READERROR: errortext="Ошибка чтения файла"; break; case MEC_NOTAMODEL: errortext="Файл не является моделью блока"; break; case MEC_NOSECTIONS: errortext="В файле нет необходимых разделов"; break; case MEC_MODELWRITEERROR: errortext="Ошибка записи файла модели"; break; default: errortext="Неизвестная ошибка"; } // Название модели, если есть if(modelname) { char *msgtext; // Здесь формируется динамический текст msgtext=rdsDynStrCat("Модель: ",modelname,FALSE); rdsAddToDynStr(&msgtext,"\n",FALSE); // Описание ошибки rdsAddToDynStr(&msgtext,errortext,FALSE); // Показываем сообщение rdsMessageBox(msgtext,"Автокомпиляция",MB_OK | MB_ICONWARNING); // Освобождаем память, занятую динамическим текстом rdsFree(msgtext); } else rdsMessageBox(errortext,"Автокомпиляция",MB_OK | MB_ICONWARNING); } //=========================================// Функция модуля автокомпиляции extern "C" __declspec(dllexport) int RDSCALL TestCAutoComp( int CallMode, // Событие RDS_PCOMPMODULEDATA ModuleData, // Данные модуля LPVOID ExtParam) // Дополнительные параметры { // Приведение указателя на личную область данных // к правильному типу TCAutoCompData *data=(TCAutoCompData*)(ModuleData->ModuleData);// Вспомогательная переменная – указатель на структуру // функции, вызванной из окна параметров RDS_PCOMPEXECFUNCDATA funcdata;switch(CallMode) { // Инициализация модуля case RDS_COMPM_INIT: // Создание личной области данных модуля ModuleData->ModuleData=data=new TCAutoCompData(); // Чтение параметров из INI-файла data->ReadFromIni(ModuleData->DataFile); break; // Очистка данных модуля case RDS_COMPM_CLEANUP: delete data; // Удаление личной области модуля break; // Вызов окна настройки модуля case RDS_COMPM_SETUP: data->Setup(ModuleData->DataFile); break;// Получение поддерживаемых модулем функций // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" case RDS_COMPM_GETOPTIONS: // Название поля ввода имени модели rdscompReturnModelNameLabel("Файл исходного текста:"); // Разрешенные кнопки return RDS_COMPFLAG_FUNCMODELBROWSE | // Обзор RDS_COMPFLAG_FUNCMODELCREATE; // Новый// Выполнить функцию case RDS_COMPM_EXECFUNCTION: // Что произошло в окне параметров funcdata=(RDS_PCOMPEXECFUNCDATA)ExtParam; switch(funcdata->Function) { // Нажата кнопка "Новый" case RDS_COMPFLAG_FUNCMODELCREATE: data->CreateEmptyModel(); break; // Нажата кнопка "Обзор" case RDS_COMPFLAG_FUNCMODELBROWSE: data->ConnectExistingModel(funcdata->ModelName); break; } break;// Проверка возможности присоединения модели к блоку case RDS_COMPM_CANATTACHBLK: return data->CanAttachBlock((RDS_PCOMPCANATTACHBLKDATA)ExtParam);// Открыть окно редактора case RDS_COMPM_OPENEDITOR: data->OpenEditor((RDS_POPENEDITORDATA)ExtParam); break;} return RDS_COMPR_DONE; } //=========================================// Проверка возможности назначения модели блоку int TCAutoCompData::CanAttachBlock(RDS_COMPCANATTACHBLKDATA *param) { RDS_BLOCKDESCRIPTION blockdescr; BOOL ok; // Получаем описание блока, к которому подключается модель blockdescr.servSize=sizeof(blockdescr); rdsGetBlockDescription(param->Block,&blockdescr); // Блок должен быть простого типа, иначе наша модель со // статическими переменными не сможет сним работать ok=(blockdescr.BlockType==RDS_BTSIMPLEBLOCK); // Если модель подключается вручную, выводим сообщение if(param->AttachReason==RDS_COMP_AR_MANUALSET && (!ok)) { ModelErrorMsg(param->ModelName,MEC_SIMPLEBLOCK); return RDS_COMPR_ERRORNOMSG; // Без сообщения пользователю } return ok?RDS_COMPR_DONE:RDS_COMPR_ERROR; } //=========================================// Записать строку текста в файл BOOL WriteString(HANDLE file,const char *text) { DWORD res,size; size=strlen(text); if(!WriteFile(file,text,size,&res,NULL)) return FALSE; return (res==size); } //=========================================// Загрузка текстового файла в память // filename – имя файла, maxread – сколько читать или 0 // для чтения всего файла char *ReadTextFile(const char *filename,DWORD maxread) { HANDLE f; DWORD size,actread; char *fullpath,*buffer; BOOL ok=TRUE; RDSWSTR fullpath_w; // Получаем полный путь к файлу fullpath=rdsGetFullFilePath(filename,NULL,NULL); if(fullpath==NULL) // Нет пути - ошибка return NULL; // Преобразуем путь в UTF16 для Windows fullpath_w=rdsUTF8toUTF16(fullpath,FALSE); // Открываем файл для чтения f=CreateFileW(fullpath_w,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL); rdsFree(fullpath); // Имя файла больше не нужно rdsFree(fullpath_w); if(f==INVALID_HANDLE_VALUE) // Ошибка открытия return NULL; // Определяем размер файла size=GetFileSize(f,NULL); if(size==0xFFFFFFFF) // Ошибка или слишком большой { CloseHandle(f); return NULL; } // Если есть ограничение, читаем только часть файла if(maxread!=0 && maxread<size) size=maxread; // Отводим память для загрузки файла buffer=(char*)rdsAllocate(size+1); if(buffer==NULL) // Не удалось отвести { CloseHandle(f); return NULL; } // Считываем файл в память, если он не пустой if(size) { if(ReadFile(f,buffer,size,&actread,NULL)) ok=(actread==size); else ok=FALSE; } // Закрываем файл CloseHandle(f); if(!ok) // Ошибка чтения { rdsFree(buffer); return NULL; } // Дописываем после конца считанного текста нулевой байт buffer[size]=0; return buffer; } //=========================================// Создать новый пустой файл модели // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" void TCAutoCompData::CreateEmptyModel(void) { // Текст пустой модели const char *text=TCTEXTSECTION_START "\r\n" TCTEXTSECTION_VARS "\r\n" "struct\r\nbegin\r\n" " signal name \"Start\" in run default 1\r\n" " signal name \"Ready\" out default 0\r\n" "end\r\n" TCTEXTSECTION_PROG "\r\n"; char *relpath,*fullpath; HANDLE file; BOOL ok; RDSWSTR fullpath_w; // Вызываем диалог сохранения relpath=rdsCallFileDialog("", RDS_CFD_SAVE|RDS_CFD_OVERWRITEPROMPT, "Текстовые файлы (*.txt)|*.txt\nВсе файлы|*.*", "txt", "Новая модель"); if(relpath==NULL) // Пользователь нажал "Отмена" return; // Преобразуем относительный путь в полный fullpath=rdsGetFullFilePath(relpath,NULL,NULL); // Преобразуем путь в UTF16 для Windows fullpath_w=rdsUTF8toUTF16(fullpath,FALSE); // Открываем файл для записи file=CreateFileW(fullpath_w,GENERIC_WRITE,0,NULL,CREATE_ALWAYS,0,NULL); rdsFree(fullpath); // Полный путь больше не нужен rdsFree(fullpath_w); if(file==INVALID_HANDLE_VALUE) // Ошибка открытия ok=FALSE; else { ok=WriteString(file,text); CloseHandle(file); } if(!ok) // Ошибка { ModelErrorMsg(relpath,MEC_MODELWRITEERROR); return; } // Пустой файл модели записан – устанавливаем его имя // в качестве имени модели блока rdscompReturnModelName(relpath); // Освобождаем динамическую строку rdsFree(relpath); } //=========================================// Выбрать файл модели // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" void TCAutoCompData::ConnectExistingModel(const char *oldmodel) { char *relpath,*buf; BOOL ok=FALSE; // Длина текста "$TESTCMODEL" int prefixlen=strlen(TCTEXTSECTION_START); // Вызываем диалог открытия файла relpath=rdsCallFileDialog(oldmodel, RDS_CFD_OPEN|RDS_CFD_MUSTEXIST, "Текстовые файлы (*.txt)|*.txt\nВсе файлы|*.*", "txt", "Файл модели"); if(relpath==NULL) // Пользователь нажал "Отмена" return; // Читаем начало файла: является ли он нашей моделью? buf=ReadTextFile(relpath,prefixlen); if(buf==NULL) // Ошибка чтения ModelErrorMsg(relpath,MEC_READERROR); else if(strcmp(buf,TCTEXTSECTION_START)) // Плохой префикс ModelErrorMsg(relpath,MEC_NOTAMODEL); else ok=TRUE; rdsFree(buf); // Считанный текст больше не нужен if(ok) // Делаем выбранный файл именем модели блока rdscompReturnModelName(relpath); rdsFree(relpath); } //=========================================// Найти ключевое слово в начале строки текста char *FindKeywordAtLineStart(char *text,const char *word) { char *s=text; // Устанавливаем s на начало текста if(text==NULL) return NULL; // Ищем в цикле for(;;) { s=strstr(s,word); // Ищем word начиная с s if(s==NULL) // Не найдено return NULL; if(s==text) // Найдено в начале текста - годится return s; // Найдено в середине текста - перед s должен находиться // перевод строки if(s[-1]=='\r' || s[-1]=='\n') // Есть перевод строки return s; // Перед s - другой символ. Продолжаем поиск } } //=========================================// Разбить текст модели на описание переменных и текст программы BOOL ProcessModelText( char *text, // текст модели в памяти char **pVars, // возвращаемое начало переменных char **pProg) // возвращаемое начало программы { char *s,*s1; if(text==NULL || pVars==NULL || pProg==NULL) return FALSE; // Ищем "$VARS" в начале строки s=FindKeywordAtLineStart(text,TCTEXTSECTION_VARS); if(s==NULL) // Нет такой строки return FALSE; // Найдено - записываем в pVars начало текста после этого // слова с пропуском всех пустых строк s+=strlen(TCTEXTSECTION_VARS); // Пропускаем название секции s+=strspn(s,"\r\n"); // Пропускаем пустые строки после названия *pVars=s; // Ищем "$PROG" в начале строки оставшегося текста s1=FindKeywordAtLineStart(s,TCTEXTSECTION_PROG); if(s1==NULL) // Нет такой строки return FALSE; // Записываем в pProg начало текста после этого слова // с пропуском всех пустых строк s=s1+strlen(TCTEXTSECTION_PROG); // Пропускаем название s+=strspn(s,"\r\n"); // Пропускаем пустые строки после названия *pProg=s; // Для завершения предыдущей секции записываем нулевой байт // вместо '$' в "$PROG" *s1=0; return TRUE; } //=========================================// Прототип функции обратного вызова окна редактора void RDSCALL TCAutoCompData_EditorCallback( RDS_HOBJECT window, // Объект-окно RDS_PFORMSERVFUNCDATA data); // Данные //=========================================// Открыть редактор модели // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" void TCAutoCompData::OpenEditor(RDS_OPENEDITORDATA *param) { char *modelpath,*modeltext; char *vars,*prog; RDS_HOBJECT win; BOOL ok; // Полный путь к файлу модели modelpath=rdsGetFullFilePath(param->Model->ModelName,NULL,NULL); // Читаем весь файл модели в память modeltext=ReadTextFile(modelpath,0); if(modeltext==NULL) // Ошибка чтения { ModelErrorMsg(param->Model->ModelName,MEC_READERROR); return; } // Разбиваем загруженный текст на описание переменных и программу if(!ProcessModelText(modeltext,&vars,&prog)) { rdsFree(modeltext); ModelErrorMsg(param->Model->ModelName,MEC_NOSECTIONS); return; } // Создаем объект-окно редактора win=rdsFORMCreate(FALSE,-1,-1,param->Model->ModelName); // Создаем в нем невизуальное поле и записываем в // него описание переменных rdsFORMAddEdit(win,0,1000,RDS_FORMCTRL_NONVISUAL,NULL,0); rdsSetObjectStr(win,1000,RDS_FORMVAL_VALUE,vars); // Создаем кнопку для вызова редактора переменных rdsFORMAddEdit(win,0,1,RDS_FORMCTRL_BUTTON|RDS_FORMFLAG_LINE, "Переменные:",150); rdsSetObjectStr(win,1,RDS_FORMVAL_VALUE,"Изменить..."); // Создаем многострочное поле ввода для текста программы rdsFORMAddEdit(win,0,2,RDS_FORMCTRL_MULTILINE, "Такт расчета:",600); rdsSetObjectStr(win,2,RDS_FORMVAL_VALUE,prog); rdsSetObjectInt(win,2,RDS_FORMVAL_MLHEIGHT,5*24);// Высота rdsSetObjectInt(win,2,RDS_FORMVAL_MLRETURNS,1); // Enter // Загруженный текст модели больше не нужен – мы все переписали в поля окна rdsFree(modeltext); // Открываем окно (с функцией обратного вызова) ok=rdsFORMShowModalServ(win,TCAutoCompData_EditorCallback); if(ok) { // Нажата кнопка "OK" - записываем текст модели в файл HANDLE file; RDSWSTR modelpath_w=rdsUTF8toUTF16(modelpath,FALSE); file=CreateFileW(modelpath_w,GENERIC_WRITE,0,NULL, CREATE_ALWAYS,0,NULL); rdsFree(modelpath_w); if(file==INVALID_HANDLE_VALUE) // Ошибка ok=FALSE; else // Записываем в файл { // Заголовок и секция переменных ok=WriteString(file,TCTEXTSECTION_START "\r\n" TCTEXTSECTION_VARS "\r\n"); // Описание переменных ok=ok && WriteString(file,rdsGetObjectStr(win,1000,RDS_FORMVAL_VALUE)); // Секция текста программы ok=ok && WriteString(file,TCTEXTSECTION_PROG "\r\n"); // Текст программы ok=ok && WriteString(file,rdsGetObjectStr(win,2,RDS_FORMVAL_VALUE)); CloseHandle(file); } if(!ok) ModelErrorMsg(param->Model->ModelName,MEC_MODELWRITEERROR); } // Уничтожаем окно rdsDeleteObject(win); // Освобождаем строку с именем файла модели rdsFree(modelpath); } //=========================================// Функция обратного вызова окна редактора модели // ВАЖНО: Исходный текст программы должен быть записан в UTF8, // в противном случае необходимо использовать версии функций // с суффиксом "W" и символьные константы с префиксом "L" void RDSCALL TCAutoCompData_EditorCallback( RDS_HOBJECT window, RDS_PFORMSERVFUNCDATA data) { RDS_HOBJECT dv=NULL; RDS_VARDESCRIPTION vdescr; char *varstr; switch(data->Event) { case RDS_FORMSERVEVENT_CLICK: // Нажата кнопка // Создаем объект для редактирования переменных dv=rdsVSCreateEditor(); // Заполняем объект описанием переменных (поле 1000) if(!rdsVSCreateByDescr(dv, rdsGetObjectStr(window,1000,RDS_FORMVAL_VALUE))) break; // Вызываем для объекта редактор переменных if(!rdsVSExecuteEditor(dv,TRUE,RDS_HVAR_FALLPLAIN, 0,"Переменные блока")) break; // Пользователь нажал "OK" – получаем текст описания // измененных переменных vdescr.servSize=sizeof(vdescr); if(!rdsVSGetVarDescription(dv,-1,&vdescr)) break; varstr=rdsCreateVarDescriptionString(vdescr.Var,TRUE,0,NULL); if(varstr) { // Заносим обратно в поле 1000 rdsSetObjectStr(win,1000,RDS_FORMVAL_VALUE,vars); rdsFree(varstr); } break; } // Уничтожаем объект-редактор, если он был создан rdsDeleteObject(dv); } //=========================================