Окончательный вариант исходного текста на языке C++ для библиотеки (DLL) с примером модуля автокомпиляции моделей блоков для языка C. Добавлен модуль поиска настроенных модулей автокомпиляции, совместимых по формату модели.
Изменения относительно §3.4 выделены цветом.
#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; } //========= Конец главной функции =========// Функция модуля автокомпиляции - поиск extern "C" __declspec(dllexport) int RDSCALL TestCAutoCompSearch( int CallMode, // Событие RDS_PCOMPMODULEDATA ModuleData, // Данные модуля LPVOID ExtParam) // Дополнительные параметры { switch(CallMode) { // Инициализация модуля case RDS_COMPM_INIT: if(rdscompLinkModuleAllowed()) rdscompLinkAvailableModule(ModuleData->ModelFormat); break; } return RDS_COMPR_DONE; } //=========================================// Имена секций файла модели // Начало файла #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); }; // Сформировать в файле исходный текст программы BOOL WriteSourceCode(HANDLE file,const char *name, RDS_HOBJECT varset,const char *prog); // Загрузить и скомпилировать одну модель void LoadAndProcessModel(RDS_COMPMODELDATA *data, int fileset,BOOL varsonly); // Сообщение об ошибке в модели static void ModelErrorMsg(const char *modelname,int errorcode); public: // Подготовиться к компиляции модели void PrepareToCompileModel(RDS_COMPPREPAREDATA *param); // Компилировать модели void CompileModels(RDS_COMPILEDATA *param); // Связать блок с моделью void AttachBlock(RDS_COMPBLOCKOPDATA *param);// Модуль настроен (есть компилятор и редактор связей) BOOL CanWork(void) { return rdsFileExists(CompPath,NULL,NULL,NULL) && rdsFileExists(LinkPath,NULL,NULL,NULL); };// Чтение параметров модуля из 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 // Ошибка записи файла модели #define MEC_BADVARS 6 // Ошибка в описаниях переменных #define MEC_SRCWRITEERROR 7 // Ошибка записи исходного текста программы #define MEC_NOOBJFILE 8 // Нет объектного файла #define MEC_COMPSTARTERROR 10 // Ошибка запуска компилятора #define MEC_NODLLFILE 11 // Нет файла DLL #define MEC_LINKSTARTERROR 12 // Ошибка запуска редактора связей #define MEC_DLLCOPYERROR 13 // Ошибка копирования DLL //========================================= // Сообщение об ошибке в модели // ВАЖНО: Исходный текст программы должен быть записан в 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; case MEC_BADVARS: errortext="Ошибка в разделе описания переменных"; break; case MEC_SRCWRITEERROR: errortext="Невозможно создать файл исходного текста"; break; case MEC_NOOBJFILE: errortext="Объектный файл не создан - " "в тексте модели есть ошибки"; break; case MEC_COMPSTARTERROR: errortext="Ошибка запуска компилятора"; break; case MEC_NODLLFILE: errortext="Файл DLL не создан - " "в модели есть ошибки"; break; case MEC_LINKSTARTERROR: errortext="Ошибка запуска редактора связей"; break; case MEC_DLLCOPYERROR: errortext="Ошибка копирования полученного файла DLL"; 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; // Подготовка к компиляции case RDS_COMPM_PREPARE: data->PrepareToCompileModel((RDS_PCOMPPREPAREDATA)ExtParam); break; // Компиляция моделей case RDS_COMPM_COMPILE: data->CompileModels((RDS_PCOMPILEDATA)ExtParam); break; // Присоединение модели к блоку case RDS_COMPM_ATTACHBLOCK: data->AttachBlock((RDS_PCOMPBLOCKOPDATA)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); } //========================================= // Получить время последней записи в файл BOOL GetFileLastWrite(const char *filename,FILETIME *pLastWrite) { HANDLE file; BOOL ok=TRUE; // Преобразуем путь в UTF16 для Windows RDSWSTR fullpath_w=rdsUTF8toUTF16(filename,FALSE); // Открываем файл для чтения file=CreateFileW(fullpath_w,GENERIC_READ,FILE_SHARE_READ, NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL); rdsFree(fullpath_w); if(file==INVALID_HANDLE_VALUE) // К файлу нет доступа return FALSE; if(pLastWrite) { // Файл открыт - получаем время изменения ok=GetFileTime(file,NULL,NULL,pLastWrite); } CloseHandle(file); return ok; } //========================================= // Проверить необходимость компиляции DLL BOOL CheckDllTime(const char *modelfile,const char *dllfile) { FILETIME modeltime,dlltime; // Получаем время изменения DLL if(!GetFileLastWrite(dllfile,&dlltime)) return FALSE; // Файла DLL, вероятно, нет // Получаем время изменения файла модели if(!GetFileLastWrite(modelfile,&modeltime)) return FALSE; // Нет доступа к файлу модели - ошибка // Если modeltime<=dlltime, можно не компилировать return CompareFileTime(&modeltime,&dlltime)<=0; } //========================================= // Запуск программы и ожидание ее завершения BOOL RunAndWait( const char *path, // Имя EXE-файла const char *parameters, // Параметры командной строки const char **search, // Массив имен констант const char **replace) // Массив значений констант { char *cmd,*commandline,*tempdir; STARTUPINFO startup; // Структура параметров запуска процесса PROCESS_INFORMATION info; // Структура описания процесса BOOL ok=TRUE; RDSWSTR commandline_w,tempdir_w; // Формирование полной командной строки cmd=rdsDynStrCat("\"",path,FALSE); rdsAddToDynStr(&cmd,"\" ",FALSE); rdsAddToDynStr(&cmd,parameters,FALSE); rdsAddToDynStr(&cmd," ",FALSE); // Пробел (на всякий случай) // В cmd теперь - команда запуска, символические константы // в ней еще не обработаны // Заменяем все константы на их значения commandline=rdsStringReplace(cmd,search,replace, -1,RDS_SRF_STDPATHS); rdsFree(cmd); // cmd больше не нужна // В commandline - полная командная строка запуска программы if(commandline==NULL) return FALSE; // Получение имени временной папки RDS без '\' tempdir=rdsTransformFileName( rdsGetSystemPath(RDS_GSPTEMPPATH), RDS_TFN_EXCLUDEPATHBS,NULL,NULL); // Заполнение структуры STARTUPINFO ZeroMemory(&startup,sizeof(STARTUPINFO)); startup.cb=sizeof(STARTUPINFO); // Запуск процесса commandline_w=rdsUTF8toUTF16(commandline,FALSE); tempdir_w=rdsUTF8toUTF16(tempdir,FALSE); if(!CreateProcessW(NULL,commandline_w,NULL,NULL,FALSE, 0,NULL,tempdir_w,&startup,&info)) ok=FALSE; // Запустить не получилось if(ok) { // Ждем завершения процесса WaitForSingleObject(info.hProcess,INFINITE); // Процесс завершен - закрываем полученные дескрипторы CloseHandle(info.hThread); CloseHandle(info.hProcess); } // Освобождаем все динамические строки rdsFree(commandline); rdsFree(tempdir); rdsFree(commandline_w); rdsFree(tempdir_w); return ok; } //========================================= // Запуск программы и ожидание ее завершения с возможным добавлением пути к exe-файлу в PATHS BOOL RunAndWaitSetPath( const char *path, // Имя EXE-файла const char *parameters, // Параметры командной строки const char **search, // Массив имен констант const char **replace, // Массив значений констант BOOL setpath) // Установить путь к exe в PATH { #define PATHVAR "Path" if(setpath) { char *exepath=rdsTransformFileName(path,RDS_TFN_GETPATHNOBS,NULL,NULL); char *oldpath=rdsGetEnvironmentVariable(PATHVAR); BOOL ok; if(oldpath) // Есть пути { char *newpath=NULL; rdsAddToDynStr(&newpath,exepath,FALSE); rdsAddToDynStr(&newpath,";",FALSE); rdsAddToDynStr(&newpath,oldpath,FALSE); rdsSetEnvironmentVariable(PATHVAR,newpath,FALSE); rdsFree(newpath); } else // Было пусто rdsSetEnvironmentVariable(PATHVAR,exepath,FALSE); ok=RunAndWait(path,parameters,search,replace); if(oldpath) // Восстанавливаем rdsSetEnvironmentVariable(PATHVAR,oldpath,FALSE); rdsFree(oldpath); rdsFree(exepath); return ok; } return RunAndWait(path,parameters,search,replace); #undef PATHVAR } //========================================= // Подготовиться к компиляции модели void TCAutoCompData::PrepareToCompileModel( RDS_COMPPREPAREDATA *param) { char *dllpath; // Получаем имя DLL с сокращенным путем для записи в параметры блока #ifdef RDS_WIN64 dllpath=rdsTransformFileName(param->Model->ModelName,RDS_TFN_CHANGEEXT,".dll64",NULL); #else dllpath=rdsTransformFileName(param->Model->ModelName,RDS_TFN_CHANGEEXT,".dll32",NULL); #endif // Выясняем, нужно ли перекомпилировать DLL if(param->Rebuild) // Принудительно компилировать все param->Model->Valid=FALSE; else // Компилировать при необходимости { // Время последней записи DLL должно быть не меньше // времени записи текста модели char *modelpath,*dllfullpath; // Полный путь к файлу модели modelpath=rdsGetFullFilePath( param->Model->ModelName,NULL,NULL); // Полный путь к файлу DLL dllfullpath=rdsGetFullFilePath(dllpath,NULL,NULL); // Сравниваем времена последней записи param->Model->Valid=CheckDllTime(modelpath,dllfullpath); // Освобождаем строки rdsFree(modelpath); rdsFree(dllfullpath); } // Записываем в параметры блоков библиотеку и функцию в ней, // которые получатся в результате компиляции rdscompSetModelFunction(param->Model->Model,dllpath,Exported); rdsFree(dllpath); } //========================================= // Компилировать все модели void TCAutoCompData::CompileModels(RDS_COMPILEDATA *param) { // Проверяем необходимые параметры if(CompPath==NULL || LinkPath==NULL) return; // Компиляция невозможна // В цикле перебираем все модели из param->InvalidModels for(int i=0;i<param->IMCount;i++) { int tempset; // Создаем новый набор временных файлов tempset=rdsTMPCreateFileSet(); // Компилируем модель LoadAndProcessModel(param->InvalidModels[i],tempset,FALSE); // Удаляем все созданные в процессе временные файлы rdsTMPDeleteFileSet(tempset); } } //========================================= // Сформировать в файле исходный текст программы BOOL TCAutoCompData::WriteSourceCode( HANDLE file, // дескриптор открытого файла const char *name, // имя модели (для сообщения) RDS_HOBJECT varset, // набор переменных const char *prog) // исходный текст реакции { BOOL ok; RDS_VARDESCRIPTION vdescr; char *undef=NULL; // Начальные описания и тип главной функции DLL ok=WriteString(file, "#include <windows.h>\r\n" "#include <stdlib.h>\r\n" "#include <math.h>\r\n" #ifdef RDS_WIN64 ok=ok && WriteString(file,"#define RDS_WIN64\r\n"); #else ok=ok && WriteString(file,"#define RDS_WIN32\r\n"); #endif ok=ok && WriteString(file, "#include <RdsDef.h>\r\n" "#define RDS_SERV_FUNC_BODY GetServiceFunc\r\n" "#include <RdsFunc.h>\r\n" "double _HugeDouble;\r\n\r\n" // значение ошибки ); // Имя главной функции DLL (из настроек модуля) ok=ok && WriteString(file,DllMainName); // Тело главной функции DLL ok=ok && WriteString(file, "(HINSTANCE /*hinst*/,unsigned long reason," "void */*lpReserved*/)\r\n" "{ if(reason==DLL_PROCESS_ATTACH)\r\n" " { if(!RDS_SERV_FUNC_BODY())\r\n" " RDS_SERV_ERROR_MSGW\r\n" " else\r\n" " rdsGetHugeDouble(&_HugeDouble);\r\n" " }\r\n" " return 1;\r\n" "}\r\n\r\n"); // Заголовок функции блока (из настроек) ok=ok && WriteString(file,ModelFuncHdr); // Продолжение функции блока и проверка типов переменных ok=ok && WriteString(file, "(int CallMode,RDS_PBLOCKDATA BlockData,LPVOID ExtParam)\r\n" "{ switch(CallMode)\r\n" " { case RDS_BFM_VARCHECK:\r\n" " if(strcmp((char*)ExtParam,\""); // Строка типа переменных блока (из varset) ok=ok && WriteString(file, rdsGetObjectStr(varset,RDS_HVAR_GETTYPESTRING,0)); // Завершение проверки типа переменных ok=ok && WriteString(file, "\")) return RDS_BFR_BADVARSMSG;\r\n" " break;\r\n"); // Такт расчета ok=ok && WriteString(file, " case RDS_BFM_MODEL:\r\n"); // Макрос для начала дерева переменных ok=ok && WriteString(file,"#define _pVarDataStart " " ((BYTE*)(BlockData->VarTreeData))\r\n"); // Макросы для переменных блока vdescr.servSize=sizeof(vdescr); if(rdsVSGetVarDescription(varset,-1,&vdescr)) { // В vdescr теперь - описание всей структуры переменных блока const char *type; char *soff; int offset=0; int n=vdescr.StructFields; // Число полей // Записываем макрос для каждой переменной for(int i=0;i<n;i++) if(rdsVSGetVarDescription(varset,i,&vdescr)) { // Получили описание i-й переменной блока switch(vdescr.Type) // Тип переменной { case RDS_VARTYPE_SIGNAL: case RDS_VARTYPE_LOGICAL: case RDS_VARTYPE_CHAR: type="char"; break; case RDS_VARTYPE_SHORT: type="short int"; break; case RDS_VARTYPE_INT: type="RDSINT32"; break; case RDS_VARTYPE_FLOAT: type="float"; break; case RDS_VARTYPE_DOUBLE: type="double"; break; default: // Тип не поддерживается // Смещение к следующей переменной offset+=vdescr.DataSize; continue; } if(i==0 && vdescr.Type!=RDS_VARTYPE_SIGNAL) break; // Первая переменная - не сигнал // Продолжать не имеет смысла ok=ok && WriteString(file,"#define "); // Имя переменной ok=ok && WriteString(file,vdescr.Name); ok=ok && WriteString(file," (*(("); ok=ok && WriteString(file,type); ok=ok && WriteString(file,"*)(_pVarDataStart+"); // Смещение к переменной (offset) в виде строки soff=rdsItoA(offset,10,0); ok=ok && WriteString(file,soff); rdsFree(soff); ok=ok && WriteString(file,")))\r\n"); // Смещение к следующей переменной offset+=vdescr.DataSize; // Добавление к undef директивы отмены этого макроса rdsAddToDynStr(&undef,"#undef ",FALSE); rdsAddToDynStr(&undef,vdescr.Name,FALSE); rdsAddToDynStr(&undef,"\r\n",FALSE); } // rdsVSGetVarDescription(varset,i,...)) } // if(rdsVSGetVarDescription(...) // Вставляем текст пользователя ok=ok && WriteString(file,"{\r\n"); ok=ok && WriteString(file,prog); ok=ok && WriteString(file,"\r\n}\r\n"); // Отмена макросов переменных if(undef!=NULL) { ok=ok && WriteString(file,undef); rdsFree(undef); } // Отмена макроса начала дерева переменных ok=ok && WriteString(file,"#undef _pVarTreeDataStart\r\n"); // Завершение функции модели ok=ok && WriteString(file, " break;\r\n" " } \\ switch\r\n" " return RDS_BFR_DONE;\r\n" "}\r\n" ); return ok; } //========================================= // Получить размер файла по его имени DWORD ReadFileSize(const char *filename) { HANDLE f; DWORD size; // Преобразуем путь в UTF16 для Windows RDSWSTR fullpath_w=rdsUTF8toUTF16(filename,FALSE); // Открываем файл на чтение f=CreateFileW(fullpath_w,GENERIC_READ,0,NULL,OPEN_EXISTING,0,NULL); rdsFree(fullpath_w); if(f==INVALID_HANDLE_VALUE) // Ошибка – нет файла return 0; // Получаем размер файла size=GetFileSize(f,NULL); // Закрываем файл CloseHandle(f); return size; } //========================================= // Загрузить и скомпилировать одну модель void TCAutoCompData::LoadAndProcessModel( RDS_COMPMODELDATA *data, // структура данных модели int fileset, // набор временных файлов BOOL varsonly) // только установка переменных { char *modeltext,*dllpath,*aux; char *vars,*prog; char *st_srcfile; char *dyn_namenoext,*dyn_objfile1,*dyn_objfile2,*dyn_dllfile; RDS_HOBJECT varset; RDS_BLOCKDESCRIPTION bdescr; BOOL ok=TRUE; // Массивы для автоматической замены строк const char *search[]={"$INCLUDE$", // 0 "$LIB$", // 1 "$NAME$", // 2 NULL}; const char *replace[sizeof(search)/sizeof(char*)]; bdescr.servSize=sizeof(bdescr); // Полный путь относительно RDS #define DYNFULLPATH(x) rdsGetFullFilePath(x,rdsGetSystemPath(RDS_GSPAPPPATH),NULL) // Читаем весь файл модели в память modeltext=ReadTextFile(data->ModelName,0); if(modeltext==NULL) // Ошибка чтения { ModelErrorMsg(data->ModelName,MEC_READERROR); return; } // Файл модели считан в modeltext - разбиваем на секции vars и prog if(!ProcessModelText(modeltext,&vars,&prog)) { rdsFree(modeltext); ModelErrorMsg(data->ModelName,MEC_NOSECTIONS); return; } // Создаем объект для работы с переменными varset=rdsVSCreateEditor(); // Записываем в объект получившийся текст описания переменных if(!rdsVSCreateByDescr(varset,vars)) { rdsFree(modeltext); rdsDeleteObject(varset); ModelErrorMsg(data->ModelName,MEC_BADVARS); return; } // Копируем переменные во все блоки, к которым подключена модель for(int i=0;i<data->NBlocks;i++) { // Берем очередной блок в списке блоков модели RDS_BHANDLE block=rdscompGetModelBlock(data->Model,i,&bdescr); if(block==NULL) continue; if(bdescr.BlockType!=RDS_BTSIMPLEBLOCK) continue; // Только для простых блоков // Записываем переменные в блок rdsVSApplyToBlock(varset,block,NULL); } if(varsonly) { // Нужно только установить переменные, компилировать не нужно rdsFree(modeltext); rdsDeleteObject(varset); return; } // Формирование исходного текста компилируемой библиотеки st_srcfile=rdsTMPCreateEmptyFileAnyExt(fileset,"$TEMP$\\model.cpp"); // В st_srcfile - указатель на имя созданного временного // файла (строка имени находится во внутренней памяти RDS) if(st_srcfile) { RDSWSTR fullpath_w=rdsUTF8toUTF16(st_srcfile,FALSE); HANDLE file=CreateFileW(fullpath_w,GENERIC_WRITE, 0,NULL,CREATE_ALWAYS,0,NULL); rdsFree(fullpath_w); if(file==INVALID_HANDLE_VALUE) // Ошибка ok=FALSE; else { // Записываем в файл исходный текст программы ok=WriteSourceCode(file,varset,prog); CloseHandle(file); } } if(!ok) ModelErrorMsg(data->ModelName,MEC_SRCWRITEERROR); // Текст модели и cписок переменных больше не нужны rdsFree(modeltext); rdsDeleteObject(varset); // Формирование имени файла без расширения и пути aux=rdsTransformFileName(st_srcfile,RDS_TFN_CHANGEEXT,"",NULL); dyn_namenoext=rdsTransformFileName(aux,RDS_TFN_GETNAME,NULL,NULL); rdsFree(aux); // Запоминание имен временных файлов, формируемых компилятором, // и создание пустых для дальнейшей проверки rdsTMPRememberFileName(fileset, dyn_objfile1=rdsTransformFileName(st_srcfile,RDS_TFN_CHANGEEXT,".obj",NULL)); rdsTMPRememberFileName(fileset, dyn_objfile2=rdsTransformFileName(st_srcfile,RDS_TFN_CHANGEEXT,".o",NULL)); rdsTMPRememberFileName(fileset, dyn_dllfile=rdsTransformFileName(st_srcfile,RDS_TFN_CHANGEEXT,".dll",NULL)); rdsTMPRememberFileName(fileset, aux=rdsTransformFileName(st_srcfile,RDS_TFN_CHANGEEXT,".lib",NULL)); rdsFree(aux); rdsTMPRememberFileName(fileset, aux=rdsTransformFileName(st_srcfile,RDS_TFN_CHANGEEXT,".tds",NULL)); rdsFree(aux); // Занесение различных путей в список замены replace replace[0]=IncludePath; // "$INCLUDE$" replace[1]=LibPath; // "$LIB$" replace[2]=dyn_namenoext; // "$NAME$" // Запуск компилятора if(ok) { char *fullpath=DYNFULLPATH(CompPath); if(RunAndWaitSetPath(fullpath,CompParams,search,replace,CompSetPath)) { // Проверяем наличие .obj if(ReadFileSize(dyn_objfile1)==0 && ReadFileSize(dyn_objfile2)==0) // Нулевого размера { ok=FALSE; ModelErrorMsg(data->ModelName,MEC_NOOBJFILE); } } else // Ошибка запуска { ok=FALSE; ModelErrorMsg(data->ModelName,MEC_COMPSTARTERROR); } rdsFree(fullpath); } // Запуск редактора связей if(ok) { char *fullpath=DYNFULLPATH(LinkPath); if(RunAndWaitSetPath(fullpath,LinkParams,search,replace,LinkSetPath)) { // Проверяем наличие .dll if(ReadFileSize(dyn_dllfile)==0) // Нулевого размера { ok=FALSE; ModelErrorMsg(data->ModelName,MEC_NODLLFILE); } } else // Ошибка запуска { ok=FALSE; ModelErrorMsg(data->ModelName,MEC_LINKSTARTERROR); } rdsFree(fullpath); } // Полный путь к файлу DLL (куда ссылается блок) dllpath=rdsGetFullFilePath(data->CompDllName,NULL,NULL); // Копируем созданный во временной директории файл DLL в папку модели if(ok) { RDSWSTR from_w=rdsUTF8toUTF16(dyn_dllfile,FALSE), to_w=rdsUTF8toUTF16(dllpath,FALSE); ok=CopyFileW(from_w,to_w,FALSE); rdsFree(from_w); rdsFree(to_w); if(!ok) ModelErrorMsg(data->ModelName,MEC_DLLCOPYERROR); } // Освобождаем динамически созданные пути rdsFree((void*)(replace[0])); rdsFree((void*)(replace[1])); rdsFree(dyn_namenoext); rdsFree(dyn_objfile1); rdsFree(dyn_objfile2); rdsFree(dyn_dllfile); rdsFree(dllpath); #undef DYNFULLPATH } //========================================= // Связать блок с моделью void TCAutoCompData::AttachBlock(RDS_COMPBLOCKOPDATA *param) { // Если модель устанавливается пользователем вручную, // нужно поменять у блока структуру переменных if(param->AttachReason==RDS_COMP_AR_MANUALSET) LoadAndProcessModel(param->Model,-1,TRUE); } //=========================================