marco
cantu

The Fun SideSite of Delphi


The Fun Side of Delphi

Borland Confrence 1997

Delphi programming is fun. The environment is easy to use and is quite powerful, and programmers enjoy using it. The more you use Delphi, the more you find out that there are ways to configure it, and perform a number of strange actions. Beside writing programs you can use Delphi to extend its own environment with components and experts.

The real fun (if we can say so) is when you spend time to do useless things, otherwise writing programs can be considered as a work. Although there is some effort involved, you can really have a lot of fun in Delphi.

This paper describes a number of ways to loose time and have fun in Delphi, writing components, stretching Delphi and Windows to the limit, and configuring the environment with Experts and other tools. Being a "fun" presentation, some multimedia will be involved, of course.

Useless Components

If you program in Delphi you probably know about Smiley. It is one of the first Delphi components ever developed, and it shows a smiling face. Actually it is a great tool to show how to build components (some of the components discussed in this paper can actually provide similar hints, as well).

We want to built a component, but how do we build one? Please refer to a specific session, article, or book, to discover everything about writing components. For this presentation you only need to know that a component is a subclass of class TComponent (or one of its subclasses), that there are three kinds of components (non-visual components, window-based components, and graphical components), and that components have methods, properties, and events.

Instead of discussing components in general, I prefer showing you how to build some useless ones (in this section) and some very strange ones (in the next section). For the moment, let me focus on how you can make a lot of work to obtain very little, but still have some fun in the process (and in the result).

The Nothing Component

The first component is probably the less useful component you can build: it does nothing. Luckily it takes very little coding to implement it. The nothing component (of class TNothing) is a graphical component, having no output, and only inherited properties and events. I can see no use for it, which is why it is the first of the list.

Still, we have to write some code. In fact if we want our component to have standard properties and events we have to list them:

type
  TNothing = class(TGraphicControl)
  public
    constructor Create (Owner: TComponent); override;
  published
    property Width default 50;
    property Height default 50;
    property Align;
    property ShowHint;
    property Visible;
    ...
  end;
We also need to write the code of the Create constructor of the component (which sets the default values) and the Register procedure:
constructor TNothing.Create (Owner: TComponent);
begin
  // call parent class constructor first
  inherited Create (Owner);
  // set the size
  Width := 50;
  Height := 50;
end;

procedure Register;
begin
  RegisterComponents('DDHB', [TNothing]);
end;

The Auto-Pressing Button

This component falls is a slightly different category. It is a component for the lazy users, a button you don't have to click to have an OnClick event. You only need to drag the mouse over it. In the best tradition of wasting time, this components requires a lot of work, because we need to handle the mouse, capture it, and perform other complex tasks. It is really a lot of work, but the result is well worth it!

I've actually written two versions of theis component. The simplest version redefines a Windows message, with the following code, in which the mouse move message handler looks for and eventually calls the OnClick event handler:

type
  TAutoButton1 = class(TButton)
  private
    procedure WmMouseMove (var Msg: TMessage);
      message wm_MouseMove;
  end;

procedure TAutoButton1.WmMouseMove (var Msg: TMessage);
begin
  inherited;
  if Assigned (OnClick) then
    OnClick (self);
end;
The second version has much more code, since I try to repeat the mouse OnClick event when the user moves the mouse over the button or after a given amount of time. Here is the declaration of the class:
type
  TAutoKind = (akTime, akMovement, akBoth);

  TAutoButton2 = class(TButton)
  private
    FAutoKind: TAutoKind;
    FMovements: Integer;
    FSeconds: Integer;
    // really private
    CurrMov: Integer;
    Capture: Boolean;
    MyTimer: TTimer;
    procedure EndCapture;
    // message handlers
    procedure WmMouseMove (var Msg: TWMMouse);
      message wm_MouseMove;
    procedure TimerProc (Sender: TObject);
    procedure WmLBUttonDown (var Msg: TMessage);
      message wm_LBUttonDown;
    procedure WmLButtonUp (var Msg: TMessage);
      message wm_LButtonUp;
  public
    constructor Create (AOwner: TComponent); override;
  published
    property AutoKind: TAutoKind
      read FAutoKind write FAutoKind default akTime;
    property Movements: Integer
      read FMovements write FMovements default 5;
    property Seconds: Integer
      read FSeconds write FSeconds default 10;
  end;
