Электронная библиотека книг Александра Фролова и Григория Фролова.
Shop2You.ru Создайте свой интернет-магазин
Библиотека
Братьев
Фроловых

Операционная система Microsoft Windows 3.1 для программиста

© Александр Фролов, Григорий Фролов
Том 12, М.: Диалог-МИФИ, 1993, 255 стр.

[Назад] [Содеожание] [Дальше]

1.6. Произвольные данные

Ресурсы приложения Windows могут включать в себя произвольные данные, такие как таблицы перекодировки, звуковые данные и т. п. При необходимости приложение может загрузить эти данные в память и делать с ними все что угодно.

Включение произвольных данных в ресурсы приложения

Для включения произвольных данных в ресурсы приложения файл описания ресурсов должен содержать один или несколько операторов следующего вида:

RId [тип ресурса] [параметры загрузки] [тип памяти] имя файла

RId является идентификатором ресурса. Можно использовать любое имя или число.

Для типа ресурса вы можете выбрать любое обозначение. Разумеется, не следует выбирать названия предопределенных ресурсов, такие как ICON или CURSOR.

Параметры загрузки и тип памяти указывается так же, как и для других описанной нами ранее ресурсов. В качестве имени файла необходимо указать имя файла, содержащего данные, например:

Hello SOUND hello.wav

Вся работа по подготовке файла с данными выполняется программистом перед сборкой проекта.

Загрузка произвольных данных из ресурсов приложения

Получение идентификатора произвольных данных из ресурсов приложения - трехступенчатый процесс. Вначале с помощью функции FindResource необходимо определить расположение ресурса в файле приложения:

HRSRC WINAPI FindResource(HINSTANCE hInst, 
   LPCSTR lpszName, LPCSTR lpszType);

Параметр hInst является идентификатором модуля, содержащего ресурс. Для извлечения ресурса из приложения вы должны указать его идентификатор, передаваемый функции WinMain через параметр hInstance.

Параметр lpszName должен содержать адрес имени ресурса. Для загрузки произвольных данных в качестве этого параметра следует передать указатель на строку, содержащую идентификатор ресурса. В приведенном выше примере используется идентификатор "Hello".

Параметр lpszType - адрес строки, содержащий тип ресурса. Для нашего примера это должна быть строка "SOUND".

Таким образом, поиск ресурса, описанного как

Hello SOUND hello.wav

должен выполняться следующим образом:

HRSRC hRsrc;
hRsrc = FindResource(hInstance, "Hello", "SOUND");

Функции FindResource в качестве третьего параметра можно передавать идентификаторы предопределенных типов ресурсов, список которых приведен ниже (некоторые из перечисленных типов ресурсов вам пока незнакомы, мы расскажем о них позже).

Идентификатор ресурса Название ресурса
RT_ACCELERATOR Таблица акселераторов
RT_BITMAP Изображение bitmap
RT_CURSOR Курсор
RT_DIALOG Диалоговая панель
RT_FONT Шрифт
RT_FONTDIR Каталог шрифтов
RT_ICON Пиктограмма
RT_MENU Меню
RT_RCDATA Произвольные данные
RT_STRING Таблица строк

Вы можете использовать функцию FindResource для загрузки таких ресурсов, как пиктограммы или курсоры, указав ей тип ресурса, соответственно, RT_ICON или RT_CURSOR. Однако в документации к SDK сказано, что загрузку предопределенных ресурсов, таких как пиктограммы и курсоры, следует выполнять специально предназначенными для этого функциями (LoadIcon, LoadCursor и т. д.).

На втором этапе, после того как ресурс найден, его следует загрузить, вызвав функцию LoadResource:

HGLOBAL WINAPI LoadResource(HINSTANCE hinst, HRSRC hrsrc);

Параметр hinst представляет собой идентификатор модуля, из файла которого загружается ресурс. Если ресурс загружается из файла вашего приложения, используйте значение hInstance, полученное через соответствующий параметр функции WinMain.

В качестве второго параметра этой функции следует передать значение, полученное от функции FindResource.

Третий этап заключается в фиксировании ресурса в оперативной памяти функцией LockResource:

void FAR* WINAPI LockResource(HGLOBAL hGlb);

В качестве параметра hGlb функции LockResource следует передать идентификатор ресурса, полученный от функции LoadResource.

