При попытке чтения отображенный в память файл, возникает ошибка: "Access violation at address 0040B$E6 in module ‘Project1.exe’. Read of address 003D0000".
Ошибка возникает не всегда. Где-то 5-7 раз из десяти запусков программы. То есть иногда программа работает без ошибок, а при следующем запуске может вылететь ошибка. Закономерностей ошибки никаких не заметил.
Код.
procedure TForm1.Button1Click(Sender: TObject);
var
str:string;
FFileHandle: THandle; // Дескриптор открытого файла
FMapHandle: THandle; // Дескриптор объекта отображения файла
FData: PByte; // Указатель на данные файла при отображении
PData: PChar; // Указатель для ссылки на данные файла
ViewPos:Int64; // Cмещение файла, откуда начинается его отображение
ViewSize:DWORD; // Определяет количество байтов файла,
// предназначенных для отображения.
// Нулевое значение указывает на отображение целого файла
begin
ViewPos:=65536;
ViewSize:=65536;
getMem(PData,20);
// Открытие файла, кодировка UTF-16LE
FFileHandle := FileOpen('1.txt', fmOpenRead);
// Создание объекта отображения в память
FMapHandle := CreateFileMapping(FFileHandle, nil, PAGE_READONLY, 0, 0, nil);
// Отображение данных файла в адресное пространство процесса
FData:=MapViewOfFile(FMapHandle,FILE_MAP_READ,
Int64Rec(ViewPos).Hi, Int64Rec(ViewPos).Lo, ViewSize);
//ошибка, если она есть, происходит на этой строке
StrLCopy( PData, PChar(FData), 10);
Уважаемые авторы вопросов! Большая просьба сообщить о результатах решения проблемы на этой странице. Иначе, следящие за обсуждением, возможно имеющие аналогичные проблемы, не получают ясного представления об их решении. А авторы ответов не получают обратной связи. Что можно расценивать, как проявление неуважения к отвечающим от автора вопроса.
16-09-2009 02:34 | Сообщение от автора вопроса
А можно полюбопытствовать, каким образом вы её решили?
Будут в файлах: #0
То есть, в середине вашего текста может быть нулевой символ?!
Не знаю пока еще, подумаю. Ошибка больше не вылазит, и это главное, остальное решится так или иначе.
Сейчас откомпилоровал в Delphi 7, работает без ошибок во всех операционных системах. Поставил еще Delphi 2010, к сожелению, то же самое, что и D2009. А мне бы хотелось, чтоб работало именно в 2009 или 2010.
>>>Вообще-то, ошибка вылетает тут: str:=PData;
Нет. У меня, там, где я отметил. Например, если написать так, то ошибка не исчезнет:
//str:=PData;
str:='раз два';
А если так:
try
StrLCopy( PData, PChar(pView), 10);
except
ShowMessage('Ошибка именно здесь!');
end;
>>>А в этом тестовом приложении, ваша ошибка точно появляется? Попробовал на нескольких файлах, у меня ошибок нет.undefined
У меня ОС – Windows 7, 64-бит. Сейчас попробовал запустить программу на виртуальной машине. На русской 32-битной XP ошибок нет. На английской XP и на русской win2000 - не запустилось ни разу (может мало пробовал). На русской 32-битной Vista тоже запустилось, но с 11 попытки… Мне надо, чтоб работало везде, ну разве что кроме w98. Также пробовал с другими файлами, кодировками. Результат тот же.
>>>А каков среднестатистический размер читаемого файла?undefined
Средний размер 20 Мб. Но есть и больше, самый большой сейчас 420 Мб. Файлов около 50.
>>>А позиция этого куска, кратна dwAllocationGranularity?undefined
С этим не будет проблемы.
>>>Добавте ShowMessage(SysErrorMessage(GetLastError)) после строки, где возникает ошибка.undefined
Добавил, при ошибке ничего не пишет. Если добавлять строку не сразу, а после finally, пишет, что операция успешна завершена, потом появляется привычное сообщение с ошибкой: Access violation итд.
try
//ошибка, если она есть, происходит на этой строке
StrLCopy( PData, PChar(pView), 10);
finally
ShowMessage(SysErrorMessage(GetLastError));
13-09-2009 05:10 | Вопрос к автору: запрос дополнительной информации
Так это тестовое приложение будет наиболее походить на рабочий проект.
Я уже писал, что ошибка, то происходит, то нет. Если при запуске программы при нажатии на кнопку ошибка не появилась, так и при повторных нажатиях по кнопке этой ошибки нет.
А в этом тестовом приложении, ваша ошибка точно появляется? Попробовал на нескольких файлах, у меня ошибок нет.
Программе необходимо очень быстро прочитать кусок большого файла
А каков среднестатистический размер читаемого файла?
Предполагается, что программа знает, где этот кусок находится
А позиция этого куска, кратна dwAllocationGranularity?
Добавте ShowMessage(SysErrorMessage(GetLastError)) после строки, где возникает ошибка.
Попробовал изменить код, с теми поправками, что вы внесли. К сожалению, ошибка некуда не пропала:
procedure TForm1.Button1Click(Sender: TObject);
var
SysInfo: TSystemInfo;
BlockSize, FileSize: Int64;
str:string;
FFileHandle: THandle; // Дескриптор открытого файла
FMapHandle: THandle; // Дескриптор объекта отображения файла
pView: Pointer; // Указатель на данные файла при отображении
PData: PChar; // Указатель для ссылки на данные файла
ViewPos:Int64; // Cмещение файла, откуда начинается его отображение
ViewSize:DWORD; // Определяет количество байтов файла,
// предназначенных для отображения.
// Нулевое значение указывает на отображение целого файла
// Открытие файла, кодировка UTF-16LE
FFileHandle := FileOpen('1.txt', fmOpenRead);
if FFileHandle=0 then ShowMessage('Ошибка открытия файла');
// Создание объекта отображения в память
FMapHandle := CreateFileMapping(FFileHandle, nil, PAGE_READONLY, 0, 0, nil);
if FMapHandle=0 then ShowMessage('Объект отображения не создан');
Программе необходимо очень быстро прочитать кусок большого файла. Предполагается, что программа знает, где этот кусок находится, поэтому чтение по блокам здесь не нужно. Вот почему в первом примере я задал небольшое смещение относительно начала файла. Так это тестовое приложение будет наиболее походить на рабочий проект.
Вот еще одно наблюдение. Я уже писал, что ошибка, то происходит, то нет. Если при запуске программы при нажатии на кнопку ошибка не появилась, так и при повторных нажатиях по кнопке этой ошибки нет. Ну и наоборот, если уже есть ошибка, то лечится только перезапуском приложения.
Во первых, у вас неправильно реализовано смещение (если оно там вообще должно быть). Позиция (ViewPos) в файле, с которой вы начинаете проецирование, изначально равна 64 кб, получается первые 64 кб вы не задумываясь пропускаете. Далее вы устанавливаете размер (ViewSize) проецируемого файла в 64 кб и при этом не проверяете размер самого файла, когда он оказывается меньше 64 кб - вы получаете свой заслуженный "Access Violation".
Во вторых, процедуру проецирования файла, вы выполняете только один раз, получается читаете только первые (вторые) 64 кб (по крайней мере, while'ов или repeat'ов не видно).
В третьих, не стоит пренебрегать проверками на ошибки, обязательно проверяйте значения возвращаемые функциями, да и про try..finally не забывайте.
Ну и в четвёртых, зачем всё это вам? Я сильно сомневаюсь, что вы работаете с текстовыми файлами (пусть даже в юникоде), размер которых превышает 2 Гб. Чем вас не устраивает обычное чтение файла? Тем более, для небольших файлов, по скорости не уступает маппированию.
Ладно, решать вам.
По поводу кода, если вы всё-таки читаете (думаете, что читаете) файл целиком - удалите из кода ViewPos и ViewSize, и уберите смещение - MapViewOfFile(FMapHandle,FILE_MAP_READ, 0, 0, 0).
Если же вам действительно жизненно необходимо чтение блоками, то вот:
procedure TForm1.Button2Click(Sender: TObject);
var
SysInfo: TSystemInfo;
pView: Pointer;
FMapHandle, FFileHandle: THandle;
ViewPos, ViewSize, FileSize, BlockSize: Int64;
begin
GetSystemInfo(SysInfo);
BlockSize:=SysInfo.dwAllocationGranularity * 1; //вместо "1", указываем минимальный блок чтения (смещение), который должен быть кратен гранулярности памяти (обычно 64 Кб)
FFileHandle:=FileOpen('C:\Temp\123.txt', fmOpenRead);
if FFileHandle=0 then
Exit;
try
FMapHandle:=CreateFileMapping(FFileHandle, nil, PAGE_READONLY, 0, 0, nil);
if FMapHandle=0 then
Exit;
Если вы заметили орфографическую ошибку на этой странице, просто выделите ошибку мышью и нажмите Ctrl+Enter. Функция может не работать в некоторых версиях броузеров.