Сообщений 3    Оценка 0        Оценить  
Система Orphus

Кроссплатформенное взаимодействие с устройствами ввода-вывода

Этап первый – библиотека UNIPRINT

Автор: Васильев Сергей Александрович
Опубликовано: 21.09.2011
Исправлено: 10.12.2016
Версия текста: 1.1
Введение
Семейство библиотек UNIO
Интерфейс библиотеки UNIPRINT
Внутренняя архитектура
Основа внешнего интерфейса библиотеки
Интерфейс программирования для языка C
Примеры использования
Заключение
Список литературы

Введение

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

К наиболее популярным кроссплатформенным библиотекам для языка C++ можно отнести:

Одним из главных препятствий на пути построения полнофункциональных кроссплатформенных решений является отсутствие открытых библиотек, предоставляющих унифицированные программные интерфейсы взаимодействия с устройствами ввода-вывода. В частности, не существует унифицированного средства взаимодействия с такими популярными сегодня устройствами, как принтеры, сканеры, web-камеры и микрофоны.

Семейство библиотек UNIO

В целях устранения имеющегося препятствия на пути построения кроссплатформенных решений мной ведётся разработка семейства библиотек по унификации и выравниванию программных интерфейсов взаимодействия с устройствами ввода-вывода, взаимодействие с которыми не покрыто уже имеющимися открытыми библиотеками.

Семейство библиотек будет включать в себя четыре конечные библиотеки:

Внешний интерфейс каждой библиотеки будет включать:

  1. Набор C++ классов, моделирующих абстрактные объекты предметной области, охватывающей соответствующие устройства ввода вывода и связанные с ними процессы.
  2. Функциональное C-API доступа к устройствам – обёртка над классами из пункта 1 для чистого языка программирования C, реализованная на основе механизма дескрипторов (аналогично подходу, использованному, например, в WinAPI).
  3. STL-совместимый интерфейс потокового вывода – набор шаблонов, предоставляющих потоковое C++ API на базе механизма iostream.

Первый этап разработки, по результатам которого пишется данная статья, ограничивается созданием унифицированной библиотеки взаимодействия с устройствами печати – UNIPRINT. Подробное описание принятых при разработке данной библиотеки решений, её API, а также сценариев её использования описаны в последующих разделах статьи.

Для понимания дальнейших материалов статьи от читателей потребуются знание основ объектно-ориентированного программирования на языке C++ (оценочный уровень сложности – 2 из 3), библиотеки STL (2 из 3) и принципов разработки и использования библиотек динамической компоновки (1 из 3).

Интерфейс библиотеки UNIPRINT

Внутренняя архитектура

Создание унифицированного кроссплатформенного программного интерфейса обычно сводится к написанию единообразной обёртки над множеством имеющихся специализированных программных интерфейсов всех поддерживаемых операционных систем. Библиотека UNIPRINT в данном случае не является исключением и также реализуется по указанной полиморфной схеме.

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

Составы дистрибутива открытой библиотеки UNIPRINT для Windows и Unix-подобных операционных систем, соответственно, различаются. В сборки под Unix дополнительно включены бинарные файлы библиотеки CUPS.

Таким образом, библиотека UNIPRINT покрывает весь спектр наиболее популярных персональных и промышленных операционных систем, её использование для различных платформ не имеет никаких специфических ограничений и не требует установки специализированных драйверов (как того требует, к примеру, использование библиотеки CUPS для операционной системы Windows).

Основа внешнего интерфейса библиотеки

Как уже отмечалось выше, в основе внешнего интерфейса библиотеки лежит набор классов, реализующих сущности, составляющие предметную область взаимодействия приложения с принтером. Каждый класс внешнего интерфейса объявлен в одноимённом заголовочном файле с расширением hpp.

Рассмотрим данные классы в нисходящем по отношению к уровню абстракции порядке следования.

