Текущий раздел:

Описания RDS

Руководство программиста

Глава 3. Создание модулей автоматической компиляции

§3.4. Компиляция моделей

Полный исходный текст на языке C++ для библиотеки (DLL) с примером модуля автокомпиляции моделей блоков для языка C. В пример добавлена собственно компиляция и подключение скомпилированной модели к блокам.

Изменения относительно §3.3 выделены цветом.

      #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);
            };

          // Сформировать в файле исходный текст программы
          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);
          // Чтение параметров модуля из 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);
      }
      //=========================================