Практически, еще когда только начинал создаваться Йоксель, было ясно, что возможность загрузить данные из существующей таблицы Мокселя просто напрашивается. Нельзя сказать, что этот функционал необходим, но иногда точно может быть полезен. Сначала предполагался примерно такой путь реализации: присосаться к объекту CSheet или CSheetDoc и при помощи торчащих оттуда методов вытягивать данные. Путь это довольно тяжелый и сопоставим по объему с написанием отдельного конвертера типа mxl => yoksel. Поэтому реализация возможности импорта из объекта «Таблица» была отложена в весьма долгий ящик.
Однако, в процессе изучения формата Мокселя стало ясно, что файл mxl по сути представляет собой продукт сериализации объекта типа CSheetDoc. Соответственно, появилась идея простого доступа к данным моксель-документа: взять CMemFile, на его основе создать CArchive, сериализовать в этот архив CSheetDoc и полученный от CMemFile блок памяти скормить в конвертер из формата mxl. Путь, в общем-то, довольно некрасивый, связанный с большим оверхедом по памяти, но все же очень простой. К тому же от одного из первых пользователей Йокселя как раз поступило рацпредложение: «а, типа, почему бы не сделать?...»
Изучение вопроса дало следующее. Объект типа «Таблица» представляет собой класс CTableOutputContext. Класс имеет, приблизительно, такую структуру:
Естественно, скорее всего, структура немного больше, но при доступе через указатель на существующий объект это сути не меняет.
Дальше просто. Делаем, как сказано выше: CMemFile => CArchive => CSheetDoc.Serialize. При этом обязательно следует заполнить член m_pDocument объекта CArchive. Иначе таблицы, содержащие ячейки с ориентацией текста, будут сериализоваться некорректно: в заголовок файла версия будет писаться 7-мая, а все ячейки записываться как для 6-ой.
И три небольшие проблемы этого алгоритма:
- Для CMemFile необходимо переопределить в конструкторе значение автоприращения буфера на 1Мб. По умолчанию там 1024 байта. В результате при сериализации больших таблиц (например 80–100Мб) MFC начинает бегать туда-сюда со своим килобайтом и создавать тормоза. С мегабайтом все работает на порядок быстрее.
- Возможна ситуация: создаем таблицу, показываем ее пользователю, пользователь ее закрывает, а объект типа «Таблица» остается жив. Если передать этот объект в ТабличныйДокумент.ЗагрузитьИзТаблицы, то в CTableOutputContext m_pDoc будет иметь ненулевое значение, но сам документ (CSheetDoc) на этот момент уже мертв. Соответственно, попытка вызвать Serialize будет приводить к вылетанию. Проблема решается достаточно просто: необходимо через CTableOutputContext обратиться к какому-нибудь методу объекта «Таблица» (например, «ВысотаТаблицы») – это приведет к созданию нового объекта CSheetDoc, к которому уже можно без проблем присобачиться.
- Проблема обработки ошибок выделения памяти. Дело в том, что если при вызове CSheetDoc.Serialize происходит ошибка выделения памяти, то ее нет возможности поймать из Йокселя. Сначала вылетает обычное для MFC CMemoryException. При обработке этого исключения, где-то в недрах MFC или 1С, происходит ПОВТОРНОЕ выкидывание CMemoryException. А это уже фатально. Если при одном активном исключении возбуждается еще одно, то, по стандарту, это приводит к автоматическому вызову std::terminate. Вот поэтому, при использовании метода ЗагрузитьИзТаблицы можно потерять несохраненные пользовательские данные при нехватке памяти в системе или при обработке очень больших объемов данных.
Ссылок на эту страницу нет