Delphi class helpers: a practical example.

Using the standard VCL TFileStream class you can’t set a sharing mode for a new file until Delphi 2010. A trick like this

var
  Stream: TStream;

begin
  Stream:= TFileStream.Create('TEST', fmCreate or fmShareDenyWrite);
...

is useless since fmCreate or fmShareDenyWrite = fmCreate, and you can see from VCL sources that a new file is created without sharing. There is a bug report #71632 on QC currently marked as “fixed” (in Delphi 2010, and I am using Delphi 2009).
If one need to create a new file with sharing enabled in Delphi 2009 or prior Delphi version, a class helper looks like a good workaround. Let us create a “helper” unit which extends TFileStream functionality by adding an additional constructor:

unit MyClasses;

interface

uses RTLConsts, Windows, SysUtils, Classes;

type
  TMyFileStream = class helper for TFileStream
    constructor CreateNew(const AFileName: string; ShareMode: LongWord = 0);
  end;

implementation

{
  Valid ShareMode values are:
    FILE_SHARE_READ,
    FILE_SHARE_WRITE,
    FILE_SHARE_DELETE,
  or any combination like FILE_SHARE_READ or FILE_SHARE_WRITE, etc
}
constructor TMyFileStream.CreateNew(const AFileName: string; ShareMode: LongWord);
var
  FileHandle: THandle;

begin
  FileHandle:= CreateFile(PChar(AFileName), GENERIC_READ or GENERIC_WRITE,
  ShareMode, nil, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);

  if FileHandle = INVALID_HANDLE_VALUE then
    raise EFOpenError.CreateResFmt(@SFOpenErrorEx,
          [ExpandFileName(AFileName), SysErrorMessage(GetLastError)]);

  inherited Create(FileHandle);

//  FFileName := AFileName;
end;

end.

Now we can test the constructor. Create a new VCL application and add the “MyClasses.pas” unit to the “uses” clause of the main form; drop a button component to the main form and create an event handler:

procedure TForm1.Button1Click(Sender: TObject);
var
  Stream, TestStream: TStream;
  Buf, OutBuf: Cardinal;

begin
//  Stream:= TFileStream.CreateNew('TEST');
  Stream:= TFileStream.CreateNew('TEST', FILE_SHARE_READ);
  try
    Buf:= 12345678;
    Stream.WriteBuffer(Buf, SizeOf(Buf));
    TestStream:= TFileStream.Create('TEST', fmOpenRead or fmShareDenyNone);
    try
      TestStream.ReadBuffer(OutBuf, SizeOf(OutBuf));
      ShowMessage(IntToStr(OutBuf));
    finally
      TestStream.Free;
    end;
  finally
    Stream.Free;
  end;
end;

We can see that file sharing for a newly created file really works.
Note that we use TFileStream.CreateNew syntax (not TMyFileStream.CreateNew). The only problem with CreateNew constructor is TFileStream.FileName property – since TFileStream.FFileName field is declared as strict private it is not accessible from a class helper, and the field cannot be set in the CreateNew constructor.

Advertisements

2 thoughts on “Delphi class helpers: a practical example.

  1. You can access private members in class helpers when you explicitly fully qualify with Self. Autocompletion won’t show the members but it will compile and work.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s