print_helper – класс-помощник, который реализует функции менеджера работы с принтерами, позволяющие получить список доступных принтеров, информацию о каждом принтере, а также «открыть» принтер для печати. Объявление класса имеет следующий вид:

        class UNIPRINTAPI print_helper
{
private:
  print_helper(void);
  print_helper(const print_helper&);
  ~print_helper(void);
  print_helper& operator = (const print_helper&);
  
public:
  // получить список имеющихся принтеровstaticint enum_printers (
    int enum_flags, std::vector<printer_enum_info>* printers);
  // получить информацию об указанном принтереstaticint get_printer_info (constchar* name, printer_detailed_info* pdi);
  // открыть принтер для печатиstatic printer* open_printer (constchar* name);
};

Как можно увидеть, конструкторы данного класса недоступны пользователю, таким образом, класс не предназначен для создания экземпляров объектов на его основе, а всего лишь нужен для удобной компоновки статических функций.

В качестве выходных параметров функций получения данных о принтерах используются структуры printer_enum_info и printer_detailed_info, объявленные в заголовочном файле print_structs.h:

        struct printer_enum_info
{
  // наименование сервера, к которому подключен принтер
  simplestring server_name;
  // наименование принтера
  simplestring printer_name;
};

struct printer_detailed_info
{
  // наименование сервера, к которому подключен принтер
  simplestring server_name;
  // наименование принтера
  simplestring printer_name;
  // параметры принтераint flags;
  // общепринятое наименование принтера
  simplestring shared_name;
  // комментарии к принтеру
  longstring comment;
  // расположение принтера
  simplestring location;
  // приоритет принтераint priority;
  // приоритет принтера по умолчаниюint default_priority;
  // статус принтераint status;
  // колличество заданий в очереди принтераint jobs_in_queue;
  // средняя скорость печати (страниц в минуту)int avg_ppm;
};

Для именования статусов и флагов, как и других перечислимых множеств, используемых в других структурах, описываемых ниже по тексту статьи, используются соответствующие константы Win32 API, дополнительно объявляемые и транслируемые для других операционных систем в заголовочном файле print_core.h.

printer – промежуточный класс, реализующий некоторые функции доступа к принтеру. Его объявление имеет следующий вид:

        class UNIPRINTAPI printer
{
private:
  HPRINTER hnative;

  printer(const printer &);
  printer& operator = (const printer&) const;

  friendclass print_helper;
  friendclass print_job;

private:
  explicit printer (HANDLE);

public:
  ~printer (void);

  // получить идентификатор принтера
  HPRINTER get_native (void) const;

  // получить детальную информацию о принтереint get_detailed_info (printer_detailed_info* pdi) const;
  // получить конфигурацию принтера по умолчаниюint get_default_configuration (printer_configuration* pconf);
};

Структура printer_configuration также объявлена в заголовочном файле print_structs.h и имеет следующий вид:

        struct printer_configuration
{
  // ориентация бумаги (обычная/альбомная)int paper_orientation;
  // размер бумагиint paper_size;
  // длина бумагиint paper_length;
  // ширина бумагиint paper_width;
  // источник бумаги (для принтеров с несколькими источниками)int paper_source;
  // режим цветопередачиint color_mode;
  // режим двусторонней печатиint duplex_mode;
  // разрешение печати по ширинеint resolution_x;
  // разрешение печати по высотеint resolution_y;
  // тип работы со шрифтами true typeint true_type_option;
  // тип множественной печатиint collation_mode;
  // медиа тип печатаемого контентаint media_type;
};

print_job – основной класс библиотеки, реализующий сущность «задание для печати», объявлен в заголовочном файле print_job.hpp:

        class UNIPRINTAPI print_job
{
private:
  printer& m_printer;
  PRINTJOBID m_job_id;
  print_job_info m_job_info;
  bool m_attached;

public:
  explicit print_job (printer& p);
  explicit print_job (printer& p, const print_job_info& pji);
  ~print_job(void);

  // связать/отвязать нативный идентификатор задания с объектом классаint attach (PRINTJOBID job_id);
  PRINTJOBID detach (void);

  // получить нативный идентификатор задания
  PRINTJOBID get_job_id (void) const;
  // получить информацию о заданииint get_print_job (print_job_info* pji) const;