Функция LockResource фиксирует данные в памяти и возвращает дальний указатель на соответствующий буфер. После фиксирования Windows не станет удалять сегмент с ресурсами из памяти, так что приложение сможет использовать данные в любой момент времени.

После того как приложение использовало ресурс и он стал ненужен, следует расфиксировать память ресурса, вызвав функцию UnlockResource. Функция определена через функцию GlobalUnlock следующим образом:

BOOL WINAPI GlobalUnlock(HGLOBAL hGlb);
#define UnlockResource(h) GlobalUnlock(h)

Эти, а так же другие функции управления памятью мы рассмотрим позже, в отдельной главе, посвященной управлению памятью в операционной системе Windows.

Перед завершением работы приложения следует освободить полученный ресурс, вызвав функцию FreeResource:

BOOL WINAPI FreeResource(HGLOBAL hGlb);

В качестве параметра hGlb следует передать идентификатор ресурса, полученный от функции LoadResource.

Приложение OEM3ANSI

В предыдущем томе "Библиотеки системного программиста" мы привели исходные тексты приложения OEM2ANSI, преобразующего файла из кодировки OEM (принятую в MS-DOS) в кодировку ANSI (принятую в Windows). В некоторых случаях нужны дополнительные таблицы перекодировки. Например, при переносе текстовых файлов из среды операционных систем ЕС ЭВМ (IBM 370) в среду Windows иногда требуется выполнять замену сходных по начертанию букв латинского алфавита на буквы русского алфавита или наоборот, букв русского алфавита на буквы латинского алфавита. Это связано с тем, что в некоторых случаях русские тексты, подготовленные в системах ЕС ЭВМ, содержат латинские буквы. Например, вместо русской заглавной буквы "А" используется латинская заглавная буква "A". Есть шрифты, где эти буквы имеют разное начертание. Кроме того, при смешивании латинских и русских букв могут появится проблемы с сортировкой.

Поэтому для таких текстов перед преобразованием из OEM в ANSI требуется выполнять дополнительные преобразования.

Приложение OEM3ANSI выполняет такое дополнительное преобразование, используя таблицу перекодировки, загружаемую из ресурсов приложения. Эта таблица подготовлена таким образом, что сходные по начертанию латинские буквы заменяются на русские. Вы, разумеется, можете выполнять вместо этого любое другое преобразование, изменив таблицу перекодировки.

Главный файл приложения OEM3ANSI приведен в листинге 1.22.


Листинг 1.22. Файл oem3ansi\oem3ansi.cpp


// ----------------------------------------
// Перекодировка текстового файла
// из OEM в ANSI с использованием
// дополнительной таблицы перекодировки
// ----------------------------------------

#define STRICT
#include <windows.h>
#include <commdlg.h>
#include <mem.h>

// Прототипы функций
HFILE GetSrcFile(void);
HFILE GetDstFile(void);
void  Oem3Ansi(HFILE, HFILE);

// Указатель на таблицу перекодировки,
// которая будет загружена из ресурсов
char far * lpXlatTable;

// -------------------------------
// Функция WinMain
// -------------------------------

#pragma argsused
int PASCAL
WinMain(HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR     lpszCmdLine,
        int       nCmdShow)
{
  HFILE hfSrc, hfDst;

  // Положение ресурса в файле
  HRSRC   hResource;

  // Идентификатор таблицы перекодировки
  HGLOBAL hXlatTable;

  // Определяем расположение ресурса
  hResource   = FindResource(hInstance, "XlatTable", "XLAT");

  // Получаем идентификатор ресурса
  hXlatTable  = LoadResource(hInstance, hResource);

  // Фиксируем ресурс в памяти, получая его адрес
  lpXlatTable = (char far *)LockResource(hXlatTable);

  // Если адрес равен NULL, при загрузке или
  // фиксации ресурса произошла ошибка
  if(lpXlatTable == NULL)
  {
    MessageBox(NULL, "Error", "Resource loading error",
       MB_OK);
    return(-1);
  }

  // Открываем входной файл.
  hfSrc = GetSrcFile();
  if(!hfSrc) return 0;

  // Открываем выходной файл
  hfDst = GetDstFile();
  if(!hfDst) return 0;

  // Выполняем перекодировку файла
  Oem3Ansi(hfSrc, hfDst);

  // Закрываем входной и выходной файлы
  _lclose(hfSrc);
  _lclose(hfDst);

  UnlockResource(hXlatTable);
  FreeResource(hXlatTable);

  return 0;
}

// -------------------------------
// Функция GetSrcFile
// Выбор файла для перекодировки
// -------------------------------

HFILE GetSrcFile(void)
{
  OPENFILENAME ofn;

  char szFile[256];
  char szFileTitle[256];
  char szFilter[256] =
         "Text Files\0*.txt;*.doc\0Any Files\0*.*\0";
  HFILE hf;

  szFile[0] = '\0';
  memset(&ofn, 0, sizeof(OPENFILENAME));

  // Инициализируем нужные нам поля
  ofn.lStructSize       = sizeof(OPENFILENAME);
  ofn.hwndOwner         = NULL;
  ofn.lpstrFilter       = szFilter;
  ofn.nFilterIndex      = 1;
  ofn.lpstrFile         = szFile;
  ofn.nMaxFile          = sizeof(szFile);
  ofn.lpstrFileTitle    = szFileTitle;
  ofn.nMaxFileTitle     = sizeof(szFileTitle);
  ofn.lpstrInitialDir   = NULL;
  ofn.Flags =   OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST
                                  | OFN_HIDEREADONLY;
  // Выбираем входной файл
  if (GetOpenFileName(&ofn)) {
    hf = _lopen(ofn.lpstrFile, OF_READ);
    return hf;
  }
  else return 0;
}

// -------------------------------
// Функция GetDstFile
// Выбор файла для записи
// результата перекодировки
// -------------------------------

HFILE GetDstFile(void)
{
  OPENFILENAME ofn;

  char szFile[256];
  char szFileTitle[256];
  char szFilter[256] =
         "Text Files\0*.txt;*.doc\0Any Files\0*.*\0";

  HFILE hf;

  szFile[0] = '\0';

  memset(&ofn, 0, sizeof(OPENFILENAME));

  ofn.lStructSize       = sizeof(OPENFILENAME);
  ofn.hwndOwner         = NULL;
  ofn.lpstrFilter       = szFilter;
  ofn.nFilterIndex      = 1;
  ofn.lpstrFile         = szFile;
  ofn.nMaxFile          = sizeof(szFile);
  ofn.lpstrFileTitle    = szFileTitle;
  ofn.nMaxFileTitle     = sizeof(szFileTitle);
  ofn.lpstrInitialDir   = NULL;
  ofn.Flags             = OFN_HIDEREADONLY;

  // Выбираем выходной файл
  if (GetSaveFileName(&ofn)) {

    // При необходимости создаем файл
    hf = _lcreat(ofn.lpstrFile, 0);
    return hf;
  }
  else return 0;
}

// -------------------------------
// Функция Oem3Ansi
// Перекодировка файла
// -------------------------------

void Oem3Ansi(HFILE hfSrcFile, HFILE hfDstFile)
{
  // Счетчик прочитанных байт
  int cbRead;

  // Буфер для считанных данных
  BYTE bBuf[2048];

  // Читаем в цикле файл и перекодируем его,
  // записывая результат в другой файл
  do {
    // Читаем в буфер 2048 байт из входного файла
    cbRead = _lread(hfSrcFile, bBuf, 2048);

    // Выполняем дополнительную перекодировку
    // по таблице, загруженной из ресурсов
    for(int i=0;i < cbRead; i++)
    {
      bBuf[i] = lpXlatTable[bBuf[i]];
    }

    // Перекодируем содержимое буфера
    // из OEM в ANSI
    OemToAnsiBuff(bBuf, bBuf, cbRead);

    // Сохраняем содержимое буфера в
    // выходном файле
    _lwrite(hfDstFile, bBuf, cbRead);

  // Завершаем цикл по концу входного файла
  } while (cbRead != 0);
}

В начале работы приложение ищет и загружает из ресурсов в память дополнительную таблицу перекодировки:

hResource   = FindResource(hInstance, "XlatTable", "XLAT");
hXlatTable  = LoadResource(hInstance, hResource);

После этого ресурс фиксируется в памяти:

lpXlatTable = (char far *)LockResource(hXlatTable);

