Shared.FileClass.pas 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664
  1. unit Shared.FileClass;
  2. {
  3. Inno Setup
  4. Copyright (C) 1997-2025 Jordan Russell
  5. Portions by Martijn Laan
  6. For conditions of distribution and use, see LICENSE.TXT.
  7. TFile class
  8. Better than File and TFileStream in that does more extensive error checking
  9. and uses descriptive, localized system error messages.
  10. TTextFileReader and TTextFileWriter support ANSI and UTF8 textfiles.
  11. }
  12. interface
  13. uses
  14. Windows, SysUtils;
  15. type
  16. TFileCreateDisposition = (fdCreateAlways, fdCreateNew, fdOpenExisting,
  17. fdOpenAlways, fdTruncateExisting);
  18. TFileAccess = (faRead, faWrite, faReadWrite);
  19. TFileSharing = (fsNone, fsRead, fsWrite, fsReadWrite);
  20. TCustomFile = class
  21. private
  22. function GetCappedSize: Cardinal;
  23. protected
  24. function GetPosition: Int64; virtual; abstract;
  25. function GetSize: Int64; virtual; abstract;
  26. public
  27. class procedure RaiseError(ErrorCode: DWORD);
  28. class procedure RaiseLastError;
  29. function Read(var Buffer; Count: Cardinal): Cardinal; virtual; abstract;
  30. procedure ReadBuffer(var Buffer; Count: Cardinal);
  31. procedure Seek(Offset: Int64); virtual; abstract;
  32. procedure WriteAnsiString(const S: AnsiString);
  33. procedure WriteBuffer(const Buffer; Count: Cardinal); virtual; abstract;
  34. property CappedSize: Cardinal read GetCappedSize;
  35. property Position: Int64 read GetPosition;
  36. property Size: Int64 read GetSize;
  37. end;
  38. TFile = class(TCustomFile)
  39. private
  40. FHandle: THandle;
  41. FHandleCreated: Boolean;
  42. protected
  43. function CreateHandle(const AFilename: String;
  44. ACreateDisposition: TFileCreateDisposition; AAccess: TFileAccess;
  45. ASharing: TFileSharing): THandle; virtual;
  46. function GetPosition: Int64; override;
  47. function GetSize: Int64; override;
  48. public
  49. constructor Create(const AFilename: String;
  50. ACreateDisposition: TFileCreateDisposition; AAccess: TFileAccess;
  51. ASharing: TFileSharing);
  52. constructor CreateDuplicate(const ASourceFile: TFile);
  53. constructor CreateWithExistingHandle(const AHandle: THandle);
  54. destructor Destroy; override;
  55. function Read(var Buffer; Count: Cardinal): Cardinal; override;
  56. procedure Seek(Offset: Int64); override;
  57. procedure SeekToEnd;
  58. procedure Truncate;
  59. procedure WriteBuffer(const Buffer; Count: Cardinal); override;
  60. property Handle: THandle read FHandle;
  61. end;
  62. TMemoryFile = class(TCustomFile)
  63. private
  64. FMemory: Pointer;
  65. FSize: Cardinal;
  66. FPosition: Int64;
  67. function ClipCount(DesiredCount: Cardinal): Cardinal;
  68. protected
  69. procedure AllocMemory(const ASize: Cardinal);
  70. function GetPosition: Int64; override;
  71. function GetSize: Int64; override;
  72. public
  73. constructor Create(const AFilename: String);
  74. constructor CreateFromMemory(const ASource; const ASize: Cardinal);
  75. constructor CreateFromZero(const ASize: Cardinal);
  76. destructor Destroy; override;
  77. function Read(var Buffer; Count: Cardinal): Cardinal; override;
  78. procedure Seek(Offset: Int64); override;
  79. procedure WriteBuffer(const Buffer; Count: Cardinal); override;
  80. property Memory: Pointer read FMemory;
  81. end;
  82. TTextFileReader = class(TFile)
  83. private
  84. FBufferOffset, FBufferSize: Cardinal;
  85. FEof: Boolean;
  86. FBuffer: array[0..4095] of AnsiChar;
  87. FSawFirstLine: Boolean;
  88. FCodePage: Word;
  89. function DoReadLine(const UTF8: Boolean): AnsiString;
  90. function GetEof: Boolean;
  91. procedure FillBuffer;
  92. public
  93. function ReadLine: String;
  94. function ReadAnsiLine: AnsiString;
  95. property CodePage: Word write FCodePage;
  96. property Eof: Boolean read GetEof;
  97. end;
  98. TTextFileWriter = class(TFile)
  99. private
  100. FSeekedToEnd: Boolean;
  101. FUTF8WithoutBOM: Boolean;
  102. procedure DoWrite(const S: AnsiString; const UTF8: Boolean);
  103. protected
  104. function CreateHandle(const AFilename: String;
  105. ACreateDisposition: TFileCreateDisposition; AAccess: TFileAccess;
  106. ASharing: TFileSharing): THandle; override;
  107. public
  108. property UTF8WithoutBOM: Boolean read FUTF8WithoutBOM write FUTF8WithoutBOM;
  109. procedure Write(const S: String);
  110. procedure WriteLine(const S: String);
  111. procedure WriteAnsi(const S: AnsiString);
  112. procedure WriteAnsiLine(const S: AnsiString);
  113. end;
  114. TFileMapping = class
  115. private
  116. FMemory: Pointer;
  117. FMapSize: Cardinal;
  118. FMappingHandle: THandle;
  119. public
  120. constructor Create(AFile: TFile; AWritable: Boolean);
  121. destructor Destroy; override;
  122. procedure Commit;
  123. procedure ReraiseInPageErrorAsFileException;
  124. property MapSize: Cardinal read FMapSize;
  125. property Memory: Pointer read FMemory;
  126. end;
  127. EFileError = class(Exception)
  128. private
  129. FErrorCode: DWORD;
  130. public
  131. property ErrorCode: DWORD read FErrorCode;
  132. end;
  133. implementation
  134. uses
  135. WideStrUtils,
  136. UnsignedFunc,
  137. Shared.CommonFunc;
  138. const
  139. SGenericIOError = 'File I/O error %d';
  140. { TCustomFile }
  141. function TCustomFile.GetCappedSize: Cardinal;
  142. { Like GetSize, but capped at $7FFFFFFF }
  143. begin
  144. const LSize = GetSize;
  145. if LSize > High(Int32) then
  146. Result := High(Int32)
  147. else
  148. Result := Cardinal(LSize);
  149. end;
  150. class procedure TCustomFile.RaiseError(ErrorCode: DWORD);
  151. var
  152. S: String;
  153. E: EFileError;
  154. begin
  155. S := Win32ErrorString(ErrorCode);
  156. if S = '' then begin
  157. { In case there was no text for the error code. Shouldn't get here under
  158. normal circumstances. }
  159. S := Format(SGenericIOError, [ErrorCode]);
  160. end;
  161. E := EFileError.Create(S);
  162. E.FErrorCode := ErrorCode;
  163. raise E;
  164. end;
  165. class procedure TCustomFile.RaiseLastError;
  166. begin
  167. RaiseError(GetLastError);
  168. end;
  169. procedure TCustomFile.ReadBuffer(var Buffer; Count: Cardinal);
  170. begin
  171. if Read(Buffer, Count) <> Count then begin
  172. { Raise localized "Reached end of file" error }
  173. RaiseError(ERROR_HANDLE_EOF);
  174. end;
  175. end;
  176. procedure TCustomFile.WriteAnsiString(const S: AnsiString);
  177. begin
  178. WriteBuffer(S[1], ULength(S));
  179. end;
  180. { TFile }
  181. constructor TFile.Create(const AFilename: String;
  182. ACreateDisposition: TFileCreateDisposition; AAccess: TFileAccess;
  183. ASharing: TFileSharing);
  184. begin
  185. inherited Create;
  186. FHandle := CreateHandle(AFilename, ACreateDisposition, AAccess, ASharing);
  187. if (FHandle = 0) or (FHandle = INVALID_HANDLE_VALUE) then
  188. RaiseLastError;
  189. FHandleCreated := True;
  190. end;
  191. constructor TFile.CreateDuplicate(const ASourceFile: TFile);
  192. begin
  193. inherited Create;
  194. var LHandle: THandle;
  195. if not DuplicateHandle(GetCurrentProcess, ASourceFile.Handle,
  196. GetCurrentProcess, @LHandle, 0, False, DUPLICATE_SAME_ACCESS) then
  197. RaiseLastError;
  198. FHandle := LHandle; { assign only on success }
  199. FHandleCreated := True;
  200. end;
  201. constructor TFile.CreateWithExistingHandle(const AHandle: THandle);
  202. begin
  203. inherited Create;
  204. FHandle := AHandle;
  205. end;
  206. destructor TFile.Destroy;
  207. begin
  208. if FHandleCreated then
  209. CloseHandle(FHandle);
  210. inherited;
  211. end;
  212. function TFile.CreateHandle(const AFilename: String;
  213. ACreateDisposition: TFileCreateDisposition; AAccess: TFileAccess;
  214. ASharing: TFileSharing): THandle;
  215. const
  216. AccessFlags: array[TFileAccess] of DWORD =
  217. (GENERIC_READ, GENERIC_WRITE, GENERIC_READ or GENERIC_WRITE);
  218. SharingFlags: array[TFileSharing] of DWORD =
  219. (0, FILE_SHARE_READ, FILE_SHARE_WRITE, FILE_SHARE_READ or FILE_SHARE_WRITE);
  220. Disps: array[TFileCreateDisposition] of DWORD =
  221. (CREATE_ALWAYS, CREATE_NEW, OPEN_EXISTING, OPEN_ALWAYS, TRUNCATE_EXISTING);
  222. begin
  223. Result := CreateFile(PChar(AFilename), AccessFlags[AAccess],
  224. SharingFlags[ASharing], nil, Disps[ACreateDisposition],
  225. FILE_ATTRIBUTE_NORMAL, 0);
  226. end;
  227. function TFile.GetPosition: Int64;
  228. begin
  229. if not SetFilePointerEx(FHandle, 0, @Result, FILE_CURRENT) then
  230. RaiseLastError;
  231. end;
  232. function TFile.GetSize: Int64;
  233. begin
  234. if not GetFileSizeEx(FHandle, Result) then
  235. RaiseLastError;
  236. end;
  237. function TFile.Read(var Buffer; Count: Cardinal): Cardinal;
  238. begin
  239. if not ReadFile(FHandle, Buffer, Count, DWORD(Result), nil) then
  240. if FHandleCreated or (GetLastError <> ERROR_BROKEN_PIPE) then
  241. RaiseLastError;
  242. end;
  243. procedure TFile.Seek(Offset: Int64);
  244. begin
  245. if not SetFilePointerEx(FHandle, Offset, nil, FILE_BEGIN) then
  246. RaiseLastError;
  247. end;
  248. procedure TFile.SeekToEnd;
  249. begin
  250. if not SetFilePointerEx(FHandle, 0, nil, FILE_END) then
  251. RaiseLastError;
  252. end;
  253. procedure TFile.Truncate;
  254. begin
  255. if not SetEndOfFile(FHandle) then
  256. RaiseLastError;
  257. end;
  258. procedure TFile.WriteBuffer(const Buffer; Count: Cardinal);
  259. var
  260. BytesWritten: DWORD;
  261. begin
  262. if not WriteFile(FHandle, Buffer, Count, BytesWritten, nil) then
  263. RaiseLastError;
  264. if BytesWritten <> Count then begin
  265. { I'm not aware of any case where WriteFile will return True but a short
  266. BytesWritten count. (An out-of-disk-space condition causes False to be
  267. returned.) But if that does happen, raise a generic-sounding localized
  268. "The system cannot write to the specified device" error. }
  269. RaiseError(ERROR_WRITE_FAULT);
  270. end;
  271. end;
  272. { TMemoryFile }
  273. constructor TMemoryFile.Create(const AFilename: String);
  274. var
  275. F: TFile;
  276. begin
  277. inherited Create;
  278. F := TFile.Create(AFilename, fdOpenExisting, faRead, fsRead);
  279. try
  280. AllocMemory(F.CappedSize);
  281. F.ReadBuffer(FMemory^, FSize);
  282. finally
  283. F.Free;
  284. end;
  285. end;
  286. constructor TMemoryFile.CreateFromMemory(const ASource; const ASize: Cardinal);
  287. begin
  288. inherited Create;
  289. AllocMemory(ASize);
  290. Move(ASource, FMemory^, NativeInt(FSize));
  291. end;
  292. constructor TMemoryFile.CreateFromZero(const ASize: Cardinal);
  293. begin
  294. inherited Create;
  295. AllocMemory(ASize);
  296. FillChar(FMemory^, NativeInt(FSize), 0);
  297. end;
  298. destructor TMemoryFile.Destroy;
  299. begin
  300. if Assigned(FMemory) then
  301. LocalFree(HLOCAL(FMemory));
  302. inherited;
  303. end;
  304. procedure TMemoryFile.AllocMemory(const ASize: Cardinal);
  305. begin
  306. { Limit size to the range of an Integer because the Move and FillChar
  307. functions take 32-bit signed integers in 32-bit builds }
  308. if ASize > Cardinal(High(Integer)) then
  309. raise Exception.Create('TMemoryFile: Size limit exceeded');
  310. FMemory := Pointer(LocalAlloc(LMEM_FIXED, ASize));
  311. if FMemory = nil then
  312. OutOfMemoryError;
  313. FSize := ASize;
  314. end;
  315. function TMemoryFile.ClipCount(DesiredCount: Cardinal): Cardinal;
  316. begin
  317. { First check if FPosition is already past FSize, so the subtraction below
  318. won't underflow. And to be extra safe, make sure FPosition isn't negative
  319. (even though Seek already checks for that). }
  320. if FPosition >= FSize then begin
  321. Result := 0;
  322. Exit;
  323. end;
  324. if FPosition < 0 then
  325. RaiseError(ERROR_NEGATIVE_SEEK);
  326. const BytesLeft: Cardinal = FSize - Cardinal(FPosition);
  327. if DesiredCount > BytesLeft then
  328. Result := BytesLeft
  329. else
  330. Result := DesiredCount;
  331. end;
  332. function TMemoryFile.GetPosition: Int64;
  333. begin
  334. Result := FPosition;
  335. end;
  336. function TMemoryFile.GetSize: Int64;
  337. begin
  338. Result := FSize;
  339. end;
  340. function TMemoryFile.Read(var Buffer; Count: Cardinal): Cardinal;
  341. begin
  342. Result := ClipCount(Count);
  343. if Result <> 0 then begin
  344. Move((PByte(FMemory) + Cardinal(FPosition))^, Buffer, NativeInt(Result));
  345. Inc(FPosition, Result);
  346. end;
  347. end;
  348. procedure TMemoryFile.Seek(Offset: Int64);
  349. begin
  350. if Offset < 0 then
  351. RaiseError(ERROR_NEGATIVE_SEEK);
  352. FPosition := Offset;
  353. end;
  354. procedure TMemoryFile.WriteBuffer(const Buffer; Count: Cardinal);
  355. begin
  356. if ClipCount(Count) <> Count then
  357. RaiseError(ERROR_HANDLE_EOF);
  358. if Count <> 0 then begin
  359. Move(Buffer, (PByte(FMemory) + Cardinal(FPosition))^, NativeInt(Count));
  360. Inc(FPosition, Count);
  361. end;
  362. end;
  363. { TTextFileReader }
  364. procedure TTextFileReader.FillBuffer;
  365. begin
  366. if (FBufferOffset < FBufferSize) or FEof then
  367. Exit;
  368. FBufferSize := Read(FBuffer, SizeOf(FBuffer));
  369. FBufferOffset := 0;
  370. if FBufferSize = 0 then
  371. FEof := True;
  372. end;
  373. function TTextFileReader.GetEof: Boolean;
  374. begin
  375. FillBuffer;
  376. Result := FEof;
  377. end;
  378. function TTextFileReader.ReadLine: String;
  379. var
  380. S: RawByteString;
  381. begin
  382. S := DoReadLine(True);
  383. if FCodePage <> 0 then
  384. SetCodePage(S, FCodePage, False);
  385. Result := String(S);
  386. end;
  387. function TTextFileReader.ReadAnsiLine: AnsiString;
  388. begin
  389. Result := DoReadLine(False);
  390. end;
  391. function TTextFileReader.DoReadLine(const UTF8: Boolean): AnsiString;
  392. var
  393. I, L: Cardinal;
  394. S: AnsiString;
  395. begin
  396. while True do begin
  397. FillBuffer;
  398. if FEof then begin
  399. { End of file reached }
  400. if S = '' then begin
  401. { If nothing was read (i.e. we were already at EOF), raise localized
  402. "Reached end of file" error }
  403. RaiseError(ERROR_HANDLE_EOF);
  404. end;
  405. Break;
  406. end;
  407. I := FBufferOffset;
  408. while I < FBufferSize do begin
  409. if FBuffer[I] in [#10, #13] then
  410. Break;
  411. Inc(I);
  412. end;
  413. L := ULength(S);
  414. if Integer(L + (I - FBufferOffset)) < 0 then
  415. OutOfMemoryError;
  416. SetLength(S, L + (I - FBufferOffset));
  417. Move(FBuffer[FBufferOffset], S[L+1], NativeInt(I - FBufferOffset));
  418. FBufferOffset := I;
  419. if FBufferOffset < FBufferSize then begin
  420. { End of line reached }
  421. Inc(FBufferOffset);
  422. if FBuffer[FBufferOffset-1] = #13 then begin
  423. { Skip #10 if it follows #13 }
  424. FillBuffer;
  425. if (FBufferOffset < FBufferSize) and (FBuffer[FBufferOffset] = #10) then
  426. Inc(FBufferOffset);
  427. end;
  428. Break;
  429. end;
  430. end;
  431. if not FSawFirstLine then begin
  432. if UTF8 then begin
  433. { Handle UTF8 as requested: check for a BOM at the start and if not found then check entire file }
  434. if (Length(S) > 2) and (S[1] = #$EF) and (S[2] = #$BB) and (S[3] = #$BF) then begin
  435. Delete(S, 1, 3);
  436. FCodePage := CP_UTF8;
  437. end else begin
  438. var OldPosition := GetPosition;
  439. try
  440. var CappedSize := GetCappedSize; //can't be 0
  441. Seek(0);
  442. var S2: AnsiString;
  443. SetLength(S2, CappedSize);
  444. SetLength(S2, Read(S2[1], CappedSize));
  445. if DetectUTF8Encoding(S2) in [etUSASCII, etUTF8] then
  446. FCodePage := CP_UTF8;
  447. finally
  448. Seek(OldPosition);
  449. end;
  450. end;
  451. end;
  452. FSawFirstLine := True;
  453. end;
  454. Result := S;
  455. end;
  456. { TTextFileWriter }
  457. function TTextFileWriter.CreateHandle(const AFilename: String;
  458. ACreateDisposition: TFileCreateDisposition; AAccess: TFileAccess;
  459. ASharing: TFileSharing): THandle;
  460. begin
  461. { faWrite access isn't enough; we need faReadWrite access since the Write
  462. method may read. No, we don't have to do this automatically, but it helps
  463. keep it from being a 'leaky abstraction'. }
  464. if AAccess = faWrite then
  465. AAccess := faReadWrite;
  466. Result := inherited CreateHandle(AFilename, ACreateDisposition, AAccess,
  467. ASharing);
  468. end;
  469. procedure TTextFileWriter.DoWrite(const S: AnsiString; const UTF8: Boolean);
  470. { Writes a string to the file, seeking to the end first if necessary }
  471. const
  472. CRLF: array[0..1] of AnsiChar = (#13, #10);
  473. UTF8BOM: array[0..2] of AnsiChar = (#$EF, #$BB, #$BF);
  474. var
  475. C: AnsiChar;
  476. begin
  477. if not FSeekedToEnd then begin
  478. const LSize = GetSize;
  479. if LSize <> 0 then begin
  480. { File is not empty. Figure out if we have to append a line break. }
  481. Seek(LSize - SizeOf(C));
  482. ReadBuffer(C, SizeOf(C));
  483. case C of
  484. #10: ; { do nothing - file ends in LF or CRLF }
  485. #13: begin
  486. { If the file ends in CR, make it into CRLF }
  487. C := #10;
  488. WriteBuffer(C, SizeOf(C));
  489. end;
  490. else
  491. { Otherwise, append CRLF }
  492. WriteBuffer(CRLF, SizeOf(CRLF));
  493. end;
  494. end else if UTF8 and not FUTF8WithoutBOM then
  495. WriteBuffer(UTF8BOM, SizeOf(UTF8BOM));
  496. FSeekedToEnd := True;
  497. end;
  498. WriteBuffer(Pointer(S)^, ULength(S));
  499. end;
  500. procedure TTextFileWriter.Write(const S: String);
  501. begin
  502. DoWrite(Utf8Encode(S), True);
  503. end;
  504. procedure TTextFileWriter.WriteLine(const S: String);
  505. begin
  506. Write(S + #13#10);
  507. end;
  508. procedure TTextFileWriter.WriteAnsi(const S: AnsiString);
  509. begin
  510. DoWrite(S, False);
  511. end;
  512. procedure TTextFileWriter.WriteAnsiLine(const S: AnsiString);
  513. begin
  514. WriteAnsi(S + #13#10);
  515. end;
  516. { TFileMapping }
  517. var
  518. _RtlNtStatusToDosError: function(Status: NTSTATUS): ULONG; stdcall;
  519. constructor TFileMapping.Create(AFile: TFile; AWritable: Boolean);
  520. const
  521. Protect: array[Boolean] of DWORD = (PAGE_READONLY, PAGE_READWRITE);
  522. DesiredAccess: array[Boolean] of DWORD = (FILE_MAP_READ, FILE_MAP_WRITE);
  523. begin
  524. inherited Create;
  525. if not Assigned(_RtlNtStatusToDosError) then
  526. _RtlNtStatusToDosError := GetProcAddress(GetModuleHandle('ntdll.dll'),
  527. 'RtlNtStatusToDosError');
  528. FMapSize := AFile.CappedSize;
  529. FMappingHandle := CreateFileMapping(AFile.Handle, nil, Protect[AWritable], 0,
  530. FMapSize, nil);
  531. if FMappingHandle = 0 then
  532. TFile.RaiseLastError;
  533. FMemory := MapViewOfFile(FMappingHandle, DesiredAccess[AWritable], 0, 0,
  534. FMapSize);
  535. if FMemory = nil then
  536. TFile.RaiseLastError;
  537. end;
  538. destructor TFileMapping.Destroy;
  539. begin
  540. if Assigned(FMemory) then
  541. UnmapViewOfFile(FMemory);
  542. if FMappingHandle <> 0 then
  543. CloseHandle(FMappingHandle);
  544. inherited;
  545. end;
  546. procedure TFileMapping.Commit;
  547. { Flushes modified pages to disk. To avoid silent data loss, this should
  548. always be called prior to destroying a writable TFileMapping instance -- but
  549. _not_ from a 'finally' section, as this method will raise an exception on
  550. failure. }
  551. begin
  552. if not FlushViewOfFile(FMemory, 0) then
  553. TFile.RaiseLastError;
  554. end;
  555. procedure TFileMapping.ReraiseInPageErrorAsFileException;
  556. { In Delphi, when an I/O error occurs while accessing a memory-mapped file --
  557. known as an "inpage error" -- the user will see an exception message of
  558. "External exception C0000006" by default.
  559. This method examines the current exception to see if it's an inpage error
  560. that occurred while accessing our mapped view, and if so, it raises a new
  561. exception of type EFileError with a more friendly and useful message, like
  562. you'd see when doing non-memory-mapped I/O with TFile. }
  563. var
  564. E: TObject;
  565. begin
  566. E := ExceptObject;
  567. if (E is EExternalException) and
  568. (EExternalException(E).ExceptionRecord.ExceptionCode = EXCEPTION_IN_PAGE_ERROR) and
  569. (Cardinal(EExternalException(E).ExceptionRecord.NumberParameters) >= Cardinal(2)) and
  570. (Cardinal(EExternalException(E).ExceptionRecord.ExceptionInformation[1]) >= Cardinal(FMemory)) and
  571. (Cardinal(EExternalException(E).ExceptionRecord.ExceptionInformation[1]) < Cardinal(Cardinal(FMemory) + FMapSize)) then begin
  572. { There should be a third parameter containing the NT status code of the error
  573. condition that caused the exception. Convert that into a Win32 error code
  574. and use it to generate our error message. }
  575. if (Cardinal(EExternalException(E).ExceptionRecord.NumberParameters) >= Cardinal(3)) and
  576. Assigned(_RtlNtStatusToDosError) then
  577. TFile.RaiseError(_RtlNtStatusToDosError(NTSTATUS(EExternalException(E).ExceptionRecord.ExceptionInformation[2])))
  578. else begin
  579. { Use generic "The system cannot [read|write] to the specified device" errors }
  580. if EExternalException(E).ExceptionRecord.ExceptionInformation[0] = 0 then
  581. TFile.RaiseError(ERROR_READ_FAULT)
  582. else
  583. TFile.RaiseError(ERROR_WRITE_FAULT);
  584. end;
  585. end;
  586. end;
  587. end.