The Wiert Corner – irregular stream of stuff

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

  • My badges

  • Twitter Updates

  • My Flickr Stream

  • Pages

  • All categories

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

    Join 4,262 other subscribers

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 [WayBack] TRecall class and thought: I think my TTemporaryCursor is smarter, as it is based on interfaces.

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

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

My [WayBack] TTemporaryCursor (now [WayBack] at bitbucket) class only stores an integer, so it cannot derive from TRecall. Besides it is based on IInterface which got introduced in Delphi 6, but was present as IUnknown since Delphi 3 (see [WayBack] Interface It! A quick guide to the ins and outs of interfaces in Delphi. By Jimmy Tharpe).

This means that TRecall could have been based on IInterface, so I wonder why it was not.

Note I’m not the first to publish about such a class (Malcolm Grooves wrote [WayBack] TCursorSnapshot, SwissDelphiCenter has [WayBack] TMyCursor, Nick Hodges published about [WayBack] 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  [WayBackanonymous method. More on that tomorrow.

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

SourceMaking.com a.k.a. [WayBack] Design Patterns and Refactoring is a great site about [WayBack] Design Patterns, [WayBack] UML, [WayBack] AntiPatterns and [WayBack] 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 [WayBack] 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 [WayBack] IInterface. When the reference goes out of scope, it calls [WayBack] _Release, and when the reference count gets to zero, the [WayBack] Destroy destructor is called. [WayBack] TMemento (now [WayBack] 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. [WayBack] Even if you do not assign it, Delphi will automatically keep the reference until the calling method ends: Delphi adds an implicit tryfinallyend block where the finallyend will [WayBack] 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 [WayBack1/WayBack2] Smart Pointers will be in Spring4D 1.2) takes the above to a new level: instead of type specific, they are generic for any type.

Edit: Spring4D implemented Smart Pointers a while ago and now names smart pointers Shared/IShared<T>/TShared<T>, see [WayBack] Spring4D – Pascal Today and the difference between the above WayBack1/WayBack2.

–jeroen

Referenced: [WayBack] Hello! There is a rather obscure and old couple of routines in the RTL that let you save things such as fonts and brushes without the need for a variab… – Andrea Raimondi – Google+

14 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. abouchez said

    This won’t work in Delphi 10.4 any more due to a compiler change about interface release scope.

    Check https://quality.embarcadero.com/browse/RSP-30050

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

  3. 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

  4. 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.

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

  6. 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

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

  8. 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.

  9. 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 comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.