  // получить/изменить настройки печатиint get_configuration (printer_configuration* pconf) const;
  int set_configuration (const printer_configuration& pconf);

  // начать/закончить печать очередной страницыint start_page (void);
  int end_page (void);

  // функции печатиint print (constvoid* buffer, int size);
  template <int size> inlineint print (constchar buffer[size])
  {
    returnthis->print((constvoid*)&buffer[0],size);
  }

  // поставить задание в очередь на печатьint schedule (void);
  // отменить выполнение заданияint abort (void);
};

Дополнительно в перечень классов внешнего интерфейса библиотеки включен класс auto_print_pager, реализующий автоматическое разбиение на страницы:

        class UNIPRINTAPI auto_print_pager
{
private:
  print_job& m_job;

  auto_print_pager (const auto_print_pager&);
  auto_print_pager& operator = (const auto_print_pager&);

public:
  auto_print_pager (print_job& job);
  ~auto_print_pager (void);
};

Интерфейс программирования для языка C

Описанный в предыдущих разделах основной программный интерфейс библиотеки uniprint специализирован для использования в программах на языке C++. Вместе с тем, концепции, реализованные в библиотеке, априори могут быть востребованы и при программировании на других языках программирования. Именно поэтому в качестве альтернативного интерфейса программирования библиотеки было решено поддержать процедурный интерфейс для языка C, который может быть задействован при программировании на любых языках, поддерживающих использование библиотек динамической компоновки.

Технически реализация данного интерфейса программирования представляет собой процедурную обёртку над описанным выше интерфейсом программирования для языка C++. В основе процедурного интерфейса библиотеки лежит широко зарекомендовавший себя в программных интерфейсах других библиотек механизм дескрипторов.

Объявление всех функций интерфейса представлено ниже:

        // helper функции
UNIPRINTCAPI int enum_printers (
  int enum_flags, printer_enum_info* enum_arr, unsigned arr_size);
UNIPRINTCAPI int get_printer_info (
  constchar* name, printer_detailed_info* pdi);

// функции работы с принтером
UNIPRINTCAPI HPRINTER open_printer (constchar* name);
UNIPRINTCAPI int get_printer_detailed_info (
  HPRINTER hprinter, printer_detailed_info* pdi);
UNIPRINTCAPI int get_printer_default_configuration (
  HANDLE hprinter, printer_configuration* pconf);
UNIPRINTCAPI int close_printer (HPRINTER hprinter);

// функции работы с заданием на печать
UNIPRINTCAPI PRINTJOBID start_print_job (HPRINTER hprinter);
UNIPRINTCAPI int close_print_job (PRINTJOBID jobid);
UNIPRINTCAPI int get_print_job_info (PRINTJOBID jid, print_job_info* pji);
UNIPRINTCAPI int get_print_job_configuration (
  PRINTJOBID jid, printer_configuration* pconf);
UNIPRINTCAPI int set_print_job_configuration (
  PRINTJOBID jid, const printer_configuration& pconf);
UNIPRINTCAPI int print_job_start_page (PRINTJOBID jid);
UNIPRINTCAPI int print_job_end_page (PRINTJOBID jid);
UNIPRINTCAPI int print_job_print (
  PRINTJOBID jid, constvoid* buffer, int size);
UNIPRINTCAPI int print_job_schedule (PRINTJOBID jid);
UNIPRINTCAPI int print_job_abort (PRINTJOBID jid);

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

Примеры использования

Простейший пример вывода данных на печать через принтер по умолчанию имеет следующий алгоритм:

  1. При помощи класса-помощника print_helper найти и открыть на печать нужный принтер (создать объект класса printer).
  2. Создать задание на печать (объект print_job).
  3. Собственно осуществить вывод на печать нужных данных.

