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

Описания RDS

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

Глава 2. Создание моделей блоков

§2.13. Вызов функций блоков

§2.13.6. Регистрация исполнителя функции

Полный исходный текст на языке C++ для библиотеки (DLL) с моделями блоков, выводящих сообщения пользователю или записывающих эти сообщения в заданный файл и иллюстрирующих регистрацию исполнителя функции. Библиотека содержит три модели:

- MessageFuncBlock_Box – блок-исполнитель функции, показывающий сообщение пользователю;
- MessageFuncBlock_File – блок-исполнитель функции, выводящий сообщение в файл;
- ShowMessage – блок, передающий зарегистрированному исполнителю сообщение для вывода.

      // Регистрация исполнителя функции
      #include <windows.h>
      #include <stdio.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 PROGGUIDEMESSAGEFUNC "ProgrammersGuide.UserMessage"
      // Структура параметров функции
      typedef struct
      { DWORD servSize;   // Размер этой структуры
        char *MessageStr; // Текст сообщения
        int Level;        // Уровень важности (0, 1 или 2)
      } TProgGuideMessageFuncParams;
      //=========================================
      // Глобальная переменная для идентификатора функции
      int MessageFunc=0;
      //=========================================

      //====================================================================
      // Блок-исполнитель функции, показывающей сообщение пользователю
      //====================================================================
      extern "C" __declspec(dllexport)
        int RDSCALL MessageFuncBlock_Box(int CallMode,
            RDS_PBLOCKDATA BlockData,
            LPVOID ExtParam)
      { RDS_PFUNCTIONCALLDATA func;
        switch(CallMode)
          { // Инициализация
            case RDS_BFM_INIT:
              // Регистрируем функцию
              if(MessageFunc==0)
                MessageFunc=rdsRegisterFunction(PROGGUIDEMESSAGEFUNC);
              // Объявляем этот блок ее исполнителем
              rdsRegisterFuncProvider(MessageFunc,FALSE);
              break;

            // Очистка
            case RDS_BFM_CLEANUP:
              // Отменяем регистрацию данного блока как исполнителя
              rdsUnregisterFuncProvider(MessageFunc);
              break;

            // Вызов функции
            case RDS_BFM_FUNCTIONCALL:
              // Приводим ExtParam к правильному типу
              func=(RDS_PFUNCTIONCALLDATA)ExtParam;
              if(func->Function==MessageFunc)
                { // Вызвана "ProgrammersGuide.UserMessage"
                  TProgGuideMessageFuncParams *params=
                    (TProgGuideMessageFuncParams*)(func->Data);
                  DWORD icon;
                  char *caption;
                  // Проверяем наличие параметров и их размер
                  if(params==NULL ||
                     params->servSize<sizeof(TProgGuideMessageFuncParams))
                    break; // Параметров нет или неверный размер
                  // В зависимости от уровня важности сообщения
                  // устанавливаем иконку и заголовок
                  // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
                  //   в противном случае необходимо использовать версии функций
                  //   с суффиксом "W" и символьные константы с префиксом "L"
                  switch(params->Level)
                    { case 0:  icon=MB_ICONINFORMATION;
                               caption="Информация";
                               break;
                      case 1:  icon=MB_ICONWARNING;
                               caption="Предупреждение";
                               break;
                      default: icon=MB_ICONERROR;
                               caption="Ошибка";
                    }
                  // Показываем сообщение пользователю
                  rdsMessageBox(params->MessageStr,caption,icon | MB_OK);
                }
              break;
          }
        return RDS_BFR_DONE;
      }
      //=========================================

      //====================================================================
      // Блок, вызывающий функцию вывода сообщения
      //====================================================================

      // Личная область данных блока, выводящего сообщение
      class TMessageFuncUserData
      { public:
          // Указатель на структуру подписки
          RDS_PFUNCPROVIDERLINK Link;

          // Функция настройки блока
          BOOL Setup(RDS_BHANDLE Block,int NumTypeVar,int NumMessVar);

          // Конструктор класса
          TMessageFuncUserData(void)
            { // Регистрируем функцию
              if(MessageFunc==0)
                MessageFunc=rdsRegisterFunction(PROGGUIDEMESSAGEFUNC);
              // Подписываемся на блок-исполнитель
              Link=rdsSubscribeToFuncProvider(MessageFunc);
            };
          // Деструктор класса
          ~TMessageFuncUserData()
            { // Прекращаем подписку
              rdsUnsubscribeFromFuncProvider(MessageFunc);
            };
      };
      //=========================================

      // Блок, выводящий сообщение по сигналу
      extern "C" __declspec(dllexport)
        int RDSCALL ShowMessage(int CallMode,
            RDS_PBLOCKDATA BlockData,
            LPVOID ExtParam)
      { // Указатель на личную область данных
        TMessageFuncUserData *data=
            (TMessageFuncUserData*)(BlockData->BlockData);
      // Макроопределения для статических переменных
      #define pShow ((char *)(BlockData->VarTreeData))
      #define Show (*((char *)(pShow)))
      #define Ready (*((char *)(pShow+RDS_VSZ_S)))
      #define Message (*((char **)(pShow+2*RDS_VSZ_S)))
      #define Type (*((RDSINT32 *)(pShow+2*RDS_VSZ_S+RDS_VSZ_A)))

        switch(CallMode)
          { // Инициализация – создание личной области
            case RDS_BFM_INIT:
              BlockData->BlockData=new TMessageFuncUserData();
              break;

            // Очистка – удаление личной области
            case RDS_BFM_CLEANUP:
              delete data;
              break;

            // Проверка типов переменных
            case RDS_BFM_VARCHECK:
              return strcmp((char*)ExtParam,"{SSAI}")?
                  RDS_BFR_BADVARSMSG:RDS_BFR_DONE;

            // Настройка блока
            case RDS_BFM_SETUP:
              return data->Setup(BlockData->Block,3,2)?1:0;

            // Такт расчета (реакция на сигнал Show)
            case RDS_BFM_MODEL:
              if(data->Link!=NULL) // Есть структура подписки
                { TProgGuideMessageFuncParams params;
                  // Готовим структуру параметров функции
                  params.servSize=sizeof(params);
                  params.MessageStr=Message;
                  params.Level=Type;
                  // Вызываем функцию по данным подписки
                  rdsCallBlockFunction(data->Link->Block,
                    data->Link->FuncId,&params);
                }
              break;
          }
        return RDS_BFR_DONE;
      // Отмена макрооперделений
      #undef Type
      #undef Message
      #undef Ready
      #undef Show
      #undef pShow
      }
      //=========================================

      // Функция настройки блока
      // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
      //   в противном случае необходимо использовать версии функций
      //   с суффиксом "W" и символьные константы с префиксом "L"
      BOOL TMessageFuncUserData::Setup(RDS_BHANDLE Block,
                int NumTypeVar,int NumMessVar)
      { RDS_HOBJECT window; // Объект-окно
        BOOL ok;            // Пользователь нажал "OK"
        char *defval;

        // Создание окна
        window=rdsFORMCreate(FALSE,-1,-1,"Сообщение");

        // Важность сообщения
        // Получение значения переменной по умолчанию
        defval=rdsGetBlockVarDefValueStr(Block,NumTypeVar,NULL);
        // Добавление поля – выпадающий список
        rdsFORMAddEdit(window,0,1,RDS_FORMCTRL_COMBOLIST,"Тип:",150);
        // Установка списка вариантов
        rdsSetObjectStr(window,1,RDS_FORMVAL_LIST,
            "Информация\nПредупреждение\nОшибка");
        // Установка текущего значения поля
        rdsSetObjectStr(window,1,RDS_FORMVAL_ITEMINDEX,defval);
        // Освобождение defval
        rdsFree(defval);

        // Текст сообщения
        // Получение значения переменной по умолчанию
        defval=rdsGetBlockVarDefValueStr(Block,NumMessVar,NULL);
        // Добавление поля – многострочное
        rdsFORMAddEdit(window,0,2,RDS_FORMCTRL_MULTILINE,"Текст:",80);
        // Установка текущего значения поля
        rdsSetObjectStr(window,2,RDS_FORMVAL_VALUE,defval);
        // Установка высоты поля в точках экрана (~3 строки)
        rdsSetObjectInt(window,2,RDS_FORMVAL_MLHEIGHT,3*24);
        // Освобождение defval
        rdsFree(defval);

        // Открытие окна
        ok=rdsFORMShowModalEx(window,NULL);
        if(ok)
          { // Пользователь нажал OK – запись измененных параметров
            defval=rdsGetObjectStr(window,1,RDS_FORMVAL_ITEMINDEX);
            rdsSetBlockVarDefValueStr(Block,NumTypeVar,defval);
            defval=rdsGetObjectStr(window,2,RDS_FORMVAL_VALUE);
            rdsSetBlockVarDefValueStr(Block,NumMessVar,defval);
          }
        // Уничтожение окна
        rdsDeleteObject(window);
        return ok;
      }
      //=========================================

      //====================================================================
      // Блок-исполнитель функции, записывающий сообщение в файл
      //====================================================================

      // Функция настройки блока вывода сообщения в файл
      // ВАЖНО: Исходный текст программы должен быть записан в UTF8,
      //   в противном случае необходимо использовать версии функций
      //   с суффиксом "W" и символьные константы с префиксом "L"
      BOOL MessageFuncBlockFileSetup(
        RDS_BHANDLE Block, // Идентификатор блока
        int NumFileVar,    // Номер переменной имени файла
        int NumClearVar)   // Номер переменной флага очистки
      { RDS_HOBJECT window;// Идентификатор объекта-окна
        BOOL ok; // Пользователь нажал "OK"
        char *defval;

        // Создание окна
        window=rdsFORMCreate(FALSE,-1,-1,"Запись в файл");

        // Поле ввода для имени файла
        // Чтение значения переменной по умолчанию
        defval=rdsGetBlockVarDefValueStr(Block,NumFileVar,NULL);
        // Поле ввода – выбор файла с диалогом сохранения
        rdsFORMAddEdit(window,0,1,RDS_FORMCTRL_SAVEDIALOG,"Файл:",300);
        // Фильтр типов файлов для диалога сохранения
        rdsSetObjectStr(window,1,RDS_FORMVAL_LIST,
            "Текстовые файлы (*.txt)|*.txt\nВсе файлы|*.*");
        // Запись значения в поле ввода
        rdsSetObjectStr(window,1,RDS_FORMVAL_VALUE,defval);
        // Освобождение defval
        rdsFree(defval);

        // Очистка при загрузке схемы
        // Чтение значения переменной по умолчанию
        defval=rdsGetBlockVarDefValueStr(Block,NumClearVar,NULL);
        // Поле ввода - флаг
        rdsFORMAddEdit(window,0,2,RDS_FORMCTRL_CHECKBOX,
            "Очищать при загрузке схемы",0);
        // Запись значения в поле ввода
        rdsSetObjectStr(window,2,RDS_FORMVAL_VALUE,defval);
        // Освобождение defval
        rdsFree(defval);

        // Открытие окна
        ok=rdsFORMShowModalEx(window,NULL);
        if(ok)
          { // Пользователь нажал OK – запись измененных значений
            defval=rdsGetObjectStr(window,1,RDS_FORMVAL_VALUE);
            rdsSetBlockVarDefValueStr(Block,NumFileVar,defval);
            defval=rdsGetObjectStr(window,2,RDS_FORMVAL_VALUE);
            rdsSetBlockVarDefValueStr(Block,NumClearVar,defval);
          }
        // Уничтожение окна
        rdsDeleteObject(window);
        return ok;
      }
      //=========================================

      // Блок-исполнитель функции, выводящий сообщение в файл
      // ВАЖНО: в модели используются функции Windows API для работы с файлом.
      // Поскольку в Windows используется кодировка UTF16, в модели вызываются
      // сервисные функции RDS для перевода UTF8 в UTF16.
      extern "C" __declspec(dllexport)
        int RDSCALL MessageFuncBlock_File(int CallMode,
            RDS_PBLOCKDATA BlockData,
            LPVOID ExtParam)
      { RDS_PFUNCTIONCALLDATA func;
      // Макроопределения для статических переменных
      #define pStart ((char *)(BlockData->VarTreeData))
      #define Start (*((char *)(pStart)))
      #define Ready (*((char *)(pStart+RDS_VSZ_S)))
      #define FileName (*((char **)(pStart+2*RDS_VSZ_S)))
      #define ClearOnLoad (*((char *)(pStart+2*RDS_VSZ_S+RDS_VSZ_A)))
        switch(CallMode)
          { // Инициализация
            case RDS_BFM_INIT:
              // Регистрация функции
              if(MessageFunc==0)
                MessageFunc=rdsRegisterFunction(PROGGUIDEMESSAGEFUNC);
              // Объявляем данный блок ее исполнителем
              rdsRegisterFuncProvider(MessageFunc,FALSE);
              break;

            // Очистка
            case RDS_BFM_CLEANUP:
              // Отмена регистрации блока как исполнителя
              rdsUnregisterFuncProvider(MessageFunc);
              break;

            // Проверка типов статических переменных
            case RDS_BFM_VARCHECK:
              return strcmp((char*)ExtParam,"{SSAL}")?
                RDS_BFR_BADVARSMSG:RDS_BFR_DONE;

            // Вызов функции настройки
            case RDS_BFM_SETUP:
              return MessageFuncBlockFileSetup(BlockData->Block,2,3)?1:0;

            // Загрузка схемы только что завершилась
            case RDS_BFM_AFTERLOAD:
              if(ClearOnLoad) // Включена очистка файла при загрузке
                { // Формируем в fullpath полный путь к файлу, т.к.
                  // введенный пользователем может быть неполным
                  char *fullpath=rdsGetFullFilePath(FileName,NULL,NULL);
                  if(fullpath) // Полный путь существует
                    { // Преобразуем в UTF16
                      RDSWSTR fullpath_w=rdsUTF8toUTF16(fullpath,FALSE);
                      DeleteFileW(fullpath_w); // Удаляем файл
                      // Освобождаем память
                      rdsFree(fullpath_w);
                      rdsFree(fullpath);
                    }
                }
              break;

            // Вызов функции
            case RDS_BFM_FUNCTIONCALL:
              func=(RDS_PFUNCTIONCALLDATA)ExtParam;
              if(func->Function==MessageFunc)
                { // Вызвана наша функция – приводим параметры к правильному типу
                  TProgGuideMessageFuncParams *params=
                      (TProgGuideMessageFuncParams*)(func->Data);
                  char *levelstr,*fullpath;
                  RDSWSTR fullpath_w; // Для имени файла в UTF16
                  if(params==NULL ||
                     params->servSize<sizeof(TProgGuideMessageFuncParams))
                    break; // Параметров нет или неверный размер
                  // Записываем в переменную levelstr строку,
                  // соотвествующую важности сообщения
                  switch(params->Level)
                    { case 0:  levelstr="Информация";
                               break;
                      case 1:  levelstr="Предупреждение";
                               break;
                      default: levelstr="Ошибка";
                    }
                  // Формируем в fullpath полный путь к файлу
                  fullpath=rdsGetFullFilePath(FileName,NULL,NULL);
                  if(fullpath) // Полный путь сформирован
                    { HANDLE h;
                      // Преобразуем в UTF16
                      fullpath_w=rdsUTF8toUTF16(fullpath,FALSE);
                      // Открываем файл fullpath на запись
                      h=CreateFileW(fullpath_w,GENERIC_WRITE,0,NULL,
                                    OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,NULL);
                      if(h!=INVALID_HANDLE_VALUE)
                        { // Файл открыт
                          char buf[100]; DWORD temp; SYSTEMTIME time;
                          // Перемещаем указатель файла в конец
                          SetFilePointer(h,0,NULL,FILE_END);
                          // Получаем текущую дату и время
                          GetLocalTime(&time);
                          // Формируем строку с датой и временем в buf
                          sprintf(buf,"%02d-%02d-%04d %02d:%02d:%02d "
                            "%s: ",time.wDay,time.wMonth,time.wYear,
                            time.wHour,time.wMinute,time.wSecond,
                            levelstr);
                          // Записываем дату, время и важность
                          WriteFile(h,buf,strlen(buf),&temp,NULL);
                          // Записываем текст сообщения
                          if(params->MessageStr)
                            WriteFile(h,params->MessageStr,
                              strlen(params->MessageStr),&temp,NULL);
                          // Записываем перевод строки
                          WriteFile(h,"\r\n",2,&temp,NULL);
                          // Закрываем файл
                          CloseHandle(h);
                        }
                      // Освобождаем память, отведенную под полный путь
                      rdsFree(fullpath_w);
                      rdsFree(fullpath);
                    }
                }
              break;
          }
        return RDS_BFR_DONE;
      // Отмена макроопределений
      #undef ClearOnLoad
      #undef FileName
      #undef Ready
      #undef Start
      #undef pStart
      }
      //=========================================