Ручки освобождаются как-то повреждены? - PullRequest
5 голосов
/ 15 декабря 2009

Странная проблема. Может быть, кто-то может дать некоторое представление.

  • Сценарий 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 ]

1 голос
/ 16 декабря 2009

Спасибо за все советы, ребята. Циклы имели правильное значение от 0 до width-1 в коде.

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

Я должен был знать, что это была моя ошибка, а не проблема Delphi.

1 голос
/ 15 декабря 2009

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

Это недавно меня поразило: оно в QC 80148. Однако с этим ничего не поделаешь, потому что функция Windows CloseHandle также вряд ли выдаст ошибку.

1 голос
/ 15 декабря 2009
  1. Перед записью каждого байта в поток памяти, установите Емкость на приблизительный размер потока битов, чтобы он не часто изменял размер памяти. Это ускорит дело

  2. Я думаю, что вы должны вычесть 1 из высоты и ширины для цикла

Приветствия

0 голосов
/ 15 декабря 2009

Вы можете использовать вызов API FlushFileBuffers(filestream.Handle), чтобы очистить tFileStream, но я предполагаю, что вначале происходит что-то еще, что может привести к повреждению, что может быть так же просто, как ваш цикл, переходящий от 0 к ширине, а не к 1 в ширину или от 0 до ширины-1 ... трудно сказать, не анализируя, что ваши подпрограммы делают для заполнения потока памяти.

0 голосов
/ 15 декабря 2009

Трудно найти такую ​​ошибку, просто предполагая, что происходит. Поскольку операция настолько продолжительна, стоит просто повторить и подождать, если будет найдена ошибка.

Я предлагаю вам регистрировать каждый шаг процесса. Возможно, вы получите огромный журнал, но он покажет вам, где что-то идет не так. Повторите процесс и улучшите свой журнал, так вы медленно, но верно найдете причину проблемы.

...