Адрес зафиксированного ресурса (таблицы перекодировки) записывается в глобальную переменную lpXlatTable. Если этот адрес равен значению NULL, выводится сообщение об ошибке и работа приложения завершается.

После этого приложение OEM3ANSI открывает входной и выходной файлы и выполняет перекодировку, вызывая функцию Oem3Ansi.

Перед завершением работы приложение расфиксирует и удаляет ресурс из памяти:

UnlockResource(hXlatTable);
FreeResource(hXlatTable);

Функция Oem3Ansi выполняет те же действия, что и функция Oem2Ansi из приложения OEM2ANSI, рассмотренного в предыдущем томе. Дополнительно перед перекодировкой из OEM в ANSI (которую выполняет функция OemToAnsiBuff из программного интерфейса Windows) функция Oem3Ansi в цикле перекодирует прочитанный из файла буфер, пользуясь дополнительной таблицей перекодировки, загруженной из ресурсов:

for(int i=0;i < cbRead; i++)
{
  bBuf[i] = lpXlatTable[bBuf[i]];
}
OemToAnsiBuff(bBuf, bBuf, cbRead);

Файл описания ресурсов приложения OEM3ANSI (листинг 1.23) содержит описание единственного ресурса. Тип этого ресурса мы определили сами как XLAT.


Листинг 1.23. Файл oem3ansi\oem3ansi.rc


/* Таблица перекодировки */
XlatTable XLAT xlatcyr.tbl

Файл описания ресурсов ссылается на таблицу перекодировки (листинг 1.24), длина которой составляет 256 байт.


Листинг 1.24. Файл oem3ansi\xlatcyr.tbl



В этой таблице для каждого возможного значения байта находится соответствующая замена. Байты со значениями от 0x00 до 0x40 при перекодировке остаются без изменения. Вместо заглавной латинской буквы "A" с кодом 0x41 в таблице стоит значение 0x80, соответствующее (в кодировке OEM) русской заглавной букве "А". Аналогичная замена выполняется и для других латинских букв, имеющих сходное с русскими буквами начертание.

Файл определения модуля приложения OEM3ANSI представлен в листинге 1.25.


Листинг 1.25. Файл oem3ansi\oem3ansi.def


; =============================
; Файл определения модуля
; =============================
NAME        OEM3ANSI
DESCRIPTION 'Приложение OEM3ANSI, (C) 1994, Frolov A.V.'
EXETYPE     windows
STUB        'winstub.exe'
STACKSIZE   5120
HEAPSIZE    1024
CODE        preload moveable discardable
DATA        preload moveable multiple

Для облегчения процедуры подготовки таблицы перекодировки вы можете использовать программу tabgen.exe, исходный текст которой приведен в листинге 1.26.


Листинг 1.26. Файл oem3ansi\tabgen.cpp


#include <stdio.h>
#include <stdlib.h>

int main (void)
{
  FILE *tfile;

  tfile=fopen("xlat.tbl","w+b");
  if(tfile == NULL)
  {
    printf("File creation error!");
    exit(1);
  }

  for(unsigned int i=0; i < 256; i++)
    fputc((char)i, tfile);

  fclose(tfile);
  return 0;
}

Эта программа создает в текущем каталоге файл с именем xlat.tbl, состоящий из 256 байт с последовательно возрастающими от 0 до 255 значениями. При использовании такого файла приложение OEM3ANSI не выполняет никакой дополнительной перекодировки, однако вы можете отредактировать этот файл, например, при помощи редактора diskedit.exe из пакета утилит Нортона.

Содержимое таблицы перекодировки можно изменять и после сборки проекта. Для этого надо воспользоваться приложением Resource Workshop.

Запустите Resource Workshop и из меню "File" выберите строку "Open Project". В меню "File type" выберите строку "EXE application" и откройте файл oem3ansi.exe. На экране появится окно с заголовком "oem3ansi.exe", в котором находится список имеющихся в файле приложения ресурсов. Вы увидите там строку "XLATTABLE". Это идентификатор ресурса, определенный в файле описания ресурсов приложения. Сделайте по нему двойной щелчок левой клавишей мыши. Появится окно редактирования "XLAT:XLATTABLE", в котором вы сможете изменить значение отдельных байт таблицы. После редактирования сохраните изменения, выбрав из меню "File" строку "Save project" и завершите приложение Resource Workshop.

[Назад] [Содеожание] [Дальше]