The Wiert Corner – irregular stream of stuff

Jeroen W. Pluimers on .NET, C#, Delphi, databases, and personal interests

  • My work

  • My badges

  • Twitter Updates

  • My Flickr Stream

    20140508-Delphi-2007--Project-Options--Cannot-Edit-Application-Title-HelpFile-Icon-Theming

    20140430-Fiddler-Filter-Actions-Button-Run-Filterset-now

    20140424-Windows-7-free-disk-space

    More Photos
  • Pages

  • All categories

  • Enter your email address to subscribe to this blog and receive notifications of new posts by email.

    Join 1,757 other followers

Delphi: using IInterface to restore cursor at end of mehod (prelude to a memento that executes any code at end of method).

Posted by jpluimers on 2014/08/06

A long while ago, I wrote about a (then overdue post) on .NET/C#: Using IDisposable to restore temporary settrings example: TemporaryCursor class.

I had been using a similar technique in Delphi since before I found out about the TRecall class and thought: I think my TTemporaryCursor is smarter, as it is based on interfaces.

TRecall (and the Vcl.Graphics descendants TBrushRecall, TFontRecall, and TPenRecall) store TPersistent properties using the Assign method. They were introduced in Delphi 6.

Too bad there are only very few people using TRecall as lots of TPersistent things warrant sasaving and restoring.

My TTemporaryCursor (now at bitbucket) class only stores an integer, so it cannot derive from TRecall. Besides it is based on IInterface which got introduced.

Note I’m not the first to publish about such a class (Malcolm Grooves wrote TCursorSnapshot, SwissDelphiCenter has TMyCursor, Nick Hodges published about TAutoCursor), it’s just that it has been in my tool box for so long, and written memento classes that you will see 2 articles on it this week.

In the mean time (this works with Delphi 2009 and up), I also wrote a small class that does similar things for any anonymous method. More on that tomorrow.

Back to TRecall: it is an example of the memento pattern in Delphi. The memento pattern allows you to restore state.

SourceMaking.com a.k.a. Design Patterns and Refactoring is a great site about Design PatternsUMLAntiPatterns and Refactoring.

Most pattern example code is available in all of the C#, C++, Delphi, Java and PHP languages.

Great stuff!

One of the constructs for restoring state is a try … finally … end construct: it allows you to always execute something in the finally block, for instance restoring the state to right before the try block.

For instance something like this:

procedure TTemporaryCursorMainForm.TemporaryCursorButtonClick(Sender: TObject);
var
  Button: TButton;
  SavedEnabled: Boolean;
begin
  Button := Sender as TButton;
  SavedEnabled := Button.Enabled; // save state
  try
    Button.Enabled := False; // set state
    Sleep(3000); // sleep 3 seconds with the button disabled
  finally
    Button.Enabled := SavedEnabled; // restore state
  end;
end;

Basically the save/set/restore state code can be anything: nothing/open/close file for instance.

Time for an example memento base class:

unit MementoUnit;

interface

uses
  TemporaryCursorUnit;

type
  IMemento = interface(IInterface)
  ['{529987B4-0C7C-4103-BCE7-EB9651E88E58}']
  end;

  TMemento = class(TInterfacedObject, IMemento)
  strict private
    FObject: TObject;
  public
    constructor Create(const AObject: TObject);
    destructor Destroy; override;
    procedure Restore(const AObject: TObject); virtual; abstract;
    class function CreateMemento(const AObject: TObject): IMemento;
  end;

implementation

{ TMemento }
constructor TMemento.Create(const AObject: TObject);
begin
  inherited Create();
  FObject := AObject;
end;

destructor TMemento.Destroy;
begin
  Restore(FObject);
  inherited Destroy();
end;

class function TMemento.CreateMemento(const AObject: TObject): IMemento;
begin
  Result := TMemento.Create(AObject);
end;

end.

The cool thing about Delphi interfaces is that they are reference counted. The Delphi base interface is IInterface. When the reference goes out of scope, it calls _Release, and when the reference count gets to zero, the Destroy destructor is called. TMemento (now also at bitbucket) forwards that to Restore.

There is a little trick with Delphi interfaces: you do not have to assign the result of TMemento.CreateMemento. Even if you do not assign it, Delphi will automatically keep the reference until the calling method ends: Delphi adds an implicit try … finally … end block where the finally … end will decrease the reference count.

