まず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 件のコメント:
コメントを投稿