Wednesday, June 15, 2011

THtmlWriter - First Look

I wanted my build process to automatically generate a web page with what was changed in the given build.   Basically I had to query our Internal Bug Tracking system and produce a web page. Allowing me to take a
snapshot of bug tracking system as it looked liked when the build was produced.

This was going to be a simple console application that our build could run.  

So I decided to try THtmlWriter written by Nick Hodges.

I spent more time reviewing the code of THtmlWriter than I did actually using it.

It was very easy to use, and will most likely use it in future

If you need to produce HTML from Delphi application it's worth looking at this.

So here is my +1 Vote for THtmlWriter

Tuesday, June 7, 2011

Application -vs- Component Developer

During Interviews I have  heard the following from candidates.
"I am Application developer, I use components, I don't build them."

I typically have thought well I have found another "point and click" developer who is not familiar with OOP.   Since I have been at my component no candidate who has said this has been hired.

Recently I ran into an exception (outside of an Interview) a Delphi Developer who was very familiar with OOP but had still not built a component. I realized that there an artificial barrier into component development, that many might see.    In a future blog postI will confront this artificial barrier head on and show how easy it is to do component development.  This post is show some of the arguments of why we would want to do component development as an application developer.

In 2007 we converted our application from BDE to DBX. We had written an application that converted all TQuery components to TDbxQuery components.  

The process of conversion to the 3 component model of DBX (TSqlQuery -> TProvider -> TClientDataset) was going to be error prone.   It was also going to take an insane amount of time that we were not willing to spend.

So we spent a few days creating a component that looked like and behaved like TQuery but internally used the 3 Component DBX model.  It was similar to TSimpleDateset but had properties and functions that matched existing  TQuery functionality that we used.  This allowed for a search and replace of instances of TQuery with TdbxQuery.   This allowed us to complete this conversion in less time that if we had just used the built in components.     Since we maintained the component we were able to expose any functionality of the 3 components model that we needed.

During this process we realized we had hundreds of  TDBGrid components.     So we decided to help our application for the long term.   We replaced TDBGrid with TDwsGrid.    Initially the code looked something like this.    It basically offered nothing over TDbGrid.

TDwsGrid = class(TCustomDBGrid)
   published
    property Align;
    property Anchors;
    property BiDiMode;
    property BorderStyle;
    property Color;
    property Columns stored False; 
    property Constraints;
    property Ctl3D;
    property DataSource;
    property DefaultDrawing;
    property DragCursor;
    property DragKind;
    property DragMode;
    property Enabled;
    property FixedColor;
    property Font;
    property ImeMode;
    property ImeName;
    property Options;
    property ParentBiDiMode;
    property ParentColor;
    property ParentCtl3D;
    property ParentFont;
    property ParentShowHint;
    property PopupMenu;
    property ReadOnly;
    property ShowHint;
    property TabOrder;
    property TabStop;
    property TitleFont;
    property Visible;
    property OnCellClick;
    property OnColEnter;
    property OnColExit;
    property OnColumnMoved;
    property OnDrawColumnCell;
    property OnDblClick;
    property OnDragDrop;
    property OnDragOver;
    property OnEditButtonClick;
    property OnEndDock;
    property OnEndDrag;
    property OnEnter;
    property OnExit;
    property OnKeyDown;
    property OnKeyPress;
    property OnKeyUp;
    property OnMouseActivate;
    property OnMouseDown;
    property OnMouseEnter;
    property OnMouseLeave;
    property OnMouseMove;
    property OnMouseUp;
    property OnMouseWheel;
    property OnMouseWheelDown;
    property OnMouseWheelUp;
    property OnStartDock;
    property OnStartDrag;
    property OnTitleClick;
  end;

The other day I had a look at this component, it now does the following.
  • Sorts the Grid based on Click of column Title
  • Persists Column positions and sizing to a configuration file.
  • Ability to Export to Excel.
It's not a huge amount functionality but it allowed us to add this to many screens without how having to place this code to do this in every place we used TDbGrid.      

When explaining this to application only developer one might expect a response of.  "Well why not buy a 3rd party control that does that?"   My answer  "We did just that, when we had the budget we purchased a TMS Subscription and now use TDBAdvGrid in many places."   

But the experience previously learned still applied to 3rd party component packages.   We now are in the processed of  creating, our new component which now looks like this:

TdwsAdvDbGrid = class(TAdvDBGrid)
end;

Allowing us to add additional functionality to TAdvDBGrid if we need it.   Granted for now we don't need to add anything.   But the lesson learned from the previous experience has shown the value.

This does not just apply to additional functionality, it also applies to common custom settings.
Say for example every time you use a TEdit you need to change the Font to comply with a standard
you have set.   Instead of doing the tedious mistake ridden work of doing this.   Instead create a TMyEdit then use that instead of TEdit.  

interface