Based on the TMemento idea, I created the TTemporaryCursor class and ITemporaryCursor interface. They could have been derived from TMemento and IMemento, but to keep it simpler, I have made the unit self-contained:

unit TemporaryCursorUnit;

interface

uses
  Vcl.Controls;

// .NET/C# Equivalent: https://wiert.me/2012/01/26/netc-using-idisposable-to-restore-temporary-settrings-example-temporarycursor-class/

type
  ITemporaryCursor = interface(IInterface)
    ['{495ADE0F-EFBE-4A0E-BF37-F1ACCACCE03D}']
  end;

  TTemporaryCursor = class(TInterfacedObject, ITemporaryCursor)
  strict private
    FCursor: TCursor;
  public
    constructor Create(const ACursor: TCursor);
    destructor Destroy; override;
    class function SetTemporaryCursor(const ACursor: TCursor = crHourGlass): ITemporaryCursor;
  end;

implementation

uses
  Vcl.Forms;

{ TTemporaryCursor }
constructor TTemporaryCursor.Create(const ACursor: TCursor);
begin
  inherited Create();
  FCursor := Screen.Cursor;
  Screen.Cursor := ACursor;
end;

destructor TTemporaryCursor.Destroy;
begin
  if Assigned(Screen) then
    Screen.Cursor := FCursor;
  inherited Destroy();
end;

class function TTemporaryCursor.SetTemporaryCursor(const ACursor: TCursor = crHourGlass): ITemporaryCursor;
begin
  Result := TTemporaryCursor.Create(ACursor);
end;

end.

You use it like this:

procedure TTemporaryCursorMainForm.TemporaryCursorButtonClick(Sender: TObject);
begin
  TTemporaryCursor.SetTemporaryCursor();
  Sleep(3000); // sleep 3 seconds with the crHourGlass cursor
  // Delphi will automatically restore the cursor
end;

What you see is no try … finally … end at all. That makes the code simpler to read and maintain.

The next post will introduce a memento class that allows you to perform a restore operation at the end of any method.

Basically the whole idea of Smart Pointers (see the series articles by Barry Kelly; note Smart Pointers will be in Spring4D 1.2)

–jeroen

13 Responses to “Delphi: using IInterface to restore cursor at end of mehod (prelude to a memento that executes any code at end of method).”

  1. […] [WayBack] Delphi: using IInterface to restore cursor at end of mehod (prelude to a memento that executes any code at end of method). […]

  2. jpluimers said

    A. Bouchez shared a nice link with a similar construct. He named it TAutoFree/IAutoFree: https://plus.google.com/u/0/+ABouchez/posts/RK8BnQgCfQT

  3. EMB said

    Beside my inner nerd like and admire how clever this is, I’m not sure that don’t having a destroy to memento class is a good thing. Maybe I’m just not used to it, but I don’t find it really clearer than having an explicit destroy.

    • jpluimers said

      (:
      There are enough places I still use try/finally, but for some patterns, this “auto-free” makes things a lot more readable.

  4. […] Delphi: using IInterface to restore cursor at end of mehod (prelude to a memento that executes any c… […]

  5. Sven said

    Please note that this feature relies on an undocumented implementation detail of Delphi: In Delphi the reference count of the ITemporaryCursor result will be decreased at the end of the method, but it is nowhere documented that this must be at the end! The compiler would be free to decrease the reference count directly after the method call (and before the Sleep() in your example) and for example Free Pascal indeed does so, because there’s no need to keep the interface around any longer. It could also be that the new NextGen compiler handles this differently as well (untested).

    Regards,
    Sven

  6. […] David Moorhouse on Delphi: using IInterface to re… […]

  7. David Moorhouse said

    Nice work. I wrote two functions, PushCursor and PopCursor, way back in Delphi 1 days to achieve the same end effect, but it had to be called within a try / finally code block.
    Good to see how modern language features simplify our code.

  8. I use similar techniques in the following videos
    Escaping the Try…Finally…Free Nest
    http://learndelphi.tv/index.php?option=com_content&view=article&id=180
    and
    Mouse Cursors in FireMonkey
    http://learndelphi.tv/index.php?option=com_content&view=article&id=184

    I think a good knowledge of Interfaces interfaces can save you a lot of coding, as well as improve readability.

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

 
%d bloggers like this: