Posts Tagged ‘Delphi’

hcOPF - Now Supports Design-Time DevExpress Grid Bindings

Monday, March 22nd, 2010

hcOPF is continually being improved as I use it on different projects, and other contributors send me code.  I posted a while ago on the design-time binding for attributes that I added.  Since then, I’ve had a requirement that the current DevExpress grid bindings could not satisfy.

The client required the same grid to be shown on 2 different forms in the application.  You wouldn’t think this would be a problem, but the grid bindings are a global registry keyed on the ObjectClass and ListClass passed to the BindViewToList() method.  You could certainly bind more than one grid to the same list, but when the ThcGridObjectList (or any other cxGrid aware list) instance attempted to locate the TcxGridTableView to which it was bound, it would only find the first view binding registered.  After contemplating extending the grid binding registry key employed to identify the binding, I decided rather than trying to hack a solution for now, it would be best to implement design-time binding.

Ultimately, I would have liked to enabled design-time data binding capabilities to the DevExpress component editor so developers could select an object attribute in the data binding the same way they select a database field in a data bound view.  I’m not even sure if that approach is feasible, and it certainly would not have been consistent with the design-time approach for ThcObject attributes.  Sometimes coding is as much an exploration as a discipline, but I needed to limit the possibility of a throw away design both because I needed to get on with the project work, and design-time coding can be very time consuming since you just can’t step through it in the debugger.  CodeSite is a very useful tool, but you still have to be looking in the right place and sending sufficient information to figure out what is going on.

What I decided to implement was a new ThcMediator descendant that would mediate between a ThcObjectList or descendant and a TcxGridTableView.  Currently, only table views are supported, but it shouldn’t take much to support other view types as well.  I re-factored the ThcMediator, creating an abstract class; ThcMediator and two descendants; ThcAttributeMediator and ThcListMediator.  Then I created a new ThcListMediator descendant (ThcTcxGridMediator) to mediate between a ThcObjectList and the selected cxGrid view.  The ThcObjectList is identified at design-time by it’s Name, which is then used at run-time to attach to the list.  Here is a screenshot depicting how you bind the list to the gridview:

The TcxGridViewMediator manages a ThcDevExpressItemBindingCollection in which you can specify the Item (normally a grid column) to object attribute pairing as shown below:

The downside is that the grid view column must first be created in the cxGrid component editor, before it can be paired with the AttributeName.

This functionality is still considered “in development” as it needs to be tested more thoroughly, but I thought there might be a few interested parties who would want to know of it sooner rather than later.  It certainly beats writing code to bind a list to a grid view, and it eliminates the need to conditionally compile any design-time business objects in order to install them into the IDE without sucking in all sorts of DevExpress code.

Check out the ObjectListMgrDemo application to see it in action (only D2010 and D7 packages are currently up to date).

Internal Errors Need better Exposure

Sunday, January 24th, 2010

Tonight I was trying to install some hcOPF objects into the D7 IDE so I could use the design-time functionality to bind UI controls to the attributes of an object.  I created a package, included all units I thought were needed and then added all implicitly imported units or the packages that contained them.  I then tried a Build and got an ‘Internal Error U1295′ at the line where I include hcDevExpress in my Implementation section.

I thought I would Google the error, and I came up with a measly 4 hits with no answers, only others asking questions, although many of the pages had to be translated by Google.  I did stumble upon an article from Steve Trefethen here.  Oddly enough, searching EDN didn’t find anything on U1295, and searching on “internal errors” brought back results for multiple products.  It seems weird that there is no product filter in the advanced search options.  Furthermore, the Additional Compiler Messages window, which I didn’t even know existed after all these years, until I read Steve’s article, gave this link which is pretty much the article Steve published (nice circular reference).

So here I am, a Delphi developer for many years pulling my hair out (what little is now left) because I have to figure out what is going on by trial and error.  I can only imagine how souring an experience this would be for a Delphi newcomer.  You would think after 15 years a product might have more comprehensive documentation, especially on it’s support site.  Perhaps a more descriptive error message might have be in order, and then additional documentation would not be necessary.  Granted I am not using using the latest Delphi release, mainly because I am performing a large scale refactoring and I’m still a CodeRush addict, and this project has a single datamodule with a large number of queries which loads much faster in D7 than D2010.