The code is quite complex, and we don't have time to cover the details. Basically when a user moves the mouse over the area of the button (WmMouseMove) the component starts a timer or counts the move messages. After a given amount of time, or when the proper number of move messages has been reached, the component simulates the mouse click event. The plain OnClick events do not work properly, but I decided I don't care...
procedure TAutoButton2.WmMouseMove (var Msg: TWMMouse);
begin
  inherited;
  if not Capture then
  begin
    SetCapture (Handle);
    Capture := True;
    CurrMov := 0;
    if FAutoKind <> akMovement then
    begin
      MyTimer := TTimer.Create (Parent);
      if FSeconds <> 0 then
        MyTimer.Interval := 3000
      else
        MyTimer.Interval := FSeconds * 1000;
      MyTimer.OnTimer := TimerProc;
      MyTimer.Enabled := True;
    end;
  end
  else // capture
  begin
    if (Msg.XPos > 0) and (Msg.XPos < Width)
      and (Msg.YPos > 0) and (Msg.YPos < Height) then
    begin
      // if we have to consider movement...
      if FAutoKind <> akTime then
      begin
        Inc (CurrMov);
        if CurrMov >= FMovements then
        begin
          if Assigned (OnClick) then
            OnClick (self);
          EndCapture;
        end;
      end;
    end
    else // out of the area... stop!
      EndCapture;
  end;
end;

procedure TAutoButton2.EndCapture;
begin
  Capture := False;
  ReleaseCapture;
  if Assigned (MyTimer) then
  begin
    MyTimer.Enabled := False;
    MyTimer.Free;
    MyTimer := nil;
  end;
end;

procedure TAutoButton2.TimerProc (Sender: TObject);
begin
  if Assigned (OnClick) then
    OnClick (self);
  EndCapture;
end;

procedure TAutoButton2.WmLBUttonDown (var Msg: TMessage);
begin
  if not Capture then
    inherited;
end;

procedure TAutoButton2.WmLButtonUp (var Msg: TMessage);
begin
  if not Capture then
    inherited;
end;

The Input Label component

This component falls in a different category, reinventing the wheel (or reinventing hot water, as we say in Italy). Many Delphi programmers ask me how user can input text in a label, and the obvious reply is, "use an edit box instead."

If you really want to get rid of edit boxes, here comes the solution: a label input components, a label component that can get the user input. This is an overly complex component, because labels have no way to get the input from the keyboard. They are graphical components, not based on a window, so they cannot receive the input focus, and they cannot get text. For this reason I've developed this example in two steps.

First step is an input-button component (quite simple) to show you the input code:

type
  TInputButton = class(TButton)
  private
    procedure WmChar (var Msg: TWMChar);
      message wm_Char;
  end;

procedure TInputButton.WmChar (var Msg: TWMChar);
var
  Temp: String;
begin
  if Char (Msg.CharCode) = #8 then
  begin
    Temp := Caption;
    Delete (Temp, Length (Temp), 1);
    Caption := Temp;
  end
  else
    Caption := Caption + Char (Msg.CharCode);
end;
The input label, instead, has to do a number of tricks to bypass the problems related to its internal structure. Basically the problem can be solved by creating other hidden components (why not an edit box?) at runtime. Here is the declaration of the class:
type
  TInputLabel = class (TLabel)
  private
    MyEdit: TEdit;
    procedure WMLButtonDown (var Msg: TMessage);
      message wm_LButtonDown;
  protected
    procedure EditChange (Sender: TObject);
    procedure EditExit (Sender: TObject);
  public
    constructor Create (AOwner: TComponent); override;
  end;
When the label is created it generates the edit box, and set some event handler for it. In fact as the user clicks on the label the focus is moved to the (invisible) edit box, and we use its events to update the label. Notice in particular the code used to mimic the focus for the label, which is based on the DrawFocusRect API call:
constructor TInputLabel.Create (AOwner: TComponent);
begin
  inherited Create (AOwner);

  MyEdit := TEdit.Create (AOwner);
  MyEdit.Parent := AOwner as TForm;
  MyEdit.Width := 0;
  MyEdit.Height := 0;
  MyEdit.TabStop := False;
  MyEdit.OnChange := EditChange;
  MyEdit.OnExit := EditExit;
end;