uses
  SysUtils, Classes, Controls, StdCtrls;

type
  TMyEdit = class(TEdit)
  public
    constructor Create(AOwner : TComponent); override;
  end;

procedure Register;

implementation
{ TMyEdit }

constructor TMyEdit.Create(AOwner: TComponent);
begin
  inherited;
  Font.Name := 'My Crazy Font';
end;


So basically a developer should be able to do both, application and component development.    Taking the time to learn component development will only help you in your your abilities to create great applications.

Thursday, June 2, 2011

Runtime/Designtime what? Delphi Packages

To most Delphi developers a package is typically where you would place components so that you can drop them on your forms.

So one might assume that a 3rd Party Component developer would know how packages work.    Well over the years I have seen several of these developers make basic mistakes when it comes to Delphi packages.

The application I work on is built with runtime packages.  We currently have to deploy 208 packages.
Management of Packages and having the built correctly is paramount for us.    Not all packages involve components, but the most common mistakes happen at that level.

This blog post is my attempt to help developers understand one area of packages, that I typically see mistakes.

A package is just a collection of units that are complied together into a BPL.

Here is the package sources for brand new package with a single unit.
package Package2;             

{$R *.res}
{$ALIGN 8}
{$ASSERTIONS ON}
{$BOOLEVAL OFF}
{$DEBUGINFO ON}
{$EXTENDEDSYNTAX ON}
{$IMPORTEDDATA ON}
{$IOCHECKS ON}
{$LOCALSYMBOLS ON}
{$LONGSTRINGS ON}
{$OPENSTRINGS ON}
{$OPTIMIZATION ON}
{$OVERFLOWCHECKS OFF}
{$RANGECHECKS OFF}
{$REFERENCEINFO ON}
{$SAFEDIVIDE OFF}
{$STACKFRAMES OFF}
{$TYPEDADDRESS OFF}
{$VARSTRINGCHECKS ON}
{$WRITEABLECONST OFF}
{$MINENUMSIZE 1}
{$IMAGEBASE $400000}
{$IMPLICITBUILD ON}

requires
  rtl;

contains
  Unit13 in 'Unit13.pas';

end.

There are two key sections requires and contains.

Contains is the list of units that your package contains.     Requires is the list of other packages your package needs  to compile.   For example say you have a  pkgBaseBall.bpl that contains a unit called baseball.pas.
Then you create a new packages called pkgSports.bpl that contains a unit called sports.pas which listed baseball.pas in it's uses clause.    You will need to add pkgBaseBall to the requires clause the package named pkgSports.bpl


Key Files involved in package development.
  • *.DPROJ - XML File in MSBUILD format that contains project options and files.
  • *.DPK - Main source code of the package.   (It's the code listed above)
  • *.BPL - Compiled Binary 
  • *.DCP - Delphi Compiled Package, contains information about he interface section of the contained units. 



Package Types
There are 3 basic types of  Packages.

  • Designtime only
  • Runtime only
  • Designtime and runtime 




Runtime Packages

When creating a Delphi you have the option to build with runtime packages.   This option is found in the project options.

If you build with packages you must distribute the packages (.BPL files) your application uses.


Most Delphi application that I have seen are build without runtime packages.    Typically these developers tend to not realize the impact bad package design can cause on application that uses runtime packages.


When building with runtime packages the list of package you must distribute is semi-colon delimited list next to the check box for Build with run time packages in the project options.     The only files you need to distribute for applications build with runtime packages is the BPLs.    The other key package files do not need to be distributed.


Runtime packages should never contain or require designtime code.




Designtime Packages

Designtime packages are where you place your design time code.   This includes 
  • Property Editors
  • Component Editors
  • Open Tool Experts
  • References to other packages that are design time.
  • Component Registration "i.e. the Register method."
Designtime packages should never contain runtime code.

Designtime and Runtime

This is the lazy developer package.   I don't want to create and maintain two packages, so I will just use one. This seems easy at first, but if not done carefully it leads to the most common mistake I run into.    Then write some code  that requires a designtime only package.    This in effect makes your package designtime only regardless of your package type selection.      It's easy to do any property or component editor will require DesignIDE which is design time package that you can't distribute.

The Rules 
The summary version of common mistakes.

  1. "Runtime" Packages should never contain Design time Code
  2. "Designtime" Packages should never contain Runtime Code.
  3. "Designtime and Runtime" Packages should never Require "Designtime"  Packages.  
  4. "Runtime" Packages should never Require "Designtime"  Packages.  
  5. Avoid "Runtime" Packages that  require "Designtime and Runtime" Packages
  6. Avoid the use of "Designtime and Runtime" Packages.

Why do I care?

If an application builds with runtime packages, and someone screws this up you will find that application requiring deployment of design time packages to work. If you do deploy the runtime package, you may run into a a runtime error "Application is not licensed to use this feature"  This is because your using files that you should not be distributing.