Based on Steve’s article and my own experience, I re-compiled all my hcOPF IDE packages, and after the IDE died once, and I had to kill the process, I finally got the business object package installed.  As I suspected, I am using conditional compilation to include CodeSite messages etc, and if I compile a project that does not use packages with different conditional directives or compiler options than used to compile the packages, I often get into this kind of situation (not always the same Internal Error).  It makes for sporadically painful development experience, so I would welcome any suggestions from package developers who dog food their product as it evolves.

The Cost of Progress

Sunday, January 17th, 2010

Ok I admit it.  I’m a bit of a hoarder.  I finally went through all my old paperwork and shredded everything that I didn’t need to keep for the CRA (Canada Revenue Agency).  Amidst 15 years of paperwork I came across the following:

Delphi 2 - The First 32 bit Windows Release

Delphi 2 - The First 32 bit Windows Release

I ordered and returned Delphi 1.  It was an impressive product, but it was so new no one was using it, so it was a little hard to justify the money (I was even poorer then).  When Delphi 2 shipped, Delphi became my development tool of choice and has been ever since.

Embarcadero’s current pricing at the on-line Canadian store is $922.64.  I wonder how they determined that price.  It’s not rounded to the nearest dollar, or priced so it appears below some psychological threshold.  Even after GST is applied, it’s an odd figure.

Anyway, I wondered how much Delphi 2 would have been sold for in today’s dollars, so I used 3% inflation annually, compounded on a yearly basis and it worked out to $635.29 CAD.  The current price is 1.45 times the adjusted cost of Delphi 2.

There are certainly more features in the box, but do you think it’s worth the price?

hcOPF UIObjectBinder gets DesignTime Support

Monday, January 4th, 2010

I used to have some design-time support for the principle objects in the framework, but in my experience component and property editors are non-trivial to write, and the framework was evolving so much that I was constantly breaking all the design-time code.  I decided to back burner design-time support until hcOPF was more mature.  I am happy to announce that ObjectBinding design-time support is now available.

You can now design your objects, install the objects into the IDE, and bind their attributes to form controls without writing a single line of code.  Here is a screenshot of the ObjectListMgr demo showing the design-time support.

Many thanks to Developer Express, not only for donating a license to their DevExpress QuantumGrid Suite so I can continue to support it with hcOPF, and for allowing Richard Morris to share their technique for delegating properties to child objects.  You can find that article here.

UIObjectBinder Design-Time Support

hcOPF Error Indicators

Monday, January 4th, 2010

The JCL and JVCL are a humungous collection of useful components and routines for Delphi.  Kudos to the team that maintains and develops them.  I have used the JCL/JVCL on a number of projects now, and my only complaint is that you can’t really pick and choose what components you want to use.

This came to light once again when I decided I wanted to use the TJvErrorIndicator component with the hcOPF.  I used a single unit from the JVCL and the next thing I knew, I had 3 more packages to distribute; the JCL, JVclCore and JvValidators.

One of the things I like most about Delphi compared to .NET is the ability to create an application with a minimal footprint.  Since I have open sourced hcOPF, I also don’t want to force anyone to adopt any components they don’t wish to use, or deploy anything larger than necessary.

I have looked through the dependancies, and there is really no need for most of the classes I am forced to deploy, they just happen to be in the same unit as something else.  I really wish the Jedi would adopt the SRP (Single Responsibility Principle) for units so I could pick and choose.  As a result, I am thinking about going over to the dark side and ripping out the ErrorIndicator code to create my own unit for hcOPF.  Not exactly an ideal scenario as I cannot contribute back any changes as easily, nor can I benefit as easily from any future enhancements the Jedi make.

Another annoyance is that for some reason the LIBSUFFIX directive is not currently being used for the Jedi packages from D7 up.  As a result I have to change the required package dependancy list for each project for each version of Delphi I am trying to support, rather than simply referencing, for example, JvCore.dcp I have to reference JvCoreD7, or JvCoreD14 etc.  I have reported this issue, and am assured it will be fixed in a future release.

