まずModeに指定できる値としてfm...の定義値を調べてみます。fmCreateのみがClasses.pasに
fmCreate = $FFFF;
(Classes.pasの53行目)(Delphi 2007の場合、以下同じ)と定義され、その他の値はSysUtils.pasに
fmOpenRead = $0000;
fmOpenWrite = $0001;
fmOpenReadWrite = $0002;
fmShareCompat = $0000 platform; // DOS compatibility mode is not portable
fmShareExclusive = $0010;
fmShareDenyWrite = $0020;
fmShareDenyRead = $0030 platform; // write-only not supported on all platforms
fmShareDenyNone = $0040;
(SysUtils.pasの44行目付近)と定義されています。…fmCreateが0xFFFFで他の定義値が16bitの定数ということは、ModeにfmCreateを指定した場合、その他の指定は一切意味を持たない、ということになりますね。実際にTFileStream.Createを見てみると、
constructor TFileStream.Create(const AFileName: string; Mode: Word; Rights: Cardinal);
begin
if Mode = fmCreate then
begin
...
end
else
begin
...
end;
FFileName := AFileName;
end;
(Classes.pasの5474行目付近)と、確かにModeがfmCreateかそれ以外かで完全に処理が分けられてしまっています。ではfmCreateのケースをさらに追ってみます。
inherited Create(FileCreate(AFileName, Rights));
(Classes.pasの5478行目)とSysUtils.pas上のFileCreate (ja)を呼び出しています(inherited CreateはTFileStreamの派生元であるTHandleStream (ja)のコンストラクタ呼び出しです)。FileCreateは
Result := Integer(CreateFile(PChar(FileName), GENERIC_READ or GENERIC_WRITE,
0, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0));
(SysUtils.pasの5259行目付近)という実装であり、結果としてfmCreateの場合、dwDesiredAccessはGENERIC_READ or GENERIC_WRITE、dwShareModeは0(
Prevents other processes from opening a file or device if they request delete, read, or write access.=他の処理からは削除、読み込み、書き込みアクセスできない)、dwCreationDispositionはCREATE_ALWAYS(
新しいファイルを作成します。指定したファイルが既に存在している場合、そのファイルを上書きし、既存の属性を消去します。)と、これらの値が固定的に指定される、ということになります。
一方fmOpen...の場合、
inherited Create(FileOpen(AFileName, Mode));
(Classes.pasの5484行目)とSysUtils.pas上のFileOpen (ja)を呼び出しています。FileOpenは
const
AccessMode: array[0..2] of LongWord = (
GENERIC_READ,
GENERIC_WRITE,
GENERIC_READ or GENERIC_WRITE);
ShareMode: array[0..4] of LongWord = (
0,
0,
FILE_SHARE_READ,
FILE_SHARE_WRITE,
FILE_SHARE_READ or FILE_SHARE_WRITE);
...
Result := Integer(CreateFile(PChar(FileName), AccessMode[Mode and 3],
ShareMode[(Mode and $F0) shr 4], nil, OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL, 0));
(SysUtils.pasの5196行目付近)という実装で、dwDesiredAccessはfmOpenRead→GENERIC_READ、fmOpenWrite→GENERIC_WRITE、fmOpenReadWrite→GENERIC_READ or GENERIC_WRITEとマップされ、dwShareModeはfmShareCompat/fmShareExclusive→0(他の処理からは削除、読み込み、書き込みアクセスできない)、fmShareDenyWrite→FILE_SHARE_READ(他の処理からは読み込みアクセスのみ許可)、fmShareDenyRead→FILE_SHARE_WRITE(他の処理からは書き込みアクセスのみ許可)、fmShareDenyNone→FILE_SHARE_READ or FILE_SHARE_WRITE(他の処理からは読み込み、書き込みアクセスとも許可)とマップされ、dwCreationDispositionはOPEN_EXISTING(
ファイルを開きます。指定したファイルが存在していない場合、この関数は失敗します。)が固定的に指定される、ということになります。
これ以外の組み合わせ、たとえば書き込みのために新規にファイルを作成(存在していたら上書き)し、そのファイルに読み込みアクセスのみ許可する、というような場合(dwDesiredAccess = GENERIC_READ or GENERIC_WRITE、dwShareMode = FILE_SHARE_READ、dwCreationDisposition = CREATE_ALWAYS)はTFileStreamを使用するのではなく、直接Windows APIのCreateFileをこれらのパラメータで呼び出し、取得したファイルハンドルでTHandleStreamを作成する、という処理が必要になります。例えばこんな感じでしょうか。
procedure HandleStreamSample(const AFilename: String);
var
FileHandle: Integer;
Stream: THandleStream;
begin
FileHandle := CreateFile(PChar(AFilename),GENERIC_READ or GENERIC_WRITE,
FILE_SHARE_READ,nil,CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,0);
if FileHandle < 0 then
begin
raise EFCreateError.CreateResFmt(@SFCreateErrorEx,[ExpandFileName(AFileName),
SysErrorMessage(GetLastError)]);
end;
Stream := THandleStream.Create(FileHandle);
try
{ Streamに対する処理 }
finally
Stream.Free;
end;
end;
あるいはCreateFileの他のパラメータ、例えば第6パラメータにFILE_FLAG_...を指定したいような場合などもこのようなコーディングが必要になります。
0 件のコメント:
コメントを投稿