Ручки освобождаются как-то повреждены?
Странная проблема. Может быть, кто-то может дать некоторое представление.
Сценарий 1. У меня есть TBitmap в памяти, который записывается в то время, когда выполняются сложные вычисления для вычисления цвета каждого пикселя. Время от времени (обычно после каждой горизонтальной линии, когда растровое изображение заполняется) TBitmap рисуется на изображение в форме (image1.Canvas.Draw(0, 0, TBitmap). В большинстве случаев это работает нормально, но я заметил, что если для каждой строки растрового изображения имеется много медленных комплексных вызовов (скажем, для расчета требуется более 30 секунд или минут), то в главной форме появляется кратковременное "мерцание", которое каким-то образом стирает растровое изображение, поэтому вызов image.draw только отрисовывает последняя вычисленная строка и первые y-строки в растровом изображении скрыты. Я обошел это, заблокировав растровое изображение перед вычислениями.
Сценарий 2. Это главная неприятность. Я пишу в TMemoryStream, а не в растровое изображение. Та же сделка Расчеты выполняются для вычисления каждого значения пикселя, а затем каждое значение пикселя записывается в TMemoryStream с помощью memstream.Write(bytevalue, 1) во время процесса. В конце всех вычислений я сохраняю поток в растровое изображение с помощью memstream.SaveToFile('what.bmp'), а затем освобождаю поток с помощью memstream.Free. Если вычисление быстрое, то поток сохраняется независимо от размера (я делаю тесты с размерами 10000x10000).
Я даже могу сказать, что полученный файл будет поврежден, так как главное окно / форма приложения слегка мигает, как будто его перекрашивают. Когда это происходит, все дескрипторы битовых карт и TMemoryStream уничтожаются / обновляются, поэтому существующие данные повреждены.
Есть идеи? Это действительно отстой. Особенно, когда создание каждого отдельного изображения может занять час, и только после того, как оно заканчивается, что-то в фоновом режиме произошло и повредило растровое изображение или TMemoryStream.
Есть ли способ, которым я могу заблокировать дескриптор TMemoryStream, как я могу с помощью растрового изображения? Это может помочь. Или какое-то объявление Delphi: "Не связывайтесь с моими объектами, даже если кажется, что приложение занимает слишком много времени"
Или кто-нибудь знает внутреннюю причину в Delphi, которая вызывает это.
TMemoryStream создается внутри процедуры, которая выполняет все вычисления, так же как и локальный объект. С проблемой растрового изображения растровое изображение было глобальной переменной вне процедуры, и это произошло, поэтому я не думаю, что это является причиной.
Это также под Windows 7, но я заметил исходную проблему растрового изображения под Vista.
Обновление 1:
Извините за неиспользование комментариев, но есть ограничение на размер текста...
В ответ Реми (и всем, кто читает это)...
Однопоточный. Для потока памяти он работает нормально для разрешения 5000x5000, если вычисления быстрые, но не работает, если вычисления медленные.
В качестве базовой структуры код соответствует
SetupMemorystream;
for y:=0 to height do
for x:=0 to width do
DoCalcs;
SetByteValue;
end;
end;
SaveStream;
Если DoCalcs относительно быстр, то все идет по плану. Если это медленно, то я получаю повреждение TMemoryStream, и результирующее растровое изображение, в котором сохранен поток, повреждено.
Это было идентично использованию TBitmap в памяти до тех пор, пока я не обнаружил, что могу заблокировать растровое изображение, которое останавливает Delphi и / или Windows, перераспределяя ему новый дескриптор "когда он хочет", который портит данные внутри растрового изображения.
Это слишком большое совпадение, чтобы не думать, что та же проблема не возникает с TMemoryStream и его дескриптором.
Обновление 2:
Еще одна полезная информация.
Когда TMemoryStream сохраняет OK, результирующий файл (для растрового изображения 5000x5000) имеет размер 75 000 054 байта.
Когда сохраненный поток поврежден, это кажется случайным значением (размера от момента, когда дескриптор был поврежден, до сохранения потока). Размеры примеров составляли 22 МБ и 9 МБ.
Когда я смотрю на получившиеся файлы, это шестнадцатеричный редактор, который показывает, что начало файлов корректно с кусками заголовка, но хвостовые концы как-то усекаются.
Это так странно. В любом случае, я могу абсолютно точно очистить TMemoryStream после вызова SaveToFile и перед его освобождением?
5 ответов
Перед записью каждого байта в поток памяти установите для параметра Capacity приблизительный размер потока битов, чтобы он не часто изменял размер памяти. Это ускорит дело
Я думаю, что вы должны вычесть 1 из высоты и ширины для цикла
ура
Спасибо за все подсказки, ребята. Циклы имели правильное значение от 0 до width-1 в коде.
Проблема заключалась в том, что переменные для ширины и высоты были глобальными переменными, которые изменились в другом месте приложения при изменении размера главной формы (они обычно отслеживают отображаемое на экране изображение, но я использовал те же переменные внутри этой процедуры, чтобы отслеживать ширину растрового изображения в памяти и высота. Таким образом, когда они изменились снаружи, петли облажались и повредили вывод. Что за проблема с отслеживанием. Как только я локализовал переменные ширины и высоты, все работает, как ожидалось.
Я должен был знать, что это была моя ошибка, а не проблема Delphi.
Когда вы освобождаете файловый поток, используемый для записи, вызов для закрытия файла не проверяется на наличие ошибок. Таким образом, ваша запись может завершиться с ошибкой, возможно, при очистке последних блоков вашего большого файла, и никаких исключений не будет.
Это недавно меня поразило: оно в QC 80148. Однако с этим ничего не поделаешь, потому что функция Windows CloseHandle также вряд ли выдаст какую-либо ошибку.
Вы можете использовать вызов API FlushFileBuffers(filestream.Handle)
сбросить tFileStream, но я предполагаю, что вначале происходит что-то еще, что может вызвать повреждение, которое может быть таким же простым, как ваш цикл, переходящий от 0 к ширине, а не от 1 к ширине или от 0 к ширине-1... его Трудно сказать, не анализируя, что ваши подпрограммы делают для заполнения потока памяти.
Трудно найти такую ошибку, просто предполагая, что происходит. Поскольку операция настолько продолжительна, дорого просто повторять и ждать, если будет найдена ошибка.
Я предлагаю вам регистрировать каждый шаг процесса. Возможно, вы получите огромный журнал, но он покажет вам, где что-то идет не так. Повторите процесс и улучшите свой журнал, таким образом, вы медленно, но верно найдете причину проблемы.