Like other developers I often look to the community for guidelines on naming, code organization, and programming techniques.  What ever happened to dclPackageName${LIBSUFFIX} for design-time packages and PackageName${LIBSUFFIX} for run-time?  What is the recommended standard now?

Designing an Object in hcOPF

Sunday, August 23rd, 2009

hcOPF uses TField like attributes to represent the attributes of an object.  These ThcAttribute descendants provide an object with the ability to Undo any changes made to it’s attributes and notify other Delphi objects when changes have been made.  They are the foundation of the framework, providing a generic, re-usable way of representing object attributes without using simple types and Reflection with it’s limitations and overhead.  In order to specify the attributes for an object and how they’re stored and used, the developer needs to provide the framework with information in some form.  This is Metadata, and although it could be represented in various ways, including XML, in hcOPF it’s currently code.

MetaData is used by the framework to construct an object with the appropriate attributes, based on how the object is stored in an RDBMS.  Every ThcObject descendant must override the Register class method and register it’s metadata with the framework.  A typical Register method would be as follows:

class procedure TPerson.Register;
var
  MetaData: ThcMetaData;
  aDef :ThcAttributeDef;
begin
  MetaData := ThcMetaData.Create;
  with MetaData do
  begin
    with TableDefs.AddTableDef('Person','P',[tpUpdateable,tpInsertable]) do
    begin
      with AttributeDefs do
      begin
        aDef := AddDef('Person_ID',ftInteger,'ID',ftInteger,
                       [apPrimaryKey,apServerGeneratedBeforeInsert]);
        OID := ThcOID.Create('Person',[aDef]);
        AddDef('FirstName',ftString,'FirstName',ftString,[apRequired],30);
        AddDef('LastName',ftString,'LastName',ftString,[apRequired],30);
      end; //with
    end;  //with
  end;  // with
  ObjectRegistry.RegisterObject(TPerson,MetaData);
end;

The lifetime of the MetaData is managed for you, but since the Register procedure is not a callback, you must create the MetaData object.  Metadata contains the definition of the tables where the object attributes are stored and their relationships.  So you begin by adding a Table Definition to the Metadata, specifying the RDBMS table and whether the framework is allowed to insert or update rows in that table.  Then you add Attribute Definitions which essentially specify the column and it’s type in the RDBMS and the corresponding attribute name and type in the OPF.  The OPF is capable of translating database types into internal Delphi types and OPF attributes to some degree.  More on that later…

The final line in every Register() routine should be a call to register the object with the framework.  Namely:

ObjectRegistry.RegisterObject(TPerson,MetaData);

Then somewhere in the project you need to call the Register class method for all objects.  I typically do this in the initialization section of the same unit in which the business object is defined.  In the finalization section you should also call the UnRegister method, although technically this is not necessary since the application termination will reclaim the memory.

initialization
  TPerson.Register;
finalization
  TPerson.UnRegister;

In hcOPF a ThcAbstractFactory descendant is the object that is responsible for persisting a ThcObject descendant. To get a factory instance you request it from a FactoryPool.  All applications must contain 3 core objects:  a FactoryPool, a SQLMapper, and a TransactMgr.  Normally you would drop these components onto your main datamodule and hook them up like this:

The FactoryPool knows about the different factory classes available for persistence.  In this case, IBX has been chosen as the DAL for a FireBird database.  Once the datamodule has been created and the database is connected, you can load and persist objects as you wish.

For a simple example on using the framework with standard Delphi controls see the following.  This application has a rather contrived database structure used to show lazy loading (even though it’s triggered immediately) with a child object.  This example also shows how to display content from two different objects on the same form, and persist them in a single transaction.  The database included was created with Firebird 2.0.  I have included a script as well so you can use Interbase if you prefer.  I also included the EXE in case you want to run the application without setting up the framework to compile the project.

How Delphi Developers Sabotage Delphi’s Future

Monday, July 20th, 2009