procedure TInputLabel.WMLButtonDown (var Msg: TMessage);
begin
  MyEdit.SetFocus;
  MyEdit.Text := Caption;
  (Owner as TForm).Canvas.DrawFocusRect (BoundsRect);
end;

procedure TInputLabel.EditChange (Sender: TObject);
begin
  Caption := MyEdit.Text;
  Invalidate;
  Update;
  (Owner as TForm).Canvas.DrawFocusRect (BoundsRect);
end;

procedure TInputLabel.EditExit (Sender: TObject);
begin
  (Owner as TForm).Invalidate;
end;

The Sound Button

When you press a button you see the 3 dimensional effect of the button being pressed. What about adding a fourth dimension, sound? Of course we need a press sound and a release sound for the button. We might eventually extend the example adding a spoken hint, with a voice reading to the user the hint of the button, but this might even be useful, so I don't want to cover it.

The sound button component has two brand new properties:

type
  TDdhSoundButton = class(TButton)
  private
    FSoundUp, FSoundDown: string;
  protected
    procedure MouseDown(Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer); override;
    procedure MouseUp(Button: TMouseButton;
      Shift: TShiftState; X, Y: Integer); override;
  published
    property SoundUp: string
      read FSoundUp write FSoundUp;
    property SoundDown: string
      read FSoundDown write FSoundDown;
  end;
