FileClass.pas 19 KB

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