I was recently reminded why I dislike TDataSets so much while working on a new client project.  The project is written in Delphi 5 by a developer who was relatively new to Delphi and object oriented programming from what I am told.  The program was developed over a long period, and “evolved” like all applications evolve.  The original developer moved on, and was replaced by a number of remote developers.  Kudos to the company for trying a more modern day approach; distributed development (a topic for another day).

As with many RAD development projects, the application suffers from three major problems:

1) developers jumped into code immediately before addressing design issues

2) business logic is scattered throughout both the form and datamodule code, often with duplication and there is no seperation of data access logic from the UI forms (so much for the single responsibility principle).

3) there is hardly a stitch of documentation in the code, and there are no requirement documents to be had.

The result, it a spiderweb of code.  It’s almost impossible to modify portions of it without introducing bugs, and it takes forever to get a thorough understanding of what the code is doing and when.  There is not even a consistent use of DataSet events like OnBeforePost vs. MasterFields etc for Master-Detail relationships.  Reports are dynamically firing queries to perform lookups on ID columns.  There is no concern for how hard they are pounding the database.  On top of that, they’re using a TDataSet descendant to talk to a proprietary database which means they are so tightly coupled to that database that changing down the road will be a nightmare.  I’ve seen this problem before with paradox tables and lower end RDBMS products that simply did not scale to the clients needs in the future.

Time and time again, I see the same problem biting companies, and reflecting poorly on Delphi as a product and Delphi programmers as a whole.  Companies seem to errantly think that paying a junior developer $20/hr. is saving them money compared to a senior developer who may cost twice as much.  What they don’t seem to understand is the unmaintainable mess they get with junior staff.  It’s usually at that point that the company decides if they have to invest a whole bunch of effort in the Delphi product they would be better off to find a junior .NET developer and re-write the product using a better technology.  The technology was never the problem.  

Now personally, I like many things about .NET, but I don’t consider it to be necessarily better than Delphi.  It’s just another tool in the toolkit, and the results a company gets has more to do with their selection of the programmer who wields the tool.  I still like Delphi because it produces such lean EXEs, has no run-time to distribute, and no delays loading assemblies or jitting code.  For some applications, it just makes more sense.

It’s just really unfortunate that Delphi provides only a RAD framework based on the dataset.  Of course .NET has an even more powerful TDataSet like object, but the .NET community has leveraged the Open Source world including the Java projects and you have an explosion of OPF/ORM options.  The movement towards agile methodologies incorporating various frameworks such as unit tests, mocking, and business objects/ORMs is much more prevalent in the .NET space.  Perhaps if the Delphi community had more activity in these areas, Delphi would be seen as a more cutting edge technology.  It would be even better if more Delphi developers actually used such practices.

The next time before you start dropping datasets onto a datamodule, think about your options.  If you want the application to be around 5 years from now, and Delphi work to be available, think about using an ORM to isolate the application from the database, and encapsulate the data you’re using.  Extend the benefits of OOP to your data, and future changes will be much easier to make.  Look at the frameworks available for Delphi for free.  Even if you’re using an older version of Delphi, there are still lots of options available.  Now, one of those options is hcOPF.  In order to use it on my latest project, I back ported it from Delphi 7 to to Delphi 5.  You will find a Compiler folder with projects for Delphi 5 and 7.  Eventually I will create one for D2007 as well, and hope that others “fill in the blanks” since those are the only versions I am currently using.

X Platform Development in Delphi

Thursday, April 9th, 2009

In order for Delphi to maintain and grow it’s market share it, IOW, continue to be successful, it needs retain current developers and attract new ones.  To do so, it must become the only development platform capable of X platform development in either native code, or .NET CIL.  To support this goal, the IDE itself must be X platform, demonstrating the capabilities of the compiler, VCL and supporting tools. This premise is supported by the efforts of Borland in the Kylix days.  Although much of the Kylix effort was the result of market hype and a non-validated developer survey, the underlying reason for X platform support is even stronger today.  