Пример реализации данного алгоритма:

      // пытаемся найти принтер по умолчанию
  std::vector<printer_enum_info> v;
  print_helper::enum_printers(PRINTER_ENUM_DEFAULT,v);
  if (v.size() == 0)
  {
    std::cout << "No printers found!" << std::endl;
    return 0;
  }

  // открываем принтер по умолчанию
  printer* p = print_helper::open_printer(&v.at(0).printer_name[0]);
  if (p == NULL)
  {
    std::cout << "Failed to open printer!" << std::endl;
    return 0;
  }
  // создаём экземпляр задания для печати
  print_job j(p);

  // выводим на печать какие-либо данные
  j.start_page();
  j.print(...);
  j.end_page();

При необходимости вывода на печать нескольких страниц целесообразно использовать автоматический класс auto_print_pager. Пример:

      // в цикле печатаем страницы
      for (unsigned i=0; i<pagecnt; i++)
  {
    auto_print_pager a(p);
    j.print(...);
  }

В приведённом примере pagecnt – переменная, содержащая количество страниц для вывода на печать. Разделение текста на страницы в данном случае осуществляется автоматически создаваемым и удаляемым на каждом шаге цикла переменной класса auto_print_pager.

На шаге 2 алгоритма вместо создания задания для печати разработчики могут использовать STL-совместимый шаблон потокового вывода данных basic_oprintstream, либо его специализацию oprintstream. Объект класса print_job в данном случае автоматически инкапсулируется шаблоном basic_oprintstream. Также при использовании шаблона не нужно явно вызывать функцию начала печати страницы, как это имело место для класса print_job. Для начала печати следующей страницы нужно записать в поток специальную константу endp, аналогичную специальной константе перевода строки endl пространства имён std.

Пример использования шаблона потоковой печати:

      // открываем поток печати
  oprintstream s(p);
  // в цикле печатаем страницыfor (unsigned i=0; i<pagecnt; i++)
    s << pagedata << endp;

Теперь рассмотрим, как те же самые действия можно выполнить через программный интерфейс библиотеки на языке программирования C:

      // ищем принтер по умолчанию
  printer_enum_info pei;
  int pcount = enum_printers(PRINTER_ENUM_DEFAULT,&pei,1);
  if (pcount <= 0)
  {
    printf("No printers found!\n");
    return 1;
  }
  // пытаемся открыть принтер по умолчанию
  HPRINTER hprinter = open_printer(&pei.printer_name[0]);
  if (!hprinter)
  {
    printf("Failed to open printer!\n");
    return 1;
  }
  // создаём задание на печать
  PRINTJOBID jobid = start_print_job(hprinter);
  if (!jobid)
  {
    printf("Failed to start print job!\n");
    close_printer(hprinter);
    return 1;
  }
  // выводим на печать какие-либо данныеfor (unsigned i = 0; i < pagecnt; i++)
  {
    print_job_start_page(jobid);
    print_job_print(jobid,something,sizeof(something));
    print_job_end_page(jobid);
  }
  // закрываем задание на печать
  close_print_job(jobid);
  // закрываем принтер
  close_printer(hprinter);

Заключение

В свете наличия большого числа персональных и промышленных операционных систем кроссплатформенное выравнивание программных интерфейсов имеет важное практическое значение. В связи с этим я надеюсь, что разрабатываемая мной кроссплатформенная библиотека взаимодействия с устройствами ввода-вывода (в частности, разработанная на первом этапе библиотека работы с принтерами) окажется полезной и востребованной.

Поскольку отдельный сайт библиотеки на данный момент ещё не создан (ссылка на него будет опубликована в одной из последующих статей), в случае возникновения интереса к данной библиотеке уважаемые читатели могут обращаться напрямую к автору. Мой адрес электронной почты – Lord-ArchDevil@yandex.ru.

Список литературы

  1. Бьерн Страуструп. Язык программирования C++. – СПб.: Невский диалект, 2008.
  2. Мэтью Г. Остерн. Обобщённое программирование и STL: Использование и наращивание стандартной библиотеки шаблонов C++. – СПб.: Невский диалект, 2004.
  3. Microsoft Developer Network for Visual Studio 2010.


Любой из материалов, опубликованных на этом сервере, не может быть воспроизведен в какой бы то ни было форме и какими бы то ни было средствами без письменного разрешения владельцев авторских прав.
    Сообщений 3    Оценка 0        Оценить