Содержание
- Ewriteerror stream write error
- Stream write error — System::Classes::TStream::WriteBuffer
- Stream write error — System::Classes::TStream::WriteBuffer
- Re: Stream write error — System::Classes::TStream::WriteBuffer
- Stream write error — System::Classes::TStream::WriteBuffer
- Stream write error — System::Classes::TStream::WriteBuffer
- Re: Stream write error — System::Classes::TStream::WriteBuffer
- Stream write error — System::Classes::TStream::WriteBuffer
- Stream write error — System::Classes::TStream::WriteBuffer
- Stream write error — System::Classes::TStream::WriteBuffer
- Clue of solution cause onedrive sync
- Re: Stream write error — System::Classes::TStream::WriteBuffer
- Fund why erreur *.ini files
- Re: Fund why erreur *.ini files
- Stream write error — System::Classes::TStream::WriteBuffer
- Блог GunSmoker-а
- 12 ноября 2011 г.
- Сериализация — потоки данных
- Оглавление
- Общие сведения
- Общие принципы работы с потоками данных
- Обработка ошибок
- Правила использования
- Практика
- Особенности
- THandleStream
- TFileStream
- TMemoryStream
- TResourceStream
- TBytesStream
- TStringStream
- TStreamAdapter
- TOleStream
- Прочие потоки
- Создание своих классов-потоков
- Преимущества и недостатки потоков данных
Ewriteerror stream write error
Additional information:
[Context]
App::Lang=en_GB
Main::ExploreArchiveNode::WorkItem=D:SteamLibrarysteamappscommonGrand Theft Auto Vmodsupdatex64dlcpackstankstellemazebankdlc.rpf|object.rpf|
GetSystemDefaultLangID=1031
SysLocale=< DefaultLCID: «1031», PriLangID: «7», SubLangID: «1», FarEast: «True», MiddleEast: «True» >
App::Path=C:UserstobipAppDataLocalNew Technology StudioAppsOpenIV
GetThreadLocale=1031 (0x0407)
Game::Platform=pc
Main::ExploreArchiveNode::ParentArchive=D:SteamLibrarysteamappscommonGrand Theft Auto Vmodsupdatex64dlcpackstankstellemazebankdlc.rpf|object.rpf
WebClientIndex=1969736853
Core::OpenArchive::LastCall=D:SteamLibrarysteamappscommonGrand Theft Auto Vmodsupdatex64dlcpackstankstellemazebankdlc.rpf|object.rpf
ApplicationPath=C:UserstobipAppDataLocalNew Technology StudioAppsOpenIV
OS=Windows 10 (Version 10.0, Build 0, 64-bit Edition)
RunCurrentItemAction=D:SteamLibrarysteamappscommonGrand Theft Auto Vmodsupdatex64dlcpackstankstellemazebankdlc.rpf|object.rpf
WebClientToken=
Game::ID=Five (GTA V)
GetSystemDefaultLCID=1031
WebClientId=45814812
Action::Open::Execute=D:SteamLibrarysteamappscommonGrand Theft Auto Vmodsupdatex64dlcpackstankstellemazebankdlc.rpf|object.rpf|_manifest.ymf|_manifest.ymf|_manifest.ymf
App::Temp=C:UserstobipAppDataLocalTempOpenIV_000A7FFD
Game::Path=D:SteamLibrarysteamappscommonGrand Theft Auto V
[Windows]
TMainWindow=OpenIV — object.rpf — [Read only mode]
TActionsModule=ActionsModule
TErrorWindow=OpenIV — Application error
[Exeption]
Exe: 4.0.0.1401 05.03.2020
Address: 0x004B605C
ClassName: EWriteError
MessageText: Stream write error
Источник
Stream write error — System::Classes::TStream::WriteBuffer
Stream write error — System::Classes::TStream::WriteBuffer
Error message:
Stream write error
Stack trace:
(0035E6DF) System::Classes::TStream::WriteBuffer
(0035E663) System::Classes::TStream::WriteBuffer
(003596AC) System::Classes::TStrings::SaveToStream
(006BA990) TIniFileStorage::
TIniFileStorage
(007265DF) TStoredSessionList::DoSave
(00726614) TStoredSessionList::Save
(00024EA3) TCustomScpExplorerForm::FormClose
(00208C7F) Vcl::Forms::TCustomForm::DoClose
(0024F2A9) Vcl::Controls::TWinControl::WndProc
(00209758) Vcl::Forms::TCustomForm::WndProc
(0080C7F8) Vclcommon::C2754_0
(0024E8F0) Vcl::Controls::TWinControl::MainWndProc
(0037326C) System::Classes::_18201
(00032429) USER32.dll
(0002488E) USER32.dll
(000244E5) USER32.dll
(0002E4B7) USER32.dll
(0007311B) ntdll.dll.KiUserCallbackDispatcher
(00025226) USER32.dll
(000220F1) UXTHEME.DLL
(00021EC0) UXTHEME.DLL
(000361C1) UXTHEME.DLL
(00034ED3) UXTHEME.DLL
(000255F9) USER32.dll
(00032429) USER32.dll
(0002488E) USER32.dll
(0001A312) USER32.dll.CallWindowProcW
(0024F3BA) Vcl::Controls::TWinControl::DefaultHandler
(0020CB3D) Vcl::Forms::TCustomForm::WMSysCommand
(0024F2A9) Vcl::Controls::TWinControl::WndProc
(00209758) Vcl::Forms::TCustomForm::WndProc
(0080C7F8) Vclcommon::C2754_0
(0024E8F0) Vcl::Controls::TWinControl::MainWndProc
(0037326C) System::Classes::_18201
(00032429) USER32.dll
(0002488E) USER32.dll
(000244E5) USER32.dll
(0002E4B7) USER32.dll
(0007311B) ntdll.dll.KiUs
I know how to reproduce the problem or the problem happens frequently enough. I wish to be contacted by the WinSCP team to help resolving the problem.
Re: Stream write error — System::Classes::TStream::WriteBuffer
Stream write error — System::Classes::TStream::WriteBuffer
Error message:
Stream write error
Trace de la pile :
(0038EF63) System::Classes::TStream::WriteBuffer
(0038EEE7) System::Classes::TStream::WriteBuffer
(00389F30) System::Classes::TStrings::SaveToStream
(0073BB8C) TIniFileStorage::
TIniFileStorage
(006F29C1) TConfiguration::DoSave
(006F28A7) TConfiguration::Save
(000026DD) wWinMain
(009250D1) __wstartup
(00038800) ntdll.dll
(000387D0) ntdll.dll
The problem happens rarely. I cannot reproduce it.
Stream write error — System::Classes::TStream::WriteBuffer
Error message:
Stream write error
Trace de la pile :
(0038EF63) System::Classes::TStream::WriteBuffer
(0038EEE7) System::Classes::TStream::WriteBuffer
(00389F30) System::Classes::TStrings::SaveToStream
(0073BB8C) TIniFileStorage::
TIniFileStorage
(006F29C1) TConfiguration::DoSave
(006F28A7) TConfiguration::Save
(000026DD) wWinMain
(009250D1) __wstartup
(00038800) ntdll.dll
(000387D0) ntdll.dll
I know how to reproduce the problem or the problem happens frequently enough. I wish to be contacted by the WinSCP team to help resolving the problem.
Re: Stream write error — System::Classes::TStream::WriteBuffer
Stream write error — System::Classes::TStream::WriteBuffer
Error message:
Stream write error
Stack trace:
(0038EF63) System::Classes::TStream::WriteBuffer
(0038EEE7) System::Classes::TStream::WriteBuffer
(00389F30) System::Classes::TStrings::SaveToStream
(0073BB8C) TIniFileStorage::
TIniFileStorage
(007C6BE7) TStoredSessionList::DoSave
(007C6C1C) TStoredSessionList::Save
(000298C9) TCustomScpExplorerForm::FormClose
(00232933) Vcl::Forms::TCustomForm::DoClose
(00278F45) Vcl::Controls::TWinControl::WndProc
(0023340C) Vcl::Forms::TCustomForm::WndProc
The problem happens rarely. I cannot reproduce it.
Stream write error — System::Classes::TStream::WriteBuffer
Error message:
Stream write error
Rastreamento de pilha:
(0036C50F) System::Classes::TStream::WriteBuffer
(0036C493) System::Classes::TStream::WriteBuffer
(003674DC) System::Classes::TStrings::SaveToStream
(006E1F0C) TIniFileStorage::
TIniFileStorage
(006A406D) TConfiguration::MoveStorage
(00839119) TPreferencesDialog::SaveConfiguration
(00835960) TPreferencesDialog::Execute
(00834AE5) DoPreferencesDialog
(00822ADE) TLoginDialog::ShowPreferencesDialog
(00822A86) TLoginDialog::PreferencesActionExecute
(0038109C) System::Classes::_18201
(000438E9) USER32.dll
(00025137) USER32.dll
(00024289) USER32.dll
(0002406B) USER32.dll.DispatchMessageW
(008C0005) C5105_3
(008C15DF) _ReThrowException
(006A40B4) TConfiguration::MoveStorage
(00839119) TPreferencesDialog::SaveConfiguration
(00835960) TPreferencesDialog::Execute
(00834AE5) DoPreferencesDialog
(00822ADE) TLoginDialog::ShowPreferencesDialog
(00822A86) TLoginDialog::PreferencesActionExecute
(0038109C) System::Classes::_18201
(000438E9) USER32.dll
(00025137) USER32.dll
(00024289) USER32.dll
(0002406B) USER32.dll.DispatchMessageW
The problem happens rarely. I cannot reproduce it.
Stream write error — System::Classes::TStream::WriteBuffer
Error message:
Stream write error
Trace de la pile :
(0038F1BB) System::Classes::TStream::WriteBuffer
(0038F13F) System::Classes::TStream::WriteBuffer
(0038A188) System::Classes::TStrings::SaveToStream
(0073C080) TIniFileStorage::
TIniFileStorage
(000DEABF) TWinConfiguration::UpdateEntryInJumpList
(000DEB33) TWinConfiguration::AddSessionToJumpList
(000AB4AF) TTerminalManager::ConnectActiveTerminal
(000ABD4C) TTerminalManager::DoSetActiveTerminal
(000ABAD6) TTerminalManager::SetActiveTerminal
(00018C95) TCustomScpExplorerForm::NewSession
(00020248) TCustomScpExplorerForm::SessionTabSwitched
(0002029C) TCustomScpExplorerForm::SessionsPageControlChange
(0027919D) Vcl::Controls::TWinControl::WndProc
(00274294) Vcl::Controls::TControl::Perform
(00279303) Vcl::Controls::_17076
(0027919D) Vcl::Controls::TWinControl::WndProc
(00233664) Vcl::Forms::TCustomForm::WndProc
somtimes after connexion choice winscp application close automaticly it’s impossible see text in Windows.
i suppose it’s linked at last version Windows 10 updated.
I know how to reproduce the problem or the problem happens frequently enough. I wish to be contacted by the WinSCP team to help resolving the problem.
pa00trunks Joined: 2019-09-11 Posts: 2 Location: france
Clue of solution cause onedrive sync
My liste of connexion is in .ini file localized in personnal folder sync with onedrive.
when i put this file out of ondrive sync connection working well.
so I suppose than fils was loock by ondrive.
système info: win10 release 1903 up to date.
I have see same probleme quickly in winscp forum about dropbox.
Re: Stream write error — System::Classes::TStream::WriteBuffer
Error message:
Stream write error
Trace de la pile :
(0038F1BB) System::Classes::TStream::WriteBuffer
(0038F13F) System::Classes::TStream::WriteBuffer
(0038A188) System::Classes::TStrings::SaveToStream
(0073C080) TIniFileStorage::
TIniFileStorage
(000DEABF) TWinConfiguration::UpdateEntryInJumpList
(000DEB33) TWinConfiguration::AddSessionToJumpList
(000AB4AF) TTerminalManager::ConnectActiveTerminal
(000ABD4C) TTerminalManager::DoSetActiveTerminal
(000ABAD6) TTerminalManager::SetActiveTerminal
(00018C95) TCustomScpExplorerForm::NewSession
.
somtimes after connexion choice winscp application close automaticly it’s impossible see text in Windows.
i suppose it’s linked at last version Windows 10 updated.
I know how to reproduce the problem or the problem happens frequently enough. I wish to be contacted by the WinSCP team to help resolving the problem.
pa00trunks Joined: 2019-09-11 Posts: 2 Location: france
Fund why erreur *.ini files
I have found why INI files with personal config saved in my documents not work.
In question Windows 10 Ransomware Protection !! will be activated in my professional computer.
This option is activated by my company by error last week.
So disable this option or parameter well WinScp and putty allow app.
Resolved for me.
martin◆
Site Admin Joined: 2002-12-10 Posts: 38,352 Location: Prague, Czechia
Re: Fund why erreur *.ini files
I have found why INI files with personal config saved in my documents not work.
In question Windows 10 Ransomware Protection !! will be activated in my professional computer.
This option is activated by my company by error last week.
So disable this option or parameter well WinScp and putty allow app.
Resolved for me.
Stream write error — System::Classes::TStream::WriteBuffer
Error message:
Stream write error
Stack trace:
(00390BEC) System::Classes::TStream::WriteBuffer
(00DD9F70) TEditorForm::SaveToFile
(00DD9FCC) TEditorForm::SaveFile
(001C7ED1) Vcl::Actnlist::TCustomAction::Execute
(002394FD) Vcl::Forms::TCustomForm::CloseQuery
(0027AC61) Vcl::Controls::TWinControl::WndProc
(00235128) Vcl::Forms::TCustomForm::WndProc
I know how to reproduce the problem or the problem happens frequently enough. I wish to be contacted by the WinSCP team to help resolving the problem.
Источник
Блог GunSmoker-а
. when altering one’s mind becomes as easy as programming a computer, what does it mean to be human.
12 ноября 2011 г.
Сериализация — потоки данных
Оглавление
Общие сведения
Если вы вспомните определение файла, то оно звучало так: «файл — это устройство с последовательным доступом, к которому можно обратиться по имени». При этом физические файлы на диске являются лишь частным случаем. Отнимите из этого определения часть «имеющие имя» — и вы получите определение потока данных. Поток данных представляет собой «что-то» с последовательным доступом. Вы можете читать из него данные или записывать. Некоторые потоки поддерживают позиционирование (вроде физического файла на диске), другие — нет (вроде сетевого сокета).
Потоки данных являются де-факто стандартом для обмена данными в Delphi. Всюду в своих процедурах, где вам необходимо принимать или отправлять нетипизированный набор данных, используйте потоки данных. Многие механизмы Delphi по умолчанию умеют работать именно с потоками данных, предоставляя методы вроде LoadFromStream и SaveToStream (и иногда предоставляя к ним обёртки-переходники вроде LoadFromFile и SaveToFile ).
Примечание: поток данных (stream) не следует путать с потоком кода (thread), который также иногда называют нитью. Они не имеют между собой ничего общего, кроме слова «поток» в названии. Если какой-то текст говорит про «потоки», не уточняя кода или данных, то значение термина должно быть ясно из контекста. Эти понятия не пересекаются, так что здесь не должно быть никаких проблем с пониманием. Замечу, что подобная путаница возможна только в русском языке, где два разных понятия переводятся одинаково. В английском языке для них используются разные слова (stream и thread).
В Delphi все потоки данных реализованы как объекты (экземпляры) классов, наследуемых от TStream . TStream является абстрактным базовым классом, который поддерживает операции чтения, записи и позиционирования, но сам при этом не умеет делать ничего. Конкретная работа реализуется его классами-наследниками.
В Delphi есть широкий набор классов, предназначенный для работы с чем угодно: от файла на диске до блока памяти. Каждый такой класс-наследник реализует базовые методы TStream по-своему. К примеру, при чтении из потока данных: наследник для работы с файлами вызовет функцию чтения данных с диска, а наследник для работы с блоком памяти использует процедуру Move для копирования данных.
Вот (неполный) список классов-наследников TStream (примеры):
- TFileStream (для работы с файлами)
- TResourceStream (для работы с ресурсами программы)
- TStringStream (для работы со строками)
- TMemoryStream (для работы с буфером в памяти)
- TBlobStream (для работы с BLOB полями)
- TWinSocketStream (для работы с сетевым сокетом)
По сравнению с прошлой темой, где было всего три вполне конкретных файловых типа, это может быть немного непонятно: зачем нужен какой-то абстрактный класс и классы-наследники? Очень просто: пусть вы хотите уметь загружать растровые изображения (bitmap). Но ведь рисунок может лежать не только в файле, он может быть и в ресурсах программы и в памяти. Не писать же три разных метода, которые делают одно и то же? Вот поэтому и нужен абстрактный класс. Он объявляет общий контракт, которому обязуются следовать все его наследники. Поэтому вы можете спокойно написать (один раз) код, который грузит рисунок из TStream . А уж вызывающий подставит вам TFileStream для загрузки рисунка из файла, TResourceStream для загрузки из ресурса и TMemoryStream для загрузки из памяти. В определённом смысле все эти классы-наследники представляют собой простые переходники (от общей спецификации, определённой TStream , до конкретного метода доступа: файл, ресурс, память, сеть и так далее; и наоборот). В общем, полиморфизм в действии.
Здесь и ниже я буду говорить в основном про физические файлы и примеры приводить в расчёте на TFileStream — как наиболее типичный случай. Просто имейте в виду, что всё сказанное будет применимо и к другим потокам данных.
Кроме того, потоки обеспечивают поддержку для загрузки/сохранения компонентов и форм. Именно благодаря этому механизму работает загрузка .dfm файлов в run-time. Этот механизм работает автоматически. Впрочем, вы можете использовать его и в своих целях. Но на это мы посмотрим в другой раз, потому что он тесно связан с RTTI. Это будет темой одной из следующих статей.
Общие принципы работы с потоками данных
Чтение/запись
Класс TStream имеет два метода для чтения данных и два метода для записи данных. Работают они обычным образом: поток читает (или пишет) указанный блок данных и сдвигает текущую позицию на размер блока данных, так что следующая операция начинается там, где закончилась предыдущая. Ничего сложного.
Итак, для чтения класс TStream предлагает методы Read и ReadBuffer , а для записи — методы Write и WriteBuffer . Эти методы используются одинаково: первым параметром указывается буфер (это нетипизированный параметр), а вторым параметром — его размер в байтах, например: Разница между методами с «Buffer» и без него заключается в том, что методы с суффиксом «Buffer» гарантируют выполнение операции до конца. Если же это невозможно (к примеру, в файле 12 байт, а вы командуете прочитать 24 байта), то будет возбуждено исключение (см. также ниже раздел про обработку ошибок).
А вот методы без суффикса «Buffer» допускают частичное выполнение операции. Они никогда не возбуждают ошибку, а вместо этого возвращают, сколько реально байт было прочитано или записано. Иногда, это может быть и 0 байт. К примеру, если в файле 12 байт, а вы вызываете Read , указывая 24 байта, то метод Read прочитает 12 байт и вернёт вам число 12 (это метод-функция). Ещё пример: если у вас поток связан с сетевым сокетом и вы вызываете Read , но пока никаких данных ещё не пришло: метод завершится тут же, возвращая 0.
Замечу, что некоторые потоки данных могут быть только для чтения или только для записи. К примеру, однонаправленный pipe может не допускать чтения, а ресурсы, очевидно, не разрешают запись. В таких случаях попытка вызова запрещённого метода приведёт к ошибке.
Помимо общих методов чтения/записи, потоки поддерживают специализированные методы для реализации чтения/записи компонентов — но, как я уже сказал, это тема для следующего раза.
Разумеется, некоторые из наследников TStream могут вводить свои специальные методы для чтения/записи. Мы посмотрим на некоторые примеры ниже.
Кроме чтения и записи TStream имеет и некоторые другие методы.
Позиционирование
Во-первых, это методы позиционирования. Вы можете вызвать свойство Size , которое вернёт вам размер потока данных в байтах (кроме того, вы можете устанавливать свойство Size , чтобы менять размер потока, но это поддерживается далеко не всеми видами потоков данных). Свойство Position указывает текущую позицию в потоке данных, где 0 соответствует началу, а значение, равное Size , — концу. Вы можете читать свойство Position , чтобы узнать текущую позицию, и записывать значение в Position , чтобы изменить текущую позицию (позиционирование поддерживается не всеми видами потоков). К примеру: Кроме абсолютного позиционирования через свойство Position , потоки данных поддерживают относительное позиционирование в стиле файловых Seek-процедур: метод Seek . Этот метод имеет два параметра: позицию и точку отсчёта. Причём последнее может иметь значения soBeginning (отсчёт от начала потока, аналог абсолютного позиционирования), soCurrent (отсчёт от текущей позиции, относительное смещение) и soEnd (отсчёт от конца потока). Для soCurrent позиция может быть и положительным числом (смещение в сторону конца потока) и отрицательным (смещение в сторону начала), для soBeginning смещение может быть только положительным (или нулём), а для soEnd — только отрицательным (или нулём). Есть также устаревшие константы вида soFromBeginning , soFromCurrent , soFromEnd — не используйте их в новом коде. При этом метод Seek возвращает предыдущее значение текущей позиции (до позиционирования). К примеру:
Копирование
У TStream есть ещё один метод: CopyFrom . Этот метод копирует указанное количество данных (в байтах) из указанного массива. Копирование производится с текущей позиции. Метод работает аналогично методу записи WriteBuffer , сдвигая текущую позицию на указанное количество байт и возбуждая исключение при ошибках. Использование CopyFrom позволяет избежать создания буфера, чтения в него данных из исходного потока, запись буфера в выходной поток и удаления буфера — все эти действия выполняются автоматически внутри метода CopyFrom . К примеру, вот простейший пример копирования файлов (см. ниже описание TFileStream ): У CopyFrom есть специальный случай: если последний параметр (размер) равен 0, то CopyFrom скопирует весь поток целиком — начиная с начала потока (вне зависимости от текущей позиции) и до конца. Так что если число байт для записи у вас не фиксировано, а как-то вычисляется, то вставьте перед вызовом CopyFrom проверку на 0: иначе вы скопируете поток целиком вместо 0 байт.
Обработка ошибок
Здесь нет ничего сложного, потоки данных используют стандартную обработку исключений. Вы можете просто писать код, не производя проверок — он по умолчанию будет иметь обработку ошибок. Конечно же, вам нужно использовать try..finally для корректной обработки утечек ресурсов программы: Во втором примере try..finally не используется при работе с потоком, потому что он вынесен во внешний код (вызывающий).
Все исключения, возбуждаемые самим потоком, наследуются от EStreamError . Наиболее типичными случаями являются ошибки EFileStreamError , EFCreateError , EFOpenError , EReadError и EWriteError .
Так что код обработки исключений потоков данных (если он вам нужен) может выглядеть как-то так:
Всё вышеуказанное — это типичное поведение для работы с исключениями. Тут не должно быть ничего нового и нетипичного.
Так что единственное «ага!» в обработке ошибок — разница между Read и ReadBuffer и между Write и WriteBuffer . Запомните, что если вы вызываете методы без суффикса «Buffer», то вы должны либо явно проверять результат их вызова (прямо как с кодом на кодах ошибок), либо иметь в виду, что данные могут прочитаться/записаться не полностью.
Правила использования
Хотелось бы сказать о «правилах использования» — а, скорее, о наиболее типичных ошибках новичков при работе с потоками данных.
- (Почти) всегда используйте ReadBuffer и WriteBuffer . Используйте Read и Write только если вам не важно выполнение операции до конца (к примеру, вы не знаете, сколько данных нужно прочитать). Иными словами, ReadBuffer и WriteBuffer должны быть вашим вариантом по умолчанию, а не наоборот.
Не забывайте, что потоки работают с большими данными, поэтому всё позиционирование осуществляется на базе Int64 (8 байт/64 бита), а не Integer (4 байта/32 бита). Поэтому если вы по недосмотру где-то используете для позиционирования выражение/переменную типа Integer , то этим вы автоматически ограничите свои данные 2 Гб. Но этот момент также зависит и от вида используемого потока. К примеру, в 32-битных приложениях TMemoryStream не может работать с памятью больше 4 Гб, а TResourceStream ограничен 2 Гб — потому что он работает с ресурсами в исполняемых файлах формата PE. А любой файл формата PE не может быть больше 2 Гб.
Практика
Все примеры ниже приводятся с полной обработкой ошибок. Примеры используют файл в папке с программой. Это удобно для тестирования и экспериментов, но, как указано ранее, этого нужно избегать в реальных программах. После тестирования и перед использованием кода в релизе вы должны заменить папку программы на подпапку в Application Data.
Как вы сейчас увидите, использование базовых методов потоков данных эквивалентно использованию нетипизированных файлов со всеми их преимуществами и недостатками. Впрочем, благодаря классам и наследованию, классы-наследники могут предоставлять другие методы и более удобный доступ для частных случаев — подробнее на это мы посмотрим ниже, а также в следующих статьях цикла. Также мы увидим как ООП предоставляет нам возможность использования некоторых интересных ходов для записи сложных динамических структур данных.
- Одно значение: Double
Одно значение переменного размера: String Данный случай прост — размер данных определяется по размеру файла. Для примера я выбрал AnsiString , а не String по двум причинам: во-первых, String — это псевдоним либо на AnsiString , либо на UnicodeString (в зависимости от версии Delphi). Так что вам нужно использовать явные типы: AnsiString , WideString (или UnicodeString ), а не String — иначе файл, созданный в одном варианте программы, нельзя будет прочитать в другом варианте программы.
Во-вторых, используя AnsiString , я показал, как вы можете загрузить в строку весь файл целиком, «как есть». Хотя, если подобный подход использовать в реальных программах, то уж лучше использовать array of Byte или хотя бы RawByteString — чтобы подчеркнуть двоичность данных.
Набор однородных значений: array of Double И снова, благодаря фиксированности размеров элементов, мы можем установить размер массива ещё до чтения из файла. Обратите внимание, что мы могли бы свести этот пример к предыдущему, прочитав/записав весь массив за раз, вместо поэлементного копирования.
Также обратите внимание, что не имеет значения, какой индекс используется внутри выражения у SizeOf . Более того, не требуется даже наличие (существование) этого элемента. Это потому, что мы не обращаемся к нему — мы только просим у компилятора его размер. Это, по сути, константа. Так что всё выражение вообще не вычисляется — оно просто заменяется числом. Это удобный трюк для написания подобного кода, потому что это удобнее, чем писать тип явно: SizeOf(Double) . Почему? А что, если мы изменим объявление типа с Double на Single ? И забудем обновить SizeOf ? Тогда это приведёт к порче памяти — т.к. писаться или читаться будет больше, чем реально есть байт в элементе. Это выглядит не очень страшно для массива из Double , но рассмотрите вариант, скажем, строки — изменение размера Char гораздо более вероятно. А вот если мы используем форму SizeOf как в примере, то такой проблемы не будет — размер изменится автоматически.
Набор однородных значений переменного размера: array of String С записью набора динамических данных возникает проблема — как отличить один элемент от другого? Мы не можем более использовать переход на другую строку, как это было с текстовыми файлами. Тут есть несколько вариантов.
Самый простой и очевидный — ввести разделитель данных. Т.е. элементы отделяются друг от друга специальным символом. В качестве такого чаще всего выступает нулевой символ (#0) — это аналог разделителя строк в текстовых файлах. Тогда чтение-запись сведётся к примеру два. Но я не стал показывать этот путь, т.к. он очевидно вводит ограничение на возможные данные: теперь данные не могут содержать в себе разделитель (каким бы вы его ни выбрали). Конечно, вы можете его экранировать, но гораздо проще будет выбор другого подхода.
И я его показал — это явная запись размера данных до записи самих данных. Т.е. мы пишем два значения для каждого элемента: длину и сами данные.
Кроме того, в этом же примере показано, как можно сделать так, чтобы внутри программы работать с хорошо знакомым String , а в файле хранить фиксированный тип ( AnsiString / RawByteString или WideString / UnicodeString ). Вообще говоря, даже если вы работаете на Delphi 7 или любой другой версии Delphi до 2007 включительно — я бы рекомендовал всегда писать Unicode-данные в формате WideString во внешние хранилища.
Обратите внимание, что в качестве счётчика длины используется LongInt , а не Integer — по причинам, указанным выше для типизированных файлов: String , Extended , Integer и Cardinal могут менять свои размеры в зависимости от окружения — поэтому мы используем другие типы, которые гарантированно всегда имеют один и тот же размер.
Ещё в этом примере показан вариант пример использования метода Read : идея в том, что если будет достигнут конец потока, то вызов метода Read вернёт 0. Т.е. это аналог функции EoF. Альтернативным решением является код Вы также можете создать вспомогательную функцию И тогда этот пример будет эквивалентен примеру для нетипизированных файлов Pascal. Я буду использовать подпрограмму EoS в примерах ниже.
Запись — набор неоднородных данных: В отличие от типизированных файлов, для нетипизированных файлов нет никаких проблем с записью неоднородных данных — вы просто пишете одно за другим. Данные фиксированного размера самодокументируются, а для динамических данных вы пишете сначала их размер, а затем сами данные. При чтении повторяете всё это в обратном порядке.
Кстати, я бы вынес запись динамических данных в отдельные служебные подпрограммы: Тогда чтение-запись свелись бы к: Выглядит существенно проще и красивее, не так ли? Иллюстрация силы выделения кода в подпрограммы.
Вообще, конечно же, более правильный код получится при использовании шаблона «декоратор». Суть заключается в создании класса, который реализует методы вида WriteBufferDyn / ReadBufferDyn , выполняя их над потоком, который ему указали. Например: В этом примере реализовано 3 вещи: во-первых, мы реализовали хранение ссылки на поток данных, над которым будем выполнять операции. Во-вторых, мы реализовали несколько вспомогательных низкоуровневых операций в секции private . В-третьих, мы реализовали высокоуровневые операции Read и Write в секции public . Тогда сохранение и загрузка сводятся к такому простому коду: Ну не красота-ли?
Имейте в виду, что именно этот подход вам нужно применять в реальных программах. В примерах ниже я буду применять процедуры вроде WriteBufferDyn / ReadBufferDyn исключительно по соображениям «меньше писать».
Набор (массив) из записей — иерархический набор данных: Для начала хочу сразу же заметить, что странное выражение для поля Salary сделано для обхода бага Delphi. Вообще, там должно стоять просто F.WriteBuffer(Values[Index].Salary, SizeOf(Values[Index].Salary)) , но в настоящий момент это выражение даёт ошибку «Variable required», поэтому используется обходной путь: мы берём указатель и разыменовываем его. Вообще говоря, это NOP-операция. А смысл её заключается в потере информации о типе. Это достаточно частый трюк, когда мы хотим запустить свои шаловливые руки под капот языка, минуя информацию типа, но в данном случае он используется для более благих целей: обхода бага компилятора. Вы можете использовать F.WriteBuffer(Values[Index].Salary, SizeOf(Values[Index].Salary)) , если ваша версия компилятора это позволяет, или просто выбрать другой тип данных (не Currency ).
В любом случае, надо заметить, что достаточно часто при записи/чтении массива записей новички пытаются сделать такую вещь, как запись элемента целиком ( F.WriteBuffer(Values[Index], SizeOf(Values[Index])) ). Это будет работать для записей фиксированного размера, не содержащих динамические данные (указатели). Ровно как это работает для типизированных файлов. Но если в записях у вас встречаются строки, динамические массивы и другие данные-указатели, то этот подход не будет работать. Собственно, если вы используете типизированные файлы, то компилятор даже не даст вам объявить такой тип данных ( file of String , например, или file of Запись , где Запись содержит String ). Но суть потоков данных — в прямом доступе, минуя информацию типа. Так что по рукам за это вам никто не даст. Вместо этого код будет просто вылетать или давать неверные результаты. А проблема тут в том, что для динамических данных, поле — это просто указатель. Записывая элемент «как есть» вы запишете в файл значение указателя, но не данные, на которые он указывает. Запись в файл произойдёт нормально, но в файле вы не найдёте своих строк. Чтение из файла тоже пройдёт отлично. Но как только вы попробуете обратиться к прочитанной строке — код вылетит с access violation, потому что указатель строки указывает в космос, на мусор.
Аналогично предыдущим обсуждениям, самый простой способ решения проблемы (но не всегда достаточный) — замена String в записях на ShortString или статический массив символов. Я не буду рассматривать этот вариант, т.к. он сводится к предыдущим примерам с записью данных фиксированного размера.
Вместо этого в примере я показал уже известную технику: запись длины строки вместе с её данными. Это избавляет вас от всех недостатков ShortString /массива символов, но даёт новый недостаток: теперь вы не можете сохранить данные одной строчкой, вам нужно писать их поле-за-полем.
Также по аналогии с предыдущим примером я покажу, как будет выглядеть код, если вы введёте класс-декоратор. Само описание класса я опущу для краткости — оно аналогично (но не тождественно) предыдущему примеру: Мы также могли внести методы Read и Write , работающие с массивом из TPerson в класс-декоратор.
Массив из записей внутри записи — составные данные: Как видите — здесь нет никаких проблем, вы просто соединяете воедино техники из предыдущих примеров. Мы используем технику с записью счётчика длины для динамических данных в двух местах: при записи строк и при записи массивов (поле Related ).
Кроме того, хотя я мог бы написать весь код в цикле, друг за другом, я всё же выделил новые подпрограммы — исключительно ради удобства. Код теперь выглядит компактно и аккуратно. Он прозрачен и его легко проверить. А если бы я внёс код из подпрограмм в главные циклы, то получилась бы слабочитаемая мешанина кода.
Заметьте, что вы всё ещё должны писать записи по отдельным полям. И если вы меняете объявление записи — вам лучше бы не забыть поменять код, сериализующий и десериализующий запись.
И снова: не забывайте, что это только пример. В реальных программах вам следует посмотреть в сторону класса-декоратора. Я не буду приводить тут пример: он делается по аналогии с предыдущими случаями. Просто добавьте новые методы чтения/записи, вот и всё.
Особенности
Посмотрим теперь на различные конкретные наследники класса TStream и то, что они нам предлагают.
Примечание: не все из нижеперечисленных возможностей существуют во всех версиях Delphi. Если у вас старая версия Delphi, то у вас могут отсутствовать некоторые описываемые классы, свойства или методы. В этом случае вам придётся обходится без них или писать аналоги самостоятельно.
THandleStream
THandleStream предназначен для работы с файлами в смысле операционной системы.
Короче говоря, THandleStream является оболочкой к файлам в стиле ОС. Объект этого типа можно связать с THandle , полученный любым способом — скажем, от CreateFile , CreatePipe и так далее: лишь бы на этот описатель можно было вызывать ReadFile и WriteFile .
Используйте THandleStream в двух случаях:
- Функция ОС вернула вам описатель THandle , а код, который вы хотите вызвать, требует TStream . Тогда просто создайте THandleStream , передав в его конструктор описатель от функции ОС, и передайте полученный объект коду. Например:
- Вы хотите использовать возможность, которую предоставляет функция ОС, но не объект Delphi. См. первый пример в следующем пункте.
Сам THandleStream не имеет ограничений и поддерживает все операции: чтение, запись, позиционирование и изменение размера. Однако нижележащий дескриптор объекта ядра может поддерживать не все операции. К примеру, описатель от файла на диске может быть открыт только для чтения, а описатель pipe не поддерживает позиционирование.
Описатель, которым инициализирован объект, доступен через свойство Handle .
Обратите внимание, что сам THandleStream никогда не закрывает описатель (ему нельзя передать ответственность за него). Поэтому вы должны закрывать описатель вручную или использовать TFileStream (см. ниже).
TFileStream
TFileStream предназначен для работы с файлами на диске.
TFileStream наследуется от THandleStream , так что он получает все возможности своего предка. TFileStream не имеет ограничений на операции и поддерживает конструктор с передачей описателя. Например: В отличие от THandleStream , TFileStream закрывает открытый описатель файла, вне зависимости от способа его инициализации.
Кроме того, TFileStream поддерживает перегруженный конструктор, позволяющий открывать файлы. У него есть два параметра: (имя файла) и (режим открытия + режим разделения).
Допустимые режимы открытия:
- fmCreate — создаёт новый файл. Если такой файл уже есть, он удаляется перед созданием. Файл открывается в режиме записи.
- fmOpenRead — открывает файл только для чтения.
- fmOpenWrite — открывает файл только для записи.
- fmOpenReadWrite — открывает файл для чтения-записи.
Режимы разделения:
- fmShareExclusive — запретить совместное использование.
- fmShareDenyWrite — запретить другим приложениям запись.
- fmShareDenyRead — запретить другим приложениям чтение.
- fmShareDenyNone — не вводить ограничения.
Режимы следует соединять друг с другом операцией or .
Примечание: у TFileStream есть вариант конструктора с дополнительным параметром, но этот (третий) параметр существует только для обратной совместимости и сейчас игнорируется. Не следует пытаться передавать в него режим разделения файла.
Типичный пример открытия файла выглядит так: А создания файла — так: При работе с файлами типа логов (для которых необходимы совместное использование или мониторинг) могут использоваться такие вызовы:
Далеко не все возможности функций открытия файлов ОС доступны через конструктор TFileStream , но зато он является универсальным для любых платформ. Предпочтительно использовать именно его для доступа к файлам вместо функций ОС. Если же вам нужны какие-либо возможности, недоступные через конструктор TFileStream , то используйте TFileStream , инициализировав его описателем файла от системной функции открытия файлов, как указано в примерах выше.
Если объект TFileStream создавался через конструктор с именем файла, то это имя файла доступно в свойстве FileName , иначе доступно только свойство Handle .
TMemoryStream
TMemoryStream реализует потоковую обёртку к данным в памяти программы. Т.е. к буферу в динамической памяти осуществляется последовательный доступ.
Используйте TMemoryStream , если вам нужен «просто поток» или вам нужен промежуточный буфер-поток.
TMemoryStream имеет конструктор без параметров, который создаёт пустой объект. Для заполнения потока после создания можно использовать обычные методы записи или же специальные методы LoadFromStream (аналог вызова CopyFrom(Stream, 0) ) и LoadFromFile .
TMemoryStream не имеет ограничений и поддерживает все операции, включая изменение размера.
TMemoryStream хранит данные в динамической куче процесса, выделяя память по мере необходимости. Он сам автоматически управляет памятью. Реально памяти может быть выделено больше, чем лежит данных в потоке — т.н. «capacity > size». Это стандартная оптимизация для «побайтовых записей».
Дополнительной возможностью TMemoryStream является предоставление свойства Memory , позволяющего обратиться к данным потока напрямую, через указатель, минуя последовательные методы чтения/записи. По этой причине вы можете рассматривать TMemoryStream как «переходник» между TStream и нетипизированным указателем.
Простейший пример использования TMemoryStream (в данном случае — для конвертации строки в TStream ): Примечание: в данном примере более эффективной была бы другая конструкция. Данный пример по сути копирует данные строки в поток. Но поскольку реально нам нужно здесь только чтение, то можно поступить по другому: создать поток, который будет работать прямо поверх данных строки, без их копирования. Это будет настоящий адаптер. Мы посмотрим на такой пример ниже.
TResourceStream
TResourceStream предназначен для организации последовательного доступа к ресурсам в исполняемых файлах.
TResourceStream похож на TMemoryStream . Он тоже работает с памятью программы (только не с кучей, а с ресурсами), он поддерживает методы SaveToStream и SaveToFile , а также свойство Memory .
Но в отличие от TMemoryStream , TResourceStream поддерживает только методы чтения, но не записи, а также не поддерживает изменение размера. Иными словами, TResourceStream — это read-only.
Собственно для инициализации у TResourceStream есть два варианта конструктора, которые имеют по три параметра: описатель модуля, имя ресурса и тип ресурса. А разница между ними заключается в способе указания имени ресурса (второго параметра): по ID или по имени.
Ну и несколько примеров:
TBytesStream
TBytesStream хранит данные потока в массиве байтов.
Используйте TBytesStream как переходник между TBytes и TStream .
Собственно, TBytesStream аналогичен TMemoryStream , только вместо блока памяти в куче он использует TBytes в качестве хранилища для данных. Он также не имеет ограничений на операции, поддерживая чтение, запись, позиционирование и изменение размера. Он поддерживает методы LoadFromStream , LoadFromFile , SaveToStream и SaveToFile , а также свойство Memory .
В отличие от TMemoryStream , у TBytesStream есть конструктор, который принимает переменную типа TBytes — это будут начальные данные потока. При этом не производится копирование данных (используется счётчик ссылок динамического массива). Все операции чтения-записи будут оперировать с исходными данными в оригинальной переменной типа TBytes . Однако если вы измените размер потока (либо явно через Size / SetSize , либо неявно через запись данных в конец потока данных), то поток сделает копию данных и будет работать уже с ней. При этом все будущие изменения в потоке не затронут оригинальной переменной типа TBytes .
Вы также можете передать nil в конструктор, чтобы инициализировать пустой поток. В этом случае он не будет связан с переменной.
Дополнительно TBytesStream вводит свойство Bytes оно работает аналогично свойству Memory , только имеет тип TBytes , а не Pointer . Предупреждение: не пытайтесь использовать Length для определения размера данных. Размер хранилища может быть больше актуального размера («Capacity > Size»). Используйте свойство Size для определения размера данных.
Простой пример использования TBytesStream как переходника (обратите внимание на усечение данных до их актуального размера, указанного в свойстве Size ):
TBytes является динамическим массивом, т.е. автоуправляемым типом. Явно освобождать его не нужно, заботиться о вопросах владения — тоже.
TStringStream
TStringStream предоставляет последовательный доступ к информации, хранящейся в обычной строке.
Используйте TStringStream для хранения данных в строках. Использование TStringStream даст вам в руки мощные возможности TStream . TStringStream удобен как промежуточный объект, который умеет хранить данные в строке, а также читать и писать их.
По сути, TStringStream является обёрткой к TBytesStream , которая просто конвертирует строку в байты и обратно. У TStringStream есть несколько вариантов конструкторов, которые инициализируют поток по разным типам строк. В Unicode версиях Delphi конструкторы также позволяют вам указывать кодировку для ANSI строк.
Методы чтения-записи TStringStream не затрагивают исходную строку, а всегда работают с копией данных (внутреннее хранилище в виде TBytes ).
Ну и, конечно же, TStringStream предоставляет строко-ориентированные свойства и методы. Во-первых, это методы WriteString и ReadString, которые пишут и читают данные из потока в виде строки. При этом кодировка (в Unicode-ных версиях Delphi) контролируется свойством Encoding . И, равно как и предыдущие классы, TStringStream выставляет наружу хранилище в «родном» формате: DataString .
Простой пример использования TStringStream как переходника между строками и TStream :
Заметьте, что несмотря на наличие методов чтения строк и загрузки/сохранения данных из/в файлы, TStringStream не пригоден для работы с текстовыми файлами. Он не работает с BOM и не позволяет прочитать одну строку (в смысле line) от разделителя до разделителя (он читает только указанное количество символов). По этой причине для работы с текстовыми файлами используют вспомогательный класс — TStringList . Этому классу будет посвящена следующая статья (где и будут показаны методы работы с текстом), а здесь же я только приведу примеры шифрования/расшифровки текстового файла, использующие оба этих класса: Конечно, на практике такой пример не имеет большого смысла, потому что гораздо проще просто работать с текстовым файлом как с двоичным — обработав его через TFileStream . Но более удачного и простого примера мне сейчас в голову не приходит, а код выше прекрасно показывает пример соединения трёх классов для работы.
TStreamAdapter
Понятие «потока данных» есть не только в Delphi, но и практически в любом другом современном языке. Разумеется, другие языки понятия не имеют, как работать с объектами Delphi, и наоборот: Delphi не знает, как устроены классы и объекты в других языках. К счастью, под Windows у нас есть COM и интерфейсы. С ними умеют работать почти все языки, так что это является де-факто стандартом межязыкового взаимодействия. И, конечно же, не могло быть иначе: для такой популярной концепции как «поток данных» существует свой интерфейс — IStream .
Иными словами, если вам нужно передать куда-то поток данных — вы используете IStream . Если вам кто-то передаёт поток данных, то это будет IStream .
Тут возникает маленькая проблемка: ваши Delphi объекты вообще-то не умеют работать с интерфейсом IStream : они работают с классом TStream . Что же делать?
Для этого в Delphi есть два класса-адаптера, которые конвертируют TStream в IStream и наоборот. При этом они являются тонкими оболочками, которые просто перенаправляют вызовы. Они конвертируют интерфейс, копирования данных потока не происходит: просто вызовы, скажем, класса конвертируются в вызовы интерфейса (и наоборот), работая с данными оригинального потока данных напрямую.
TStreamAdapter предоставляет переходник от TStream к IStream . Он принимает в конструкторе экземпляр TStream и выставляет наружу IStream , который вы можете передать в чужой код.
TStreamAdapter поддерживает те же операции, что и оригинальный поток, который в него завёрнут.
TStreamAdapter может взять на себе ответственность за удаление исходного потока данных, а может оставить её вам. Вот два примера использования TStreamAdapter , иллюстрирующие оба подхода:
TOleStream
TOleStream представляет собой обратный класс к TStreamAdapter : переходник от IStream к TStream .
Используется он аналогично. Единственная разница — нет вопроса владения, поскольку интерфейсы относятся к автоуправляемым типам.
TOleStream поддерживает те же операции, что и оригинальный поток, который в него завёрнут.
Пример использования TOleStream :
Прочие потоки
Это далеко не все стандартные потоки, реализованные в Delphi. К примеру, есть ещё TWinSocketStream , TBLOBStream , TZCompressionStream и многие-многие другие. Многие из них являются переходниками, но много классов также предоставляют свою собственную функциональность.
Создание своих классов-потоков
Как бы много в Delphi ни было классов-наследников, всегда найдётся случай, когда вас не устраивают стандартные классы. В этом случае вам нужно реализовывать свой класс-наследник.
К примеру, помните пример для TMemoryStream ? Там мы копировали данные строки в поток. Давайте сейчас напишем класс, который позволял бы работать с блоком памяти напрямую, без копирования. Разумеется, такой класс не может поддерживать операцию изменения размера, но чтение, запись и позиционирование — вполне.
Это несложно, если использовать в качестве базового класса TCustomMemoryStream — он уже умеет читать данные из блока памяти, поддерживает позиционирование, но не умеет писать и никак не управляет нижележащим хранилищем. Тогда мы получаем: Вот список методов, которые вы можете захотеть переопределить в своих классах-наследниках:
- (protected) function GetSize: Int64; и procedure SetSize(const NewSize: Int64); — для управление размером хранилища. Если вы не зададите свой GetSize , то реализация по умолчанию будет использовать Seek для поиска конца потока и определения размера (равному текущей позиции в конце потока). SetSize по умолчанию просто ничего не делает.
- (public) function Read(var Buffer; Count: Longint): Longint; и function Write(const Buffer; Count: Longint): Longint; для чтения и записи данных. По умолчанию оба метода абстрактные. В любом наследнике вы должны их замещать.
- (public) function Seek(const Offset: Int64; Origin: TSeekOrigin): Int64; для перемещения по потоку. Реализация по умолчанию вызывает исключение. Вы должны заместить этот метод, если ваши данные поддерживают позиционирование. Обычно это так. Исключение составляют случаи вроде сетевых сокетов.
Вот и всё. Всего три категории и всего пять методов. Реализуйте их — и у вас будет готовый поток. Все прочие методы и свойства являются переходниками к вышеуказанным. К примеру, метод GetPosition (get-акцессор для свойства Position ) реализован как Result := Seek(0, soCurrent);
В общем, как видите, создать свой класс-поток — это очень просто.
Преимущества и недостатки потоков данных
Плюсы:
- Де-факто стандарт языка Delphi
- Являются основой для других (более высокоуровневых) механизмов
- Имеют готовые оболочки для самых типичных случаев («не надо писать самому» — в отличие от файлов в стиле Pascal)
- Гибкость
- Стандартная обработка ошибок на исключениях
- Поддержка произвольных файлов (нет ограничения на размер)
- Нет проблем с многопоточностью*
- Межъязыковая совместимость (через IStream )
- Поддержка Unicode и кодировок при работе с текстовыми данными (но не текстовыми файлами — нет поддержки BOM)
- Легко расширяются написанием классов-наследников
Минусы:
- Необходимость ручной сериализации данных
- Ориентированы на побайтовую обработку, слабо пригодны для работы с текстовыми файлами
- Часто неопытные программисты используют Read и Write вместо ReadBuffer и WriteBuffer , не делая проверку результатов. Часто это приводит к некорректному коду без обработки ошибок
- Круче кривая обучения: сам TStream не умеет делать ничего. Значит, чтобы делать в программах что-то полезное, нужно изучать многочисленные наследники TStream , чтобы знать кто что умеет и когда что кого нужно применять. К примеру, если вам нужно отправить растр по сети, то вы должны сообразить, что вы можете создать THandleStream для описателя сетевого сокета и использовать его в сочетании с методом SaveToStream объекта растра. Сравните это с файлами в стиле Pascal, где было всего три файловых типа, покрывавших все случаи использования
Вывод: если вы работаете в Delphi и хотите работать с любыми данными, то потоки данных должны быть вашим первым вариантом. Используйте что-то другое, только если это «другое» больше подходит для вашей задачи (к примеру, динамический массив для типизированных данных; также некоторые люди могут рассматривать файлы в стиле Pascal более подходящими для работы с текстовыми данными).
(*) — некоторые люди неверно читают это предложение. Обратите внимание, что речь идёт о достоинствах и недостатках по сравнению с другими способами работы с файлами. Например, файлами Паскаля. Поэтому, в этом предложении утверждается, что у вас не будет проблем если вы работаете с двумя разными потоками данных (двумя объектами) в двух разных потоках кода, а вовсе не (как читают это некоторые) то, что вы можете обращаться к одному объекту из разных потоков.
Источник
|
|
|
[!] Как относитесь к модерированию на этом форуме? Выскажите свое мнение здесь
Stream write error
, При сохранении
- Подписаться на тему
- Сообщить другу
- Скачать/распечатать тему
|
|
При попытке сохранения файла из ресурса вылетает Stream write error. |
Adil |
|
Код — в студию! |
Kamenev_D |
|
Есть функция:
void __fastcall TForm1::InstallBDE() { TResourceStream *bde_stream = new TResourceStream(0,»BDE_ONLY», «BDE» ); bde_stream->SaveToFile(Path_Edit->Text+»BdeInst.dll»); delete bde_stream; typedef DWORD (CALLBACK* LPRSP)(DWORD,DWORD); HINSTANCE hDLL; AnsiString BDE_DLL = Path_Edit->Text + «BdeInst.dll»; hDLL = LoadLibrary(BDE_DLL.c_str()); if(hDLL) { LPRSP RSP = (LPRSP)GetProcAddress(hDLL, «DllRegisterServer»); if(RSP==NULL) { ShowMessage(«Не найдена указанная процедура»); return ; } if (RSP != NULL) RSP(0, 1); } FreeLibrary(hDLL); } которая вытаскивает BdeInst.dll и запускает его в обработчике кнопки вызываеться эта функция — и получаем эррор(который может появляться через раз) |
Adil |
|
Проверь в отладчике, что у тебя в Path_Edit->Text, значение bde_stream->Size (должно соответствовать размеру ресурса), мосмотри что лежит в bde_stream->Memory (должны быть байты как в ресурсе). А вообще, TResourceStream не имеет «своего» метода SaveToFile — он его унаследовал от TCustomMemoryStream и поэтому bde_stream->SaveToFile вовсе не добавит ресурс к длл-ке, как ты, имхо, надеешься, просто перепишет файл солдержимым своего Memory… З.Ы. Сообщение об ошибке еще может выскакивать, если твоя длл-ка используется при этом. |
Kamenev_D |
|
Цитата bde_stream->SaveToFile вовсе не добавит ресурс к длл Мне надо извлечь из ресурсов |
Adil |
|
Цитата Kamenev_D, 28.07.04, 13:49 — а это тогда о чем? |
0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
0 пользователей:
- Предыдущая тема
- Borland C++ Builder/Turbo C++ Explorer
- Следующая тема
[ Script execution time: 0,0197 ] [ 16 queries used ] [ Generated: 12.02.23, 00:09 GMT ]
I read my physical drive and am having some problems.
64-bit applications:
- 512 bytes:
Stream read error
/Stream write error
.Read
: workReadBuffer
: not work
- 38400 bytes: work.
32-bit applications: work on all cases.
function setinact(diskid: string): boolean;
var
hdevi: THandleStream;
hDevice: THandle;
mfile: TMemoryStream;
hbuff, mbuff: array[0..511] of byte;
i: integer;
BytesReturned: DWORD;
begin
Result:=False;
hDevice := CreateFile(Pchar('\.PHYSICALDRIVE'+diskid), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if hDevice <> INVALID_HANDLE_VALUE then
begin
try
hdevi := THandleStream.Create(hDevice);
try
mfile:=TMemoryStream.Create();
try
hdevi.ReadBuffer(hbuff[0],length(hbuff));
mfile.WriteBuffer(hbuff[0],Length(hbuff));
mfile.Position:=0;
mfile.ReadBuffer(mbuff[0],length(mbuff));
mbuff[446]:=$00;
mbuff[462]:=$00;
mbuff[478]:=$00;
mbuff[494]:=$00;
hdevi.Position:=0;
hdevi.WriteBuffer(mbuff[0],length(mbuff));
Result:=True;
finally
mfile.Free;
end;
finally
hdevi.Free;
DeviceIoControl(hDevice, IOCTL_DISK_UPDATE_PROPERTIES, nil, 0, nil, 0, BytesReturned, nil);
end;
finally
CloseHandle(hDevice);
end;
end;
end;
How to read 512 bytes on 64-bit applications?
Updated:
I’ve run this application on another PC, it worked. I do not understand why.
Updated 2:
Thanks to David Heffernan. The code below work. But why for 32-bit applications, it always succeed with the first code?
function setinact(diskid: string): boolean;
var
hDevice: THandle;
hbuff: PByte;
i: integer;
hexstr: String;
DISK_GEOMETRY : _DISK_GEOMETRY;
BytesPerSector: Int64;
BytesReturned: DWORD;
begin
Result:=False;
hDevice := CreateFile(Pchar('\.PHYSICALDRIVE'+diskid), GENERIC_READ or GENERIC_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE, nil, OPEN_EXISTING, 0, 0);
if hDevice = INVALID_HANDLE_VALUE then Exit;
try
GetMem(hbuff, 512);
try
if not DeviceIoControl(hDevice, IOCTL_DISK_GET_DRIVE_GEOMETRY, Nil, 0, @DISK_GEOMETRY, sizeof(DISK_GEOMETRY), BytesReturned, nil) then Exit;
BytesPerSector:=DISK_GEOMETRY.BytesPerSector;
if not ReadFile(hDevice, hbuff^, 512, BytesReturned, nil) then Exit;
.................
SetFilePointer(hDevice, 0, nil, FILE_BEGIN);
if not WriteFile(hDevice, hbuff^, 512, BytesReturned, nil) then Exit;
finally
FreeMem(hbuff, 512);
end;
Result:=True;
DeviceIoControl(hDevice, IOCTL_DISK_UPDATE_PROPERTIES, nil, 0, nil, 0, BytesReturned, nil);
finally
CloseHandle(hDevice);
end;
end;
-
dbutters@…
Guest
2018-03-05 17:41
<Try to describe precise steps that lead to the problem (where do you click, what keys do you press, what do you see, etc.)>
<If relevant, consider attaching a session log file or a screenshot)>
WinSCP 5.9.5
Error message:
Stream write error
Stack trace:
(0035E6DF) System::Classes::TStream::WriteBuffer
(0035E663) System::Classes::TStream::WriteBuffer
(003596AC) System::Classes::TStrings::SaveToStream
(006BA990) TIniFileStorage::~TIniFileStorage
(007265DF) TStoredSessionList::DoSave
(00726614) TStoredSessionList::Save
(00024EA3) TCustomScpExplorerForm::FormClose
(00208C7F) Vcl::Forms::TCustomForm::DoClose
(0024F2A9) Vcl::Controls::TWinControl::WndProc
(00209758) Vcl::Forms::TCustomForm::WndProc
(0080C7F8) Vclcommon::C2754_0
(0024E8F0) Vcl::Controls::TWinControl::MainWndProc
(0037326C) System::Classes::_18201
(00032429) USER32.dll
(0002488E) USER32.dll
(000244E5) USER32.dll
(0002E4B7) USER32.dll
(0007311B) ntdll.dll.KiUserCallbackDispatcher
(00025226) USER32.dll
(000220F1) UXTHEME.DLL
(00021EC0) UXTHEME.DLL
(000361C1) UXTHEME.DLL
(00034ED3) UXTHEME.DLL
(000255F9) USER32.dll
(00032429) USER32.dll
(0002488E) USER32.dll
(0001A312) USER32.dll.CallWindowProcW
(0024F3BA) Vcl::Controls::TWinControl::DefaultHandler
(0020CB3D) Vcl::Forms::TCustomForm::WMSysCommand
(0024F2A9) Vcl::Controls::TWinControl::WndProc
(00209758) Vcl::Forms::TCustomForm::WndProc
(0080C7F8) Vclcommon::C2754_0
(0024E8F0) Vcl::Controls::TWinControl::MainWndProc
(0037326C) System::Classes::_18201
(00032429) USER32.dll
(0002488E) USER32.dll
(000244E5) USER32.dll
(0002E4B7) USER32.dll
(0007311B) ntdll.dll.KiUs
I know how to reproduce the problem or the problem happens frequently enough. I wish to be contacted by the WinSCP team to help resolving the problem.
Reply with quote
Advertisement
-
martin◆
Site Admin - Joined:
- 2002-12-10
- Posts:
- 38,461
- Location:
- Prague, Czechia
2018-03-07
Please upgrade to the latest version of WinSCP first.
Reply with quote
-
xbeneteau@…
Guest
2019-06-04 13:39
<Try to describe precise steps that lead to the problem (where do you click, what keys do you press, what do you see, etc.)>
<If relevant, consider attaching a session log file or a screenshot)>
WinSCP 5.15.2
Error message:
Stream write error
Trace de la pile :
(0038EF63) System::Classes::TStream::WriteBuffer
(0038EEE7) System::Classes::TStream::WriteBuffer
(00389F30) System::Classes::TStrings::SaveToStream
(0073BB8C) TIniFileStorage::~TIniFileStorage
(006F29C1) TConfiguration::DoSave
(006F28A7) TConfiguration::Save
(000026DD) wWinMain
(009250D1) __wstartup
(00038800) ntdll.dll
(000387D0) ntdll.dll
The problem happens rarely. I cannot reproduce it.
Reply with quote
-
xbeneteau@…
Guest
2019-06-17 09:27
<Try to describe precise steps that lead to the problem (where do you click, what keys do you press, what do you see, etc.)>
<If relevant, consider attaching a session log file or a screenshot)>
WinSCP 5.15.2
Error message:
Stream write error
Trace de la pile :
(0038EF63) System::Classes::TStream::WriteBuffer
(0038EEE7) System::Classes::TStream::WriteBuffer
(00389F30) System::Classes::TStrings::SaveToStream
(0073BB8C) TIniFileStorage::~TIniFileStorage
(006F29C1) TConfiguration::DoSave
(006F28A7) TConfiguration::Save
(000026DD) wWinMain
(009250D1) __wstartup
(00038800) ntdll.dll
(000387D0) ntdll.dll
I know how to reproduce the problem or the problem happens frequently enough. I wish to be contacted by the WinSCP team to help resolving the problem.
Reply with quote
-
martin◆
Site Admin
2019-06-18
Thanks for your report.
I have sent you an email with a debug version of WinSCP to the address you have used to register on this forum.
Reply with quote
Advertisement
-
danielposney@…
Guest
2019-07-20 15:53
<Try to describe precise steps that lead to the problem (where do you click, what keys do you press, what do you see, etc.)>
<If relevant, consider attaching a session log file or a screenshot)>
WinSCP 5.15.2
Error message:
Stream write error
Stack trace:
(0038EF63) System::Classes::TStream::WriteBuffer
(0038EEE7) System::Classes::TStream::WriteBuffer
(00389F30) System::Classes::TStrings::SaveToStream
(0073BB8C) TIniFileStorage::~TIniFileStorage
(007C6BE7) TStoredSessionList::DoSave
(007C6C1C) TStoredSessionList::Save
(000298C9) TCustomScpExplorerForm::FormClose
(00232933) Vcl::Forms::TCustomForm::DoClose
(00278F45) Vcl::Controls::TWinControl::WndProc
(0023340C) Vcl::Forms::TCustomForm::WndProc
The problem happens rarely. I cannot reproduce it.
Reply with quote
-
jacquesxnunes@…
Guest
2019-08-01 19:26
<Try to describe precise steps that lead to the problem (where do you click, what keys do you press, what do you see, etc.)>
<If relevant, consider attaching a session log file or a screenshot)>
WinSCP 5.13.8
Error message:
Stream write error
Rastreamento de pilha:
(0036C50F) System::Classes::TStream::WriteBuffer
(0036C493) System::Classes::TStream::WriteBuffer
(003674DC) System::Classes::TStrings::SaveToStream
(006E1F0C) TIniFileStorage::~TIniFileStorage
(006A406D) TConfiguration::MoveStorage
(00839119) TPreferencesDialog::SaveConfiguration
(00835960) TPreferencesDialog::Execute
(00834AE5) DoPreferencesDialog
(00822ADE) TLoginDialog::ShowPreferencesDialog
(00822A86) TLoginDialog::PreferencesActionExecute
(0038109C) System::Classes::_18201
(000438E9) USER32.dll
(00025137) USER32.dll
(00024289) USER32.dll
(0002406B) USER32.dll.DispatchMessageW
(008C0005) C5105_3
(008C15DF) _ReThrowException
(006A40B4) TConfiguration::MoveStorage
(00839119) TPreferencesDialog::SaveConfiguration
(00835960) TPreferencesDialog::Execute
(00834AE5) DoPreferencesDialog
(00822ADE) TLoginDialog::ShowPreferencesDialog
(00822A86) TLoginDialog::PreferencesActionExecute
(0038109C) System::Classes::_18201
(000438E9) USER32.dll
(00025137) USER32.dll
(00024289) USER32.dll
(0002406B) USER32.dll.DispatchMessageW
The problem happens rarely. I cannot reproduce it.
Reply with quote
-
pierre-alexandre.combes@…
Guest
2019-09-11 13:26
<Try to describe precise steps that lead to the problem (where do you click, what keys do you press, what do you see, etc.)>
<If relevant, consider attaching a session log file or a screenshot)>
WinSCP 5.15.3
Error message:
Stream write error
Trace de la pile :
(0038F1BB) System::Classes::TStream::WriteBuffer
(0038F13F) System::Classes::TStream::WriteBuffer
(0038A188) System::Classes::TStrings::SaveToStream
(0073C080) TIniFileStorage::~TIniFileStorage
(000DEABF) TWinConfiguration::UpdateEntryInJumpList
(000DEB33) TWinConfiguration::AddSessionToJumpList
(000AB4AF) TTerminalManager::ConnectActiveTerminal
(000ABD4C) TTerminalManager::DoSetActiveTerminal
(000ABAD6) TTerminalManager::SetActiveTerminal
(00018C95) TCustomScpExplorerForm::NewSession
(00020248) TCustomScpExplorerForm::SessionTabSwitched
(0002029C) TCustomScpExplorerForm::SessionsPageControlChange
(0027919D) Vcl::Controls::TWinControl::WndProc
(00274294) Vcl::Controls::TControl::Perform
(00279303) Vcl::Controls::_17076
(0027919D) Vcl::Controls::TWinControl::WndProc
(00233664) Vcl::Forms::TCustomForm::WndProc
somtimes after connexion choice winscp application close automaticly it’s impossible see text in Windows.
i suppose it’s linked at last version Windows 10 updated.
regards
I know how to reproduce the problem or the problem happens frequently enough. I wish to be contacted by the WinSCP team to help resolving the problem.
Reply with quote
- pa00trunks
- Joined:
- 2019-09-11
- Posts:
- 2
- Location:
- france
2019-09-11 16:58
My liste of connexion is in .ini file localized in personnal folder sync with onedrive.
when i put this file out of ondrive sync connection working well.
so I suppose than fils was loock by ondrive.
système info: win10 release 1903 up to date.
I have see same probleme quickly in winscp forum about dropbox.
regards
Reply with quote
Advertisement
-
martin◆
Site Admin - Joined:
- 2002-12-10
- Posts:
- 38,461
- Location:
- Prague, Czechia
2019-09-13
WinSCP 5.15.3
Error message:
Stream write errorTrace de la pile :
(0038F1BB) System::Classes::TStream::WriteBuffer
(0038F13F) System::Classes::TStream::WriteBuffer
(0038A188) System::Classes::TStrings::SaveToStream
(0073C080) TIniFileStorage::~TIniFileStorage
(000DEABF) TWinConfiguration::UpdateEntryInJumpList
(000DEB33) TWinConfiguration::AddSessionToJumpList
(000AB4AF) TTerminalManager::ConnectActiveTerminal
(000ABD4C) TTerminalManager::DoSetActiveTerminal
(000ABAD6) TTerminalManager::SetActiveTerminal
(00018C95) TCustomScpExplorerForm::NewSession
…somtimes after connexion choice winscp application close automaticly it’s impossible see text in Windows.
i suppose it’s linked at last version Windows 10 updated.regards
I know how to reproduce the problem or the problem happens frequently enough. I wish to be contacted by the WinSCP team to help resolving the problem.
Thanks for your report.
I have sent you an email with a debug version of WinSCP to the address you have used to register on this forum.
Reply with quote
- pa00trunks
- Joined:
- 2019-09-11
- Posts:
- 2
- Location:
- france
2019-09-17 09:24
I have found why INI files with personal config saved in my documents not work.
In question Windows 10 Ransomware Protection !! will be activated in my professional computer.
This option is activated by my company by error last week.
So disable this option or parameter well WinScp and putty allow app.
Resolved for me.
Regards.
Reply with quote
-
martin◆
Site Admin - Joined:
- 2002-12-10
- Posts:
- 38,461
- Location:
- Prague, Czechia
2019-09-17
pa00trunks wrote:
I have found why INI files with personal config saved in my documents not work.
In question Windows 10 Ransomware Protection !! will be activated in my professional computer.
This option is activated by my company by error last week.So disable this option or parameter well WinScp and putty allow app.
Resolved for me.
Thanks for sharing your findings.
WinSCP should have produced a better error message anyway.
So I have at least improved that:
https://winscp.net/tracker/1787
Reply with quote
-
pmoriarty@…
Guest
2020-10-25 07:37
<Try to describe precise steps that lead to the problem (where do you click, what keys do you press, what do you see, etc.)>
<If relevant, consider attaching a session log file or a screenshot)>
WinSCP 5.17.8
Error message:
Stream write error
Stack trace:
(00390BEC) System::Classes::TStream::WriteBuffer
(00DD9F70) TEditorForm::SaveToFile
(00DD9FCC) TEditorForm::SaveFile
(001C7ED1) Vcl::Actnlist::TCustomAction::Execute
(002394FD) Vcl::Forms::TCustomForm::CloseQuery
(0027AC61) Vcl::Controls::TWinControl::WndProc
(00235128) Vcl::Forms::TCustomForm::WndProc
I know how to reproduce the problem or the problem happens frequently enough. I wish to be contacted by the WinSCP team to help resolving the problem.
Reply with quote
Advertisement
-
martin◆
Site Admin
2020-10-27
Thanks for your report.
I have sent you an email with a debug version of WinSCP to the address you have used to register on this forum.
Reply with quote
Advertisement