Developers want to make money, wherever and whenever they can.  The question is how?  Developers would rather spend less time learning new languages and technologies and more time writing applications that may make them money.  Unfortunately, learning new technologies is often required to produce unique and better applications, especially when it comes to the Web.  Also, Microsoft seems to love changing the development technologies platform substantially every 5 or so years, and maybe that is how they have maintained dominance.  While everyone else is trying to figure out how the new APIs work, and when to use them, Microsoft continues to write software and make money at it.  It’s not as if their software is necessarily better, they just know how to market it.  Interestingly enough, they continue to develop native code solutions for more than low level solutions such as the OS.

How can Delphi continue to grow their market share when .NET is the predominant platform?  The answer is leverage.  Now, to maximize leverage you have to be able to produce X platform applications.  Why?  Well for one, there are competitors who have been doing so for quite some time using Widget libraries like Qt. Now there is also .NET which may actually deliver on Java’s promise to Write Once Run Anywhere/Everywhere (WORA) or (WORE).  .NET is not only a major threat  to Embarcadero’s Developer Tools specifically, but also the Object Pascal language as well.  .NET supports multiple languages, and the premier language, C# is cleaner in many respects than OP.  Add to that, the ease of integration with third party .NET libraries, the plethora of open source projects most of which are in C#, and the number of jobs requiring .NET expertise and you have a very compelling argument to go with C# .NET.  Somehow CodeGear has to ensure the survival of OP to ensure the survival of the VCL and it’s RAD Studio IDE.  One major way to ensure the survival of OP is to facilitate X platform support.  Another would be to open the language and the VCL.  Today Firebird is a very successful open source project with a large community.

The mere existence of Lazarus and the Free Pascal Compiler’s Delphi compatibility directive is a testament to the fact there are many developers who want to use Delphi for X platform development.  As of January 2009, you can even use the Free Pascal compiler to target the iPhone!   

Embarcadero may want to pay some attention to Apple.  Apple knows how to commercialize open source projects and contribute back to the community.  If Embarcadero followed Apple’s lead, we would not only have a much better Windows product, but we would also likely have Delphi for the Mac, Linux and the iPhone as well.  

Time to quit following Microsoft and carve your own niche…that’s just my 2 cents.

Customizing TApplication Without Helpers

Wednesday, February 18th, 2009

Have you ever wished you could create a TApplication descendant to incorporate some core functionality you wanted across all your applications?  We did, and this post describes how we accomplished it in Delphi 7 which does not support class helpers.

One of the fundamental features Delphi lacks for “out of the box” enterprise application development is the ability to determine whether the current logged in user belongs to an Active Directory group directly, or indirectly through memberships in other groups.  This is essential for determining if the user is allowed to run the application at all, and if so, what subset of the application’s features they are allowed to access.

At work, we maintain a DEV, TEST and PROD database environment.  Our applications accept this environment specifier as a command line parameter.  We dynamically change our database connections at run-time, and restrict access to the applications on the file system using AD groups.  Some of our applications used Oracle database tables in the application’s schema for security, and to record their usage.  Since there was no consistent schema used for all applications, this was difficult to maintain and any applications with security were usually accompanied by a “Security Manager” companion application.  The development group wanted to off load all the user provisioning support calls we received, to the service desk, so we had to provide a uniform security mechanism they could administrate - AD.

The Requirements

My experience has taught me that full blown requirements gathering just doesn’t work, nor does diving into code.  I prefer a middle of the road approach.  I like to create a point form list of application requirements, and then prototype it to see how well it works, driving out more ideas.

My requirements for this effort were:

  • prevent users from running the application if they did not belong to the appropriate group(s)
  • provide easy access to AD Membership information for securing application features
  • must be easily incorporated into existing applications with minimal changes (including apps not requiring security)
  • incorporate our standard About box
  • provide some basic roles and allow developer defined ones
  • provide application version information

The Implementation

Initially I started by developing components that could provide AD Group membership information.  Then we standardized on a group naming convention, taking into account the multiple database environments we needed to support:

Format(’%s - %s - %s’,[ApplicationName,Environment,RoleName]);  //i.e.: ‘HierarchyMgr - DEV - PowerUsers’

