ホーム >プログラム >Delphi 6 ローテクTips >Unicode対応のTFileStream

Unicode対応のTFileStream。


unit myStream;

interface
uses
  Classes,
  SysUtils,
  Windows;


type
  TFileStreamW = class(THandleStream)
  public
    constructor Create(const FileName: WideString; Mode: Word);
    destructor Destroy; override;
  end;


implementation
uses
  RTLConsts;


{TFileStreamW}
constructor TFileStreamW.Create(const FileName: WideString; Mode: Word);
//TFileStreamのUnicode版 
var
  li_AccessMode, li_ShareMode: DWORD;
begin
  if (Mode = fmCreate) then begin
    inherited Create(CreateFileW(
        PWideChar(FileName),           //ファイル名
        GENERIC_READ or GENERIC_WRITE, //アクセスモード
        0,                             //共有モード
        nil,                           //セキュリティ
        OPEN_ALWAYS,                   //作成方法
        FILE_ATTRIBUTE_NORMAL,         //ファイル属性
        0                              //テンプレート
    ));
  end else begin
    //アクセスモード
    li_AccessMode := GENERIC_READ;
    if ((Mode and fmOpenWrite) <> 0) then li_AccessMode := li_AccessMode or GENERIC_WRITE;

    {$WARN SYMBOL_PLATFORM OFF}
    //共有モード
    li_ShareMode := 0;
    if (Mode >= fmShareExclusive) then Dec(Mode, $10);
    if ((Mode and (fmShareDenyRead - $10)) <> 0) then begin
      //読み込みは禁止=書き込みはOK
      li_ShareMode := FILE_SHARE_WRITE;
    end;
    if ((Mode and (fmShareDenyWrite - $10)) <> 0) then begin
      //書き込みは禁止=読み込みはOK
      li_ShareMode := li_ShareMode or FILE_SHARE_READ;
    end;
    {$WARN SYMBOL_PLATFORM ON}

    inherited Create(CreateFileW(
        PWideChar(FileName),
        li_AccessMode,
        li_ShareMode,
        nil,
        OPEN_EXISTING,
        FILE_ATTRIBUTE_NORMAL,
        0
    ));
  end;
  if (FHandle = Integer(INVALID_HANDLE_VALUE)) then begin
    //エラーメッセージはUnicode対応ではない。
    raise EFCreateError.CreateResFmt(@SFCreateError, [FileName]);
  end;
end;

destructor TFileStreamW.Destroy;
begin
  if (FHandle >= 0) then CloseHandle(FHandle);
  inherited Destroy;
end;

THandleStreamを継承してCreateFileW APIの戻り値をTHandleStreamのCreateの引数に渡すだけです。
あっけないくらい簡単にできてしまいます。


ところでDelphiの場合ファイルの共有モードは許可しない指定をします。

fmShareDenyWrite 他のアプリケーションは読み出し用にファイルを開くことはできるが,書き込み用に開くことはできない
fmShareDenyRead 他のアプリケーションは書き込み用にファイルを開くことはできるが,読み出し用に開くことはできない
fmShareDenyNone 他のアプリケーションからのファイルの読み出しおよび書き込みを妨げる処理は行われない

fmShareDenyWriteやfmShareDenyReadはまだしもfmShareDenyNoneになると意味としては「許可しないのはない」ということになるのでしょうが、分かりにくい表現になってしまっています。
CreateFile APIのように許可する共有を指定するようにした方が素直で分かりやすいのではないかと思います。
ということでCreateの引数の指定の仕方を変えた版を。

interface
uses
  Classes,
  SysUtils,
  Windows;


type
  TMyFileStream = class(THandleStream)
  public
    constructor Create(FileName: WideString; AccessMode, ShareMode: DWORD); overload;
    constructor Create(FileName: WideString); overload;
    destructor Destroy; override;
  end;


implementation
uses
  RTLConsts;

//------------------------------------------------------------------------------
{TMyFileStream}
constructor TMyFileStream.Create(FileName: WideString; AccessMode, ShareMode: DWORD);
//引数の指定の仕方を変えた版
var
  li_CreateMode: DWORD;
begin
  if ((AccessMode and GENERIC_WRITE) <> 0) then begin
    li_CreateMode := OPEN_ALWAYS;
  end else begin
    li_CreateMode := OPEN_EXISTING;
  end;
  inherited Create(CreateFileW(
      PWidechar(FileName),   //ファイル名
      AccessMode,            //アクセスモード
      ShareMode,             //共有モード
      nil,                   //セキュリティ
      li_CreateMode,         //作成方法
      FILE_ATTRIBUTE_NORMAL, //ファイル属性
      0                      //テンプレート
  ));
end;

constructor TMyFileStream.Create(FileName: WideString);
begin
  Create(FileName, GENERIC_READ, FILE_SHARE_READ or FILE_SHARE_WRITE);
end;

destructor TMyFileStream.Destroy;
begin
  if (FHandle >= 0) then CloseHandle(FHandle);
  inherited Destroy;
end;

AccessModeとShareModeはCreateFile APIの指定そのままです。
AccessModeには読み込みか書き込みかあるいはその両方かをGENERIC_READとGENERIC_WRITEの組み合わせで指定します。
ShareModeには共有するモードをFILE_SHARE_READとFILE_SHARE_WRITEの組み合わせで指定します。


2009-01-14: