Shared.FileClass.pas 19 KB

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