These sounds are played when a button is pressed or realeased:
procedure TDdhSoundButton.MouseDown(
  Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  PlaySound (PChar (FSoundDown), 0, snd_Async);
end;

procedure TDdhSoundButton.MouseUp(Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
begin
  inherited;
  PlaySound (PChar (FSoundUp), 0, snd_Async);
end;

An Animated Button: TAniButton

This is probably a slightly more useful component, but building and using it is a lot of fun, so it really pertains to the presentation. To show graphics inside a button you can might think of using the glyph of a Bitmap Button, but this doesn't work, mainly because changing the image causes the whole button to refresh, with a nasty effect. An alternative is to place a paint area over the button and use TCanvas methods to paint over it, and change the image at runtime.

To provide the images to the animated button, I've decide to rely on the ImageList component, which allows you to place many bitmaps in a single container. Each of the bitmaps will be displayed after the previous one, providing animated effects. The code is quite long, and is available for reference in the companion source code, but it is not in the paper.

Strange Components

After the first group, the useless components, comes a second one, including very awkward ones. Again, there is little reason to use this components in a professional application, but they can be fun.

The Auto-Font-Changer Component

Programmers can be very good at using too many fonts in a form, making it look weird. But nothing can beat a component that automatically changes the font of the components on a form at run-time, without even bothering to ask. The TAutoFont component even provides two different approaches: it can use random fonts, or allow more control to the program. Some of the versions do not work really very well, because of the many font-compatibility problems of Windows, but the effect is nice anyway.

This is the class definition:

type
  TAutoFont = class(TComponent)
  private
    FTimer: TTimer;
    FInterval: Cardinal;
    FFixedSize, FAllAlike: Boolean;
  protected
    procedure OnTimer (Sender: TObject);
    procedure SetInterval (Value: Cardinal);
  public
    constructor Create (AOwner: TComponent); override;
  published
    property Interval: Cardinal
      read FInterval write SetInterval default 10000;
    property FixedSize: Boolean
      read FFixedSize write FFixedSize default True;
    property AllAlike: Boolean
      read FAllAlike write FAllAlike default True;
  end;
The only relevant method of the class is the OnTimer event handler, which includes the font changing code:
procedure TAutoFont.OnTimer (Sender: TObject);
var
  I: Integer;
  Fnt: TFont;
begin
  (Owner as TForm).Font.Name :=
    Screen.Fonts [Random (Screen.Fonts.Count)];
  if not FFixedSize then
    (Owner as TForm).Font.Size := Random (36);
  if not FAllAlike then
  begin
    Fnt := TFont.Create;
    Fnt.Assign ((Owner as TForm).Font);
    for I := 0 to Owner.ComponentCount - 1 do
    begin
      Fnt.Name := 
	    Screen.Fonts [Random (Screen.Fonts.Count)];
      if Owner.Components [I] is TWinControl then
        SendMessage (
		  TWinControl (Owner.Components [I]).Handle,
          wm_SetFont, Fnt.Handle, MakeLong (1,0));
    end;
    Fnt.Free;
  end;
end;

The Smart-Closing Component

When you close a form, it simply disappears. Beside hiding forms, there are many other approaches to closing them. I don't intend to discuss the Action property of the OnClose event, but simply show you how to write a button used to close a form slowly by shrinking it to a minimal size. This is not the only possible closing approach, but one of the simplest ones, thanks to the Scale method of forms:
type
  TSmartClose = class(TComponent)
  public
    procedure Close;
  end;

procedure TSmartClose.Close;
begin
  (Owner as TForm).AutoScroll := False;
  repeat
    (Owner as TForm).ScaleBy (93, 100);
    Application.ProcessMessages;
  until (Owner As TForm).Height < 50;
  (Owner as TForm).Close;
end;

The Screen-Virus Component

Never seen a screen virus? It is an illness of the screen of the computer that causes red spots to appear. The same virus can attack forms and windows, and is really fun to write and use it. The only question is how to prevent the virus to spread out of the screen. The code? The key is painting on the screen, after creating a device context with the GetWindowDC API function.

Again the most relevant portion of the code is in the OnTimer event handler:

type
  TScreenVirus = class(TComponent)
  private
    FTimer: TTimer;
    FInterval: Cardinal;
    FColor: TColor;
    FRadius: Integer;
  protected
    procedure OnTimer (Sender: TObject);
    procedure SetInterval (Value: Cardinal);
  public
    constructor Create (AOwner: TComponent); override;
    procedure StartInfection;
  published
    property Interval: Cardinal
      read FInterval write SetInterval;
    property Color: TColor
      read FColor write FColor default clRed;
    property Radius: Integer
      read FRadius write FRadius default 10;
  end;

constructor TScreenVirus.Create (AOwner: TComponent);
begin
  inherited Create (AOwner);
  FTimer := TTimer.Create (Owner);
  FInterval := FTimer.Interval;
  FTimer.Enabled := False;
  FTimer.OnTimer := OnTimer;
  FColor := clRed;
  FRadius := 10;
end;

procedure TScreenVirus.StartInfection;
begin
  if Assigned (FTimer) then
    FTimer.Enabled := True;
end;

procedure TScreenVirus.SetInterval (Value: Cardinal);
begin
  if Value <> FInterval then
  begin
    FInterval := Value;
    FTimer.Interval := Interval;
  end;
end;

procedure TScreenVirus.OnTimer (Sender: TObject);
var
  hdcDesk: THandle;
  Brush: TBrush;
  X, Y: Integer;
begin
  hdcDesk := GetWindowDC (GetDesktopWindow);
  Brush := TBrush.Create;
  Brush.Color := FColor;
  SelectObject (hdcDesk, Brush.Handle);
  X := Random (Screen.Width);
  Y := Random (Screen.Height);
  Ellipse (hdcDesk, X - FRadius, Y - FRadius,
    X + FRadius, Y + FRadius);
  ReleaseDC (hdcDesk, GetDesktopWindow);
  Brush.Free;
end;

Marketing Components

Programmers are often not very good at marketing, and here I've no intention to offer real advice. I've just noticed two trends: one is adding copyright notices everywhere, so everyone realizes who built that nice component, the other is to bother the user so much that he's going to stop using your component altogether (or eventually spend the money to get rid of messages. This is more common in the demo/trial versions, also called "before-you-buy-'cause-you're-going-to-buy-this-component" versions).

Copyright

There are hundreds of ways to add a copyright notice to a component. Here is a limited list of features you can implement sorted by frequence/oddness:

Remember you shall buy

When the component is free, but you should register it, the component writer generally tries to remind you he's waiting for the money. The contents of this screen can be more or less friendly, and generally refers to an order form in the help file or a separate file. The real question is, when do you show this screen? A common approach is when the component is created, usually as the program starts. This can be have a drawback if the program has many copies of the component. An alternative is to use a timer to show this screen every x seconds. Also, some programmers like to address more the programmer then the end user, showing the how-to-buy screen only at desing time (when the csDesigning is set in the ComponentState property).

An actual example

Beside presenting ideas about adding copyright notices, I've actually written a component which implements most of these features. The TFunCopyright component implements: dummy properties you cannot change, showing boring messages, changing the caption of the form and changing the title of the application (at design time only), showing copyright information in an external component, a label:
type
  TFunCopyright = class(TComponent)
  private
    FCopyright, FAuthor: string;
    FDummy1, FDummy2: string;
    FLabel: TLabel;
  protected
    procedure SetLabel (Value: TLabel);
  public
    constructor Create (AOwner: TComponent); override;
  published
    property Copyright: string
      read FCopyright write FDummy1;
    property Author: string
      read FAuthor write FDummy2;
    property OutputLabel: TLabel
      read FLabel write SetLabel;
end;

constructor TFunCopyright.Create (AOwner: TComponent);
begin
  inherited Create (AOwner);

  FAuthor := 'Marco Cantù';
  FCopyright := '(c)MC 1997';
  if csDesigning in ComponentState then
  begin
    with Owner as TForm do
      Caption := Caption +
        ' using a component by ' + FAuthor;
    with Application do
      Title := Title +
      ' using a component by ' + FAuthor;
    ShowMessage ('This form is using a component by ' +
      FAuthor);
  end
  else
    ShowMessage ('This program uses a component by ' +
      FAuthor);
end;

procedure TFunCopyright.SetLabel (Value: TLabel);
begin
  if Value <> FLabel then
  begin
    FLabel := Value;
    FLabel.Caption := FCopyright;
  end;
end;

Configuring the Object Inspector

Having written useless or crazy components, we can move to a different topic, writing overly complex and not much useful property editors, to customize the Object Inspector. Again here is a list of awful ideas you can spend a lot of time working on.

An Integer Spin-Editor

Why punch in numbers in the Object Inspector? It will be easier to increase or decrease values using an UpDown component, and we can indeed write a separate property editor using it. At the end, it will take for more time to input numbers, but it will eventually be easier. But how do you create a property editor? Basically derive a class from an existing property editor class, overriding some virtual functions:
type
  TSpecialIntProperty = class (TIntegerProperty)
  public
    function GetAttributes: TPropertyAttributes; 
	  override;
    procedure Edit; override;
  end;
The important method is Edit, which is often used to show a dialog box (built in Delphi, as usual):
function TSpecialIntProperty.GetAttributes:
  TPropertyAttributes;
begin
  Result := [paDialog, paReadOnly];
end;

procedure TSpecialIntProperty.Edit;
var
  PEForm: TSpinForm;
begin
  PEForm := TSpinForm.Create (Application);
  try
    PEForm.Edit1.Text := GetValue;
    if PEForm.ShowModal = mrOK then
      SetValue (PEForm.Edit1.Text);
  finally
    PEForm.Free;
  end;
end;
In this code GetValue and SetValue are two special methods of the parent property editor, accessing to the data of the given property of the current component. To make this work you have to write also a proper registration procedure:
procedure Register;
begin
  RegisterPropertyEditor (TypeInfo(Integer),
    TButton, '', TSpecialIntProperty);
end; 

A Sounds Property Editor

Another really useful property editor, instead, is a sound editor related to the Sound Button component we've discussed before. This lets you associate a sound name with given properties, and test it with a preview option, and look for a suitable file on disk. The code is quite simple but this example is actually useful, so I'm going to skip its complete description. What is interesting, in this case is the registration code (which is repeated for the two sound properties of the component):
RegisterPropertyEditor (TypeInfo(string),
    TSoundButton, 'SoundUp', TSoundProperty);

A Custom Color Property Editor

Colors are so nice to work with, and the standard color dialog box becomes boring after a while: for this reason I've explored many alternatives to replace it. What is nice about this third property editor, is that it automatically changes every time you use it. Here is its complete course code:
type
  TMyColorProperty = class (TColorProperty)
  public
    procedure Edit; override;
  end;

procedure Register;

implementation

var
  nEditor: Integer;

procedure TMyColorProperty.Edit;
begin
  try
    case nEditor of
      0: begin
        FormColor1 := TFormColor1.Create (Application);
        ...
      1: begin
        FormColor2 := TFormColor2.Create (Application);
        ...
      2: inherited Edit;
    end;
  finally
    nEditor := (nEditor + 1) mod 3;
  end;
end;

procedure Register;
begin
  RegisterPropertyEditor (TypeInfo(TColor),
    TComponent, '', TMyColorProperty);
end;

initialization
  nEditor := 0;
end.

Stupid Experts, pardon Wizards

Building components is easy. Building other Delphi tools requires some more work, but can still be fun. I've built two useless wizards (previously known as Experts, and now better know as Expert Wizards). First of all, how to you build an expert wizard? As for any other Delphi add-on tool you have an abstract class with a virtual interface (an interface with many abstract virtual methods) to override in your subclass. Here is my simple Expert Wizard.

The Blank Expert Wizard

First is something I use, although it is really a weird Expert Wizard: the Blank Expert Wizard. When you start a new project it is created with an empty form. The Blank Expert Wizard allows you to do the same, but also to create a project with no form. And it prompts for a directory immediately, instead of asking for one only when the files are saved to disk.

This is actually an excuse to see how an expert is built. First derive a new class, with a bunch of overridden methods (required since they are virtual abstract):

type
  TBlankExpert = class (TIExpert)
  public
    function GetStyle: TExpertStyle; override;
    function GetName: string; override;
    function GetComment: string; override;
    function GetGlyph: HBITMAP; override;
    function GetState: TExpertState; override;
    function GetIDString: string; override;
    function GetMenuText: string; override;
    procedure Execute; override;
  end;
Most of the methods have empty or default code. The only real code is in the Execute method:
  function TBlankExpert.GetStyle: TExpertStyle;
  begin
    Result := esStandard;
  end;

  function TBlankExpert.GetName: String;
  begin
    Result := 'Blank Expert'
  end;

  function TBlankExpert.GetComment: String;
  begin
    Result := '';  // no thanks
  end;

  function TBlankExpert.GetGlyph: HBITMAP;
  begin
    Result := 0;  // no thanks
  end;

  function TBlankExpert.GetState: TExpertState;
  begin
    Result := [esEnabled];
  end;

  function TBlankExpert.GetIDString: String;
  begin
    Result := 'MarcoCantu.BlankExpert'
  end;

  function TBlankExpert.GetMenuText: String;
  begin
    Result := '&Blank Expert...'
  end;

  procedure TBlankExpert.Execute;
  var
    DirName: string;
  begin
    if MessageDlg ('Are you sure you want to exit'#13 +
      'from the current project, saving it?',
      mtConfirmation, [mbYes, mbNo], 0) = mrYes then
    begin
      ToolServices.SaveProject;
      ToolServices.CloseProject;
      SelectDirectory (DirName,
        [sdAllowCreate, sdPerformCreate, sdPrompt], 0);
      ToolServices.OpenProject (DirName + '\Project1.dpr');
    end;
  end;

Windows 95 Fun

Needless to say the best fun in Delphi 2 is Windows 95 programming. There are so many funny and useless things you can do in Windows that it is difficult to find a starting point. Not having much space/time left, I've decided to focus on a couple of topics, which can be generally indicated as "breaking Windows" and "stumping the users".

Breaking Windows

How can you do this? Here is a list: create too many windows, create too many components, use too much memory, waste too many resources (such as pens), access nil pointers, use the new Delphi 2 long strings in wrong ways, produce a stack overflown by calling an endless recursive function. I demonstrate all these techniques in the WINCRASH program, which has a big problem, I might have to reboot the system to show you all of its features. (Attention: save your files before running it, because it can really crash the system!)

The code of this example is fairly simple: Just write several for loops in which you allocate resources forever. Here are two methods:

procedure TForm1.ButtonWindowsClick(Sender: TObject);
var
  NewForm: TForm;
  Hwnd: THandle;
  I: Integer;
begin
  NewForm := TForm.Create (Application);
  NewForm.Show;
  NewForm.Update;

  // create a number of windows...
  try
    for I := 1 to 1000000 do
    begin
      Hwnd := CreateWindow ('button', 'Button',
        ws_child or ws_border or bs_pushbutton,
        I mod (ClientWidth - 40),
        I mod (ClientHeight - 20),
        40, 20,
        Handle, 0, HInstance, nil);
      if Hwnd = 0 then
        raise Exception.Create ('Out of handles');
      if (I mod 20) = 0 then
        NewForm.Caption := 'Created: ' +
          IntToStr (I);
      Application.ProcessMessages;
    end;
  finally
    ButtonWindows.Caption := Format ('Created: %d', [I]);
    NewForm.Free;
  end;
end;

procedure TForm1.ButtonPensClick(Sender: TObject);
var
  H: THandle;
  I: Integer;
begin
  try
    for I := 1 to 1000000 do
    begin
      H := CreatePen (ps_solid, 1, RGB (0, 0, 0));
      if H = 0 then
        raise Exception.Create ('Out of handles');
      if (I mod 20) = 0 then
        ButtonPens.Caption := Format ('Created: %d', [I]);
      Application.ProcessMessages;
    end;
  finally
    ButtonPens.Caption := Format ('Created: %d', [I]);
  end;
end;

Stump the User

The second, and probably even best category of Windows fun programs is the "stump the user" groups. Beside the screen-virus most users won't probably be scared by, there are some nice techniques to stump users: transparent windows (a feature I consider a Windows bug), unreachable menu item, with too many sub-levels for the screen, and fake GPFaults and unrecoverable error messages.

This last trick is explored by the UAE example. You can show a simple UAE message box, build a full fledged dialog box, with the details sub window, and even make a close button which doesn't want to be pressed.

The fake error form has a details button that shows open the second part of the form. This is accomplished by adding components out of the surface of the form itself, as you can see in its textual description:

object Form2: TForm2
  AutoScroll = False
  Caption = 'Error'
  ClientHeight = 93
  ClientWidth = 320
  OnShow = FormShow
  object Label1: TLabel
    Left = 56
    Top = 16
    Width = 172
    Height = 65
    AutoSize = False
    Caption = 
      'The program has performed an illegal ' +
	  'operation. If the problem' +
      'persist contact the software vendor.'
    WordWrap = True
  end
  object Image1: TImage
    Left = 8
    Top = 16
    Width = 41
    Height = 41
    Picture.Data = {...}
  end
  object Button1: TButton
    Left = 240
    Top = 16
    Width = 75
    Height = 25
    Caption = 'Close'
    TabOrder = 0
    OnClick = Button1Click
  end
  object Button2: TButton
    Left = 240
    Top = 56
    Width = 75
    Height = 25
    Caption = 'Details >>'
    TabOrder = 1
    OnClick = Button2Click
  end
  object Memo1: TMemo // out of the form!
    Left = 24
    Top = 104
    Width = 265
    Height = 89
    Color = clBtnFace
    Lines.Strings = (
      'AX:BX    73A5:495B'
      'SX:PK    676F:FFFF'
      'OH:OH   7645:2347'
      'Crash    3485:9874'
      ''
      'What'#39's going on here?')
    TabOrder = 2
  end
end
When a user presses the details button the program simply update the size of the form:
procedure TForm2.Button2Click(Sender: TObject);
begin
  Height := 231;
end;
A second form, which inherits from the first one, has an extra trick, a moving close button:
procedure TForm3.Button1Click(Sender: TObject);
begin
  Button1.Left := Random (ClientWidth - Button1.Width);
  Button1.Top := Random (ClientHeight - Button1.Height);
end;
Finally, you can create a hole in a window by using the SetWindowRgn Win32 API function. This can really make users scream:
procedure TForm1.Button4Click(Sender: TObject);
var
  HRegion1, Hreg2, Hreg3: THandle;
  Col: TColor;
begin
  ShowMessage ('Ready for a real crash?');
  Col := Color;
  Color := clRed;
  PlaySound ('boom.wav', 0, snd_sync);
  HRegion1 := CreatePolygonRgn (Pts,
    sizeof (Pts) div 8,
    alternate);
  SetWindowRgn (
    Handle, HRegion1, True);
  ShowMessage ('Now, what have you done?');
  Color := Col;
  ShowMessage ('You should better buy a new monitor');
end;

Conclusion

As I mentioned fun Windows 95 programming is such a wide topic that there is no way to cover it in a presentation devoted to loosing time with Delphi. There are already so many other Delphi components you can write wasting your time, and other Experts we can add to the environment with no real benefit. Then there are do-nothing version control system and many other Delhi extensions available in the Open Tools API. So, do not worry: There are thousands of ways to have fun trying t write useless Delphi programs, components, and other tools. It might even happen that someone finds a use for something you have written. The result is that you might get some money out of it, having more time free to have fun.

Author

Marco Cantù is a freelance writer and consultant. He is the author of Mastering Delphi 3 (SYBEX), the forthcoming Delphi Developer's Guide, and a couple of C++ books. He is a frequent contributor to several programming magazines. He lives in Italy, and is the founder of WinTech Italia, a training and consulting company focused on Windows and Delphi.