TApplication was never really meant to be used as an ancestor class.  None of it’s methods are virtual, it’s presence in the Forms unit and tight coupling to TForm and other classes speak to this.  So I traced through the sequence of code excecuted on application startup and found that the only way I could load my own application object instead of TApplication was to make my unit the first one in the uses clause of a project.  Then in my initialization I can free the existing application object, replacing it with my own using the following code:

initialization
if assigned(Forms.Application) then
Forms.Application.Free;

Application := TEnApplication.Create(nil);
Application.ShowHint := True;
//in case any units access the Forms.Application global variable explicitly, assign it to the same instance
Forms.Application := Application;

finalization
//Application object will be destroyed by native Delphi VCL

If you’re interested in the actual code, send me an email…

Delphi on My Mac with VMWare 2.01

Saturday, February 14th, 2009

This summer a friend of mine convinced me to go to the Apple Developer conference with him.  He had purchased an original iPhone and convinced me we should find out how to write applications for the next generation iPhone.  It was shortly after the conference that I bought my first Mac - a Macbook Pro.  I bought a Mac laptop because I have been traveling more frequently and have often missed being able to cut a little Delphi code and keep in touch with the world via the Net.  Since I had to buy a Mac for iPhone development, I thought it was the perfect time to see if a laptop was right for me.  So far, I’m liking the ability to code anywhere and any time.

Initially I thought about using BootCamp to load windows onto my MacBook Pro, but decided against it because I only had a 100Gb hard drive, since I unwittingly bought an older version of the MacBook Pro on eBay (it’s only a 2 Ghz Core Duo MacBook Pro 1,1), making the trade off for a smaller but faster HD.  This model also didn’t have an external Firewire 800 port so running Windows off an external drive wasn’t a good option both for performance and the portability impediment of an external device.

After looking at other options such as replacing the HD with a larger version I opted to buy VMWare’s Fusion 2 for 50% off, a mere $40USD.  It’s amazing to me the complexity of the software you can buy for cheap these days.  As a programmer it’s also disturbing, since I understand the time and effort that goes into writing such a piece of software.

After working now with Delphi 7 on Fusion, I have concluded that Delphi 7 under VMWare on my older laptop is actually faster than running it on my AMD X2 4400+ Windows PC with a 10K rpm HD.   It’s been a little difficult getting used to a laptop keyboard, but I think my PC is going to be relegated to acting as my server in the near future.

One major issue that I encountered with Delphi on my MacBook Pro was the lack of an Insert key.  Although I own Rad Studio 2007, I am still actively using Delphi 7.  This is in part to the component libraries that I own, and CodeRush.  For those of you out there who don’t know about CodeRush, it’s an IDE add-in written by Mark Miller when he owned Eagle Software, prior to selling his IP and joining DevExpress.  CodeRush was a great productivity tool.  I especially like the code navigation and code template features.  Unfortunately it has some bugs, one of which is that Insert mode is occasionaly turned off, and the code templates start to overwrite other code.  As I understand it,  a lot of these kind of problems are due to bugs in the Editor itself and a lack of documentation and support from Borland for third party vendors.  Each release of Delphi involved a significant rework of the product, and with the advent of .NET with a much larger market and a vendor encouraging developers to write IDE add-ins, Mark left the Delphi community and halted maintenance on CodeRush for Delphi.

Today, I figured out how toggle Insert mode in Delphi 7.  VMWare is supposed to have a menu option to send special key codes to the guest OS, but for some reason Insert was not on my Send Key sub menu as shown in the VMWare docs.  To get around it, I went to VMWare Preferences for keyboard and mouse, enabled the key mappings and then added one.  I chose Alt + X to map to the Insert key and it works now…sort of.  The first time I press Alt+X Insert mode is toggled correctly, but if I continue to hold the Alt key and hit the X, the first or second time I start to get the ‘X’ character in the editor rather than toggling insert mode.  Hopefully this issue will be fixed as well as a couple other UI annoyances in the upcoming 2.02 release.  At any rate I would highly recommend a MacBook Pro with VMWare for all your Delphi development.