Техника объектно-ориентированного программирования и Turbo Vision дают Вам мощный способ инкапсуляции данных и кода и мощные способы построения взаимосвязанных структур объекта. Но что если Вы хотите просто сохранить объекты на диске? До последнего времени данные хранились в записях и запись на диск была проста, но данные внутри Turbo Vision в большинстве случаев находятся внутри объектов. Конечно, Вы можете отделить данные от объекта и записать их на диск. Но поскольку совместное хранение данных и кода дает большие преимущества, разделить их снова было бы шагом назад. Не могут ли ООП и Turbo Vision как-то решить эту проблему? Имеено для этого разработаны потоки. Поток Turbo Vision - это коллекция объектов с определенным способом хранения: обычно в файле, EMS, последовательном порту или некотором другом устройстве. Потоки обрабатывают В/В на уровне объекта, а не на уровне данных. Когда Вы расширяете объект Turbo Vision, Вам необходимо обеспечить обработку всех полей данных, которые Вы определяете.Вопрос: В/В объектов.
Как программист на Паскале Вы знаете, что до того, как Вы сможете выполнять В/В, Вы должны сказать компилятору какой тип данных Вы будете читать или писать в файл. Файл должен быть типированным и тип должен быть определен во время компиляции. Turbo Pascal реализует очень полезное исключение из этого правила: доступ к нетипированному файлу через BlockWrite и BlockRead. Но обход проверки типов создает для программиста определенные сложности, хотя позволяет ему выполнять очень быстрый двоичный В/В. Вторая проблема в том, что Вы не можете использовать файлы прямо с объектами. Turbo Pascal не позволяет Вам создать файл типа объекта. И поскольку объекты могут содержать виртуальные методы, адрес которых определяется во время выполнения, сохранение информации VMT вне программы бессмысленно; чтение такой информации в программу еще более бессмысленно. Вы можете обойти эту проблему. Вы можете скопировать данные из Ваших объектов и сохранить эту информацию в файле, а затем заново построить объекты из этих данных. Но это не элегантное решение и усложняет создание объектов.Ответ: потоки.
Turbo Vision позволяет Вам обойти обе трудности, а так же предоставляет некоторые дополнительные возможности. Потоки предоставляют простой, чрезвычайно элегантный способ хранения данных объекта вне программы.Потоки являются полиморфными.
Поток Turbo Vision дает Вам преимущество и типированных и нетипированных файлов: проверка типов еще выполняется, но то, что Вы хотите послать в поток, не обязано определяться во время компиляции. Причина этого в том, что потоки знают как они работают с объектами и, если только объект порожден от TObject, поток может обрабатывать его. В самом деле, различные объекты Turbo Vision можно легко записать в один поток, как группу идентичных объектов.Управление объектами в потоке.
Все, что Вы должны сделать - это определить для потока, какие объекты он должен обрабатывать, чтобы он знал как совместить данные с VMT. Затем Вы можете поместить объекты в поток и получить их обратно без малейших усилий. Но как можно читать и писать в один поток такие различные объекты как TDeskTop и TDiаlog и во время компиляции не знать даже какие объекты будут обрабатываться? Это сильно отличается от В/В традиционного Паскаля. В действительности поток может обрабатывать даже новые типы объектов, которые еще не были созданы в момент компиляции потока. Это решается с помощью регистрации. Каждому типу объекта Turbo Vision (и новым типам объектов, которые Вы порождаете из иерархии) назначается уникальный регистрационный номер. Этот номер записывается в поток перед данными объекта. Затем, когда Вы читаете объект из потока, Turbo Vision берет регистрационный номер и на основании его знает сколько данных прочитать и какую VMT подсоединить к Вашим данным.Сущность использования потоков.
На фундаментальном уровне Вы можете думать о потоках как о файлах Паскаля. В своей основе файл Паскаля - это просто последовательное устройство В/В, Вы просто пишите в него и читаете из него. В свою очередь поток - это полиморфное последовательное устройство В/В, что означает, что он ведет себя так же, как последовательный файл, но Вы можете так же читать и писать объекты различного типа. Потоки можно так же рассматривать (как и файлы Паскаля) как устройство В/В с прямым доступом, где Вы устанавливаете позицию в файле, читаете или пишите с этой точки, возвращаете позицию указателя файла и т.д. Эти операции так же доступны с потоками и описаны в разделе "Прямой доступ к потокам". Существует 2 аспекта использования потока, которые Вы должны хорошо себе представлять и к счастью они оба очень просты. Первый - это установка потока, второй - чтение и запись объектов в поток.Установка потока.
Для использования потока Вы должны просто инициализировать его. Точный синтаксис констрактора Init будет изменяться в зависимости от типа используемого потока. Например, если Вы открываете поток DOS, Вам требуется передать имя файла DOS и режим доступа (чтение, запись, чтение/запись) к файлу, содержащему поток. Например, чтобы инициализировать буферизованный поток DOS для загрузки объекта панель экрана в программу, Вам требуется только: var SaveFile: TBufStream; begin SaveFile.Init('SAMPLE.DSK', stOpen, 1024); . После того, как Вы инициализировали поток, Вы можете его использовать. TStream - это абстрактный механизм потока и Вы не можете создать экземпляр от него, но все объекты потоков порождены от TStream. Они включают TDosStream, обеспечивающий дисковый В/В, TBufStream, обеспечивающий буферизованный В/В с диска (полезен, если Вы читаете или пишите большое количество небольших кусков данных) и TEmsStream, который пересылает объекты в EMS память (особенно полезен для реализации быстрых ресурсов). Turbo Vision так же реализует индексированные потоки с указателем на место в потоке. Изменяя положение указателя, Вы можете осуществлять прямой доступ к потоку.Чтение и запись потока.
Базовый объект потоков TStream реализует 3 основных метода: Get, Put и Error. Get и Put примерно соответствуют процедурам Read и Write. Процедура Error вызывается при возникновении ошибки в потоке.Вывод в поток.
Вначале рассмотрим процедуру Put. Общий синтаксис метода Put: SomeStream.Put(PSomeObject); где SomeStream - это любой объект, порожденный от TStream и инициализированный, а PSomeObject - это указатель на любой объект, порожденный от TObject и зарегистрированный с этим потоком. Это все, что Вы должны сделать. Поток может узнать из VMT PSomeObject тип объекта (предполагая, что тип был зарегистрирован) и, следовательно, знать, какой номер ID записывать и сколько данных записать после него. Однако для Вас, как для программиста на Turbo Vision, будет особенно важно то, что когда Вы записываете в поток группу с подэлементами, подэлементы будут автоматически записываться в поток. Поэтому сохранение сложного объекта будет вовсе не сложным, поскольку выполняется автоматически! Вы можете сохранить все состояние Вашей программы просто записав панель экрана в поток. Когда Вы запустите Вашу программу снова и загрузите панель экрана, программа будет в том же состоянии, что и в точке сохранения панели экрана.Ввод из потока.
Считать объекты из потока так же просто. Вы делаете это, используя функцию Get: PSomeObject := SomeStream.Get; где как и раньше SomeStream - инициализированный поток Turbo Vision, а РSomeObject - указатель на любой тип объекта Turbo Vision. Get просто возвращает указатель на то, что он считал из потока. Сколько данных он считал и какой тип VMT он назначил этим данным определяется не типом PSomeObject, а типом объекта, найденного в потоке. Поэтому, если объект в текущей позиции SomeStream не того же типа, что PSomeObject, Вы получите случайную информацию. Как и Put, Get восстанавливает сложные объекты. Так, если объект, считанный Вами из потока, - это видимый элемент, владеющий подэлементами, подэлементы будут так же загружены.В случае ошибки.
Наконец, процедура Error определяет что произойдет, когда возникнет ошибка. По умолчанию TStream.Error просто устанавливает 2 поля (Status и ErrorInfo) в потоке. Если Вы хотите выполнить другие действия, например сгенерировать ошибку времени выполнения или вывести ошибку в диалоговое окно, Вам необходимо перекрыть процедуру Error.Удаление потока.
Когда Вы заканчиваете использование потока, Вы вызываете его метод Done точно так же, как Вы вызывали Close для дискового файла. Как и для любого объекта Turbo Vision, Вы делаете это Dispose(SomeStream, Done); который удаляет объект потока.Использование объектов с потоком.
Все стандартные объекты Turbo Vision готовы к использованию с потоками и все потоки Turbo Vision знают эти стандартные объекты. Когда Вы порождает новый тип объекта от одного из стандартных, очень просто подготовить его для использования с потоком и сообщить потоку о его существовании.Методы Load и Store.
Действительное чтение и запись объектов в поток обрабатывается методами Load и Store. Хотя каждый объект должен содержать эти методы для того, чтобы его можно было использовать с потоками, Вы никогда не вызываете их прямо. (Они вызываются из Get и Put). Все, что Вам требуется сделать - это убедиться, что Ваш объект знает как послать себя в поток, когда ему говорят сделать это. Благодаря ООП эта работа очень проста, поскольку большая часть механизма наследуется из объекта предка. Все, что Ваш объект должен сделать - это загрузить или сохранить ту часть, которую Вы добавили; за остальное отвечает вызов метода предка. Например Вы породили новый тип видимого элемента от TWindow, назвав его именем известного сюрреалиста Рене Магритте, который нарисовал много известных картин с окнами: type TМagritte = object(TWindow) Painted: Boolean; constructor Load(var S: TStream); procedure Draw; procedure Store(var S: TStream); end; В часть данных было добавлено только одно поле Boolean. Для того, чтобы загрузить объект, Вы просто читаете стандартный TWindow, а затем читаете дополнительный байт, соответствующий полю Boolean. То же самое применимо к сохранению объекта: Вы просто записываете TWindow, а потом записываете еще один байт. Типичные методы Load и Store для порожденных объектов имеют вид: constructor TМargitte.Load(var S: Stream); begin TWindow.Load(S); S.Read(Painted, SizeOf(Boolean)) end; procedure TМargitte.Store(var S: Stream); begin TWindow.Store(S); S.Write(Painted, SizeOf(Boolean)) end; Предупреждение: Вы полностью ответственны за то, что сохраняется столько же данных, сколько и загружается и что данные загружаются в том же порядке, в каком они сохранялись. Компилятор не выдает ошибок. Это может приводить к огромным проблемам, если Вы неаккуратны. Если Вы изменяете поля объектов, убедитесь, что скорректированы и Load и Store.Регистрация потока.
В дополнение к определению методов Load и Store для нового объекта, Вы должны так же зарегистрировать новый тип объекта в потоке. Регистрация - это простой двухшаговый процесс, Вы определяете запись регистрации потока и передаете ее в глобальную процедуру RegisterType. Чтобы определить запись регистрации потока, просто следуйте формату. Запись регистрации потока - это запись Паскаля типа TStreamRec, определенная: PStreamRec = ^TStreamRec; TStreamRec = record ObjType: Word; VmtLink: Word; Load: Pointer; Store: Pointer; Next: Word; end; Примечание: Все стандартные объекты Turbo Vision зарегистрированы и Вам не нужно делать этого. По соглашениям Turbo Vision все записи регистрации потоков имеют имена соответствующих объектных типов с заменой начальной Т на R. Так запись регистрации для TDeskTop - RDeskTop и запись регистрации для TMagritte - RMagritte. Абстрактные типы, такие как TObject и TView не имеют регистрационных записей, поскольку никогда не создаются экземпляры этого типа.Номера ID объектов.
Поле ObjType - это единственная часть записи, которую Вы должны знать. Каждый новый тип, определенный Вами, будет требовать своего собственного уникального номера типа. Turbo Vision резервирует регистрационные номера от 0 до 99 для стандартных объектов. Вы можете использовать регистрационные номера от 100 до 65,535. Примечание: Вы ответственны за создание и поддержку библиотеки номеров ID для всех новых объектов, которые используются в потоках В/В и должны сделать ID доступными для пользователей Ваших модулей. Как и константы команд, номера, назначаемые Вами могут быть абсолютно произвольными, главное, чтобы они были уникальными.Автоматические поля.
Поле VmtLink - это связь с таблицей виртуальных методов (VMT) объекта. Вы просто назначаете его как смещение типа Вашего объекта: RSomeObject.VmtLink := Ofs(TypeOf(TSomeObject)^); Поля Load и Store содержат адреса методов Load и Store Вашего объекта. RSomeObject.Load := @TSomeObject.Load; RSomeObject.Store := @TSomeObject.Store; Последнее поле Next назначается в процедуре RegisterType и не требует Вашего вмешательства. Оно предназначено для внутреннего использования в связанном списке регистрационных записей потока.Регистрация.
После того, как Вы создали запись регистрации потока, Вы вызываете RegisterType, передавая ему Вашу запись. Так, чтобы зарегистрировать объект TMagritte для использования с потоками, Вы пишите: const RMagritte: TStreamRec = ( ObjType: 100; VmtLink: Ofs(TypeOf(TMagritte)^); Load: @TMagritte.Load; Store: @TMagritte.Store ); RegisterType(RMagritte); Теперь Вы можете выводить экземпляры нового типа объекта в любой поток Turbo Vision и читать эти экземпляры из потоков.Механизм потоков.
Сейчас, когда мы просмотрели использование потоков, давайте посмотрим что делает Turbo Vision с Вашими объектами, когда Вы вводите или выводите их.Работа Put.
Когда Вы посылаете объект в поток методом Put, поток берет указатель VMT со смещением 0 от объекта и просматривает список типов, зарегистрированных с потоками на соответствие. Когда он находит соответствие, поток выбирает регистрационный номер ID объекта и записывает его в поток. Поток затем вызывает метод Store объекта для записи объекта. Метод Store использует процедуру Write потока, которая записывает правильное число байт в поток. Ваш объект ничего не знает о потоке. Это может быть дисковый файл, EMS память или другой тип потока - Ваш объект просто говорит "Запиши меня в поток" и поток выполняет остальное.Работа Get.
Когда Вы читаете объект из потока методом Get, вначале вводится номер ID и сканируется список зарегистрированных типов на соответствие. Когда соответствие найдено, запись регистрации предоставляет потоку положение метода Load и VMT объекта. Затем вызывается метод Load для чтения соответствующего количества данных из потока. И снова, Вы просто говорите потоку взять следующий объект и вернуть указатель на его положение. Ваш объект не заботится, из какого потока он был получен. Поток обеспечивает чтение правильного количества данных, используя метод Load объекта. Это показывает как важно зарегистрировать тип до попытки В/В в поток.Обработка nil указателей на объект.
Вы можете записать nil объект в поток. Однако, когда Вы делаете это, в поток записывается слово со значением 0. При чтении ID = 0, поток возвращает nil указатель, поэтому 0 зарезервирован и не может использоваться как ID номер объекта.Коллекции в потоках: полный пример.
В главе 7 "Коллекции" Вы видели как можно сохранять в коллекциях различные, но связанные объекты. К потокам так же применимы полиморфные свойства и они могут использоваться для сохранения целой коллекции на диске для восстановления в другое время или даже другой программы. Посмотрим еще раз TVGUID20.PAS. Что нужно сделать, чтобы эта программа вывела коллекцию в поток? Ответ удивительно прост. Во-первых, начнем с базового объекта TGraphObject и "научим" его как сохранять его данные (X и Y) в потоке. Это делает метод Store. Затем определим новый метод Store для каждого наследника TGraphObject, который добавляет новые поля (TGraphCircle добавляет Radius; TGraphRect добавляет Width и Height). Затем создадим регистрационную запись для каждого типа объекта, который будет сохраняться и зарегистрируем каждый из этих типов. Это все. Все остальное как при обычном вводе/выводе: объявите переменную потока; создайте новый поток; поместите всю коллекцию в поток одним оператором; закройте поток.Добавление методов Store.
Здесь приведены методы Store. Заметим, что PGraphPoint не требует его, поскольку не добавляет полей при наследовании от PGraphObject. type PGraphObject = ^TGraphObject; TGraphObject = object(TObject); . procedure Store(var S: TStream); virtual; end; PGraphCircle = ^TGraphCircle; TGraphCircle = object(TGraphObject) Raduis: Integer; . procedure Store(var S: TStream); virtual; end; PGraphRect = ^TGraphRect; TGraphRect = object(TGraphObject) Width, Height: Integer; . procedure Store(var S: TStream); virtual; end; Реализация Store совершенно проста. Каждый объект вызывает наследуемый метод Store, который сохраняет все наследуемые данные. Затем метод Write потока записывает дополнительные данные. { TGraphObject не вызывает TObject.Store, поскольку TObjeсt не имеет данных для сохранения } procedure TGraphObject.Store(var S: TStream); begin S.Write(X, SizeOf(X)); S.Write(Y, SizeOf(Y)); end; procedure TGraphCircle.Store(var S: TStream); begin TGraphObject.Store(S); S.Write(Radius, SizeOf(Radius)); end; procedure TGraphRect.Store(var S: TStream); begin TGraphObject.Store(S); S.Write(Width, SizeOf(Width)); S.Write(Height, SizeOf(Height)); end; Заметим, что метод Write из TStream выполняет двоичную запись. Его первый параметр может быть переменной любого типа, но TStream.Write не знает размера этой переменной. Второй параметр предоставляет эту информацию и Вы должны использовать стандартную функцию SizeOf. Таким образом, если Вы решите изменить координатную систему, используя числа с плавающей точкой, Вам не потребуется корректировать методы Store.Записи регистрации.
Определение константы записи регистрации для каждого из наследуемых типов, выполняется последним шагом. Хорошая мысль следовать соглашению Turbo Vision по именованию: используя R в качестве первой буквы. Примечание: Вспомним, что каждая запись регистрации получает уникальный номер идентификатора объекта (Objtype). Turbo Vision резервирует от 0 до 99 для стандартных объектов. Рекомендуем сохранить ID номера всех объектов в одном месте для избежания дублирования. const RGraphPoint: TStreamRec = ( ObjType: 150; VmtLink: Ofs(TypeOf(TGraphPoint)^); Load: nil; Store: @TGraphPoint.Store; RGraphCircle: TStreamRec = ( ObjType: 151; VmtLink: Ofs(TypeOf(TGraphCircle)^); Load: nil; Store: @TGraphCircle.Store; RGraphRect: TStreamRec = ( ObjType: 152; VmtLink: Ofs(TypeOf(TGraphRect)^); Load: nil; Store: @TGraphRect.Store; Вам не требуется регистрационная запись для TGraphObject, поскольку это абстрактный тип и никогда не имеет экземпляров и не помещается в коллекцию или поток. Каждый указатель Load записи регистрации устанавливается в nil, поскольку этот пример был создан только для сохранения данных в поток. Методы Load будут определены, а регистрационные записи скорректированы в следующем примере (TVGUID22.PAS).Регистрация.
Вы не должны забывать регистрировать каждую из этих записей до выполнения ввода/вывода в поток. Простейший способ сделать это - поместить их в одну процедуру и вызвать ее в начале Вашей программы (или в методе Init Вашей программы). procedure StreamRegistration; begin RegisterType(RCollection); RegisterType(RGraphPoint); RegisterType(RGraphCircle); RegisterType(RGraphRect); end; Заметим, что Вы зарегистрировали TCollection (используя его запись RCollection - теперь Вы видите почему соглашения об именовании упрощает программирование), хотя Вы не определяли TCollection. Это правило просто: Вы отвечаете за регистрацию всех типов объектов, которые выводятся в поток.Запись в поток.
Все, что осталось сделать - это обычная последовательность В/В в файл: создать поток; поместить данные (коллекцию) в него; закрыть поток. Вы не используете итератор ForEach для записи каждого элемента коллекции в поток. Вы просто говорите потоку поместить коллекцию в поток: { TVGUID21.PAS } var GraphicsList: PCollection; GraphicsStream: TBufStream; begin StreamRegistration; . GraphicsStream.Init('GRAPHICS.STM', stCreate, 1024); GraphicsStream.Put(GraphicsList); GraphicsStream.Done; . end. Будет создаваться дисковый файл, который содержит всю информацию, необходимую для чтения коллекции в память. Когда поток открывается и коллекция считывается (см. TVGUID22.PAS), все скрытые связи между коллекцией и ее элементами и объектами и их таблицами виртуальных методов будут восстановлены. Эта техника используется в IDE Turbo Pascal для сохранения его файла панели экрана. Следующий пример показывает Вам как сделать это. Но вначале Вы должны изучить объекты, выводимые в поток, которые содержат связи с другими объектами.Кто сохраняет?
Важное предупреждение о потоках: пользователь объекта - это тот объект, который должен записывать этот объект в поток. Это предупреждение подобно тому с которым Вы вероятно сталкивались при использовании традиционного Паскаля: владелец указателя - это тот, кто должен освободить этот указатель. В сложной реальной программе многочисленные объекты часто будут содержать указатель на какую-то структуру. Когда придет время для В/В в поток, Вы должны решить кто владелец структуры; только этот владелец должен посылать эту структуру в поток. Иначе Вы получите в потоке несколько копий одной структуры. Затем когда Вы будете читать поток будет создано несколько экземпляров структуры и каждый отдельный объект теперь будет указывать на собственную копию структуры вместо единственной первоначальной структуры.Экземпляры видимых подэлементов.
Вам будет удобно сохранять указатели на подэлементы группы в локальных экземплярах переменных. Например, диалоговое окно часто хранит указатели на объекты элементов управления в мнемонически названных полях для упрощения доступа (такие поля как OKButton или FileInputLine). Когда этот видимый элемент вставляется в дерево видимых элементов, владелец содержит 2 указателя на этот подэлемент, один в поле, а другой в списке подэлементов. Если Вы не разрешаете это, чтение объекта из потока приведет к дублированию экземпляров. Решение - в предоставлении методов GetSubViewPtr и PutSubViewPtr в TGroup. Когда сохраняется поле, которое является подэлементом, вместо того чтобы записать указатель, как это делается с другими переменными, Вы вызываете PutSubViewPtr, который сохраняет ссылку на позицию подэлемента в списке подэлементов группы. Таким образом когда Вы загружаете группу с помощью Load из потока, Вы можете вызвать GetSubViewPtr, который гарантирует, что это поле и список подэлементов указывают на один объект. Приведем короткий пример использования GetSubViewPtr и PutSubViewPtr в простом окне: type TButtonWindow = object(TWindow) Button: PButton; constructor Load(var S: TStream); procedure Store(var S: TStream); end; constructor Load(var S: TStream); begin TWindow.Load(S); GetSubViewPtr(S, Button); end; procedure Store(var S: TStream); begin TWindow.Store(S); PutSubViewPtr(S, Button); end; Давайте посмотрим чем этолт метод Store отличается от нормального Store. После сохранения окна Вы просто сохраняете ссылку на поле Button вместо сохранения самого поля. Сам объект кнопки сохраняется как подэлемент окна, когда Вы вызвали TWindow.Store. Все что Вы делаете дополнительно к помещению информации в поток это говорите, что Button указывает на этот подэлемент. Метод Load делает то же в обратном порядке, вначале загружая окно и подэлемент кнопки, а затем восстанавливая указатель на этот подэлемент в Button.Равные экземпляры видимого элемента.
Аналогичная ситуация возникает, когда видимый элемент имеет поле, указывающее на равный ему видимый элемент. Видимый элемент называется равным другому видимому элементу, если оба видимых элемента принадлежат одной группе. Хороший пример - скроллер. Поскольку скроллер знает о двух полосах скроллинга, являющихся элементами окна, которому принадлежит скроллер, он имеет два поля, которые указывают на эти видимые элементы. Как и с видимыми подэлементами, у Вас могут быть проблемы при чтении и записи ссылок на равные видимые элементы в поток. Решение также просто. Методы PutPeerViewPtr и GetPeerViewPtr предназначены для доступа к позиции другого видимого элемента в списке подобъектов владельца. Нужно заботится только о загрузке ссылок на равные видимые элементы, который еще не загружены (т.е. они стоят позже в списке подэлементов и следовательно позже в потоке). Turbo Vision обрабатывает это автоматически, сохраняя трассу всех таких ссылок вперед и разрешая их, когда все подэлементы группы будут загружены. Вам необходимо помнить, что ссылки на равные видимые элементы не действительны до тех пор, пока не будет завершен весь Load. Вследствие этого Вы не должны помещать в метод Load код, который использует подэлементы, зависящие от равных подэлементов, иначе результаты будут непредсказуемы.Сохранение и загрузка панели экрана.
Если Вы сохраняете панель экрана в потоке, панель экрана будет сохранять все свое содержимое: всю среду панели экрана, включая все текущие видимые элементы. Если Вы хотите разрешить пользователю сохранять панель экрана, Вам необходимо убедиться, что все возможные видимые элементы имеют соответствующие видимые элементы Store и Load, что все видимые элементы зарегистрированы, поскольку пользователь может сохранить панель экрана в любой момент. Чтобы сделать это Вы можете использовать подобный код: procedure TMyApp.RestoreDeskTop; var SaveFile: TBufStream; Temp: PDeskTop; begin SaveFile.Init('T.DSK', stOpen, 1024); Temp := PDeskTop(SaveFile.Get); SaveFile.Done; if Temp <> nil then begin Dispose(DeskTop, Done); DeskTop := Temp; Append(DeskTop); DeskTop^.DrawView; end; if SaveFile.Status <> 0 then ErrorReadingFile; end; Вы можете сделать следующий шаг и сохранять и восстанавливать всю программу. Объект TApplication может сам сохранять и восстанавливать себя.Копирование потоков.
TStream имеет метод CopyFrom(S, Count), который копирует Count байт из потока S. CopyFrom может использоваться для копирования всего содержимого потока в другой поток. Если Вы постоянно обращаетесь к дисковым потокам, Вы можете скопировать их в EMS поток для более быстрого доступа: NewStream := New(TEmsStream, Init(OldStream^.GetSize)); OldStream^.Seek(0); NewStream^.CopyFrom(OldStream, OldStream^.GetSize);Прямой доступ к потокам.
До сих пор мы использовали потоки как последовательные устройства: Вы выводили объекты в конец потока и считывали их обратно в том же порядке. Turbo Vision предоставляет Вам дополнительные возможности. Он позволяет Вам интерпретировать поток как виртуальное устройство с прямым доступом. В дополнение к Get и Put, которые соответствуют Read и Write для файла, потоки предоставляют возможности аналогичные файловым Seek, FilePos, FileSize и Truncate. - Процедура Seek передвигает указатель текущего потока на заданную позицию (в байтах лот начала потока) как стандартная процедура Seek Turbo Pascal. - Функция GetPos обратна процедуре Seek. Она возвращает LongInt с текущей позицией в потоке. - Функция GetSize возвращает размер потока в байтах. - Процедура Truncate удаляет все данные после текущей позиции потока, делая текущую позицию последней в потоке. Чтобы можно было использовать эти программы, прямой доступ к потоку требует создания вне потока индекса, содержащего начальные позиции каждого объекта в потоке. Коллекция идеальна для этой цели и в действительности используется в Turbo Vision с файлами ресурсов (ресурсы обсуждаются в главе 9). Если Вы хотите использовать прямой доступ к потоку, Вы можете использовать файл ресурса.Не-объекты в потоке.
Вы можете писать в поток данные, которые не являются объектами, но Вы должны использовать при этом другой подход. Стандартные методы Get и Put требуют, чтобы Вы сохраняли или загружали объект, порожденный от TObject. Если Вы хотите создать поток не-объектов, перейдите прямо на низкоуровневые процедуры Read и Write, каждая из которых читает или пишет заданное число байт в поток. Этот же механизм используется в Get и Put для чтения и записи данных объектов; Вы просто обходите механизм VMT, используемый в Get и Put.Проектирование Ваших потоков.
Этот раздел суммирует методы и возможности обработки ошибок потоков Turbo Vision так, чтобы Вы знали что Вы можете использовать при создании новых типов потоков. TStream - это абстрактный объект, который должен быть расширен, чтобы создать используемый тип потока. Большинство методов TStream абстрактные и должны быть реализованы в Ваших наследниках, а другие зависят от методов TStream. Только методы Error, Get и Put полностью реализованы в TStream. GetPos, GetSize, Read, Seek, SetPos, Truncate и Write должны быть перекрыты. Если тип порожденного объекта имеет буфер, так же должен быть перекрыт метод Flush.Обработка ошибок потока.
TStream имеет метод Error(Code, Info), который вызывается, когда поток обнаруживает ошибку. Error просто устанавливает поле Status в одну из констант, приведенную в разделе "Константы stXXXX" главы 14. Поле ErrorInfo за исключением ситуации, когда Status равен stGetError или stPutError, неопределено. Если Status - stGetError, поле ErrorInfo содержит номер ID незарегистрированного типа в потоке. Если Status - stPutError, поле ErrorInfo содержит смещение VMT для типа, который Вы пытаетесь поместить в поток. Вы можете перекрыть TStream.Error для создания любой обработки ошибок, включая ошибки времени выполнения.
Назад | Содержание | Вперед