Tuesday, September 8, 2009
SamplingProfiler v1.7.4
This release also includes preparation for an “attach to process” option, which is currently not enabled, but should hopefully make in the next version (available “when ready”).
Delphi 2010 paths added to options.
Fixed a bug in silent mode.
What To Do When You Find a Bug in Your Program
If possible pretend that you are the company's answering machine. "You have reached Acme LTD, we are sorry but all the support lines are busy. Please call back in 12 hours".
Answer that you are sorry but the line is lousy and you did not hear the last sentence. Blib, zzp, blip. "Could you please call us back tomorrow, or when the lines are fixed?".
Answer that you are sorry but the support team is on holidays and that they will be back in no time, in 3 months.
Answer "I do not care, leave me alone, its Friday. You asked for the application now learn to live with it".
No Go Doings
After you have reproduced the steps and actually found the bug:
Introduce new, more severe bugs. The bug from the phone call will look like child's play. When this new bug is found, introduce another one. Just keep adding more bugs.
Ignore the bug. Just continue adding extra functionality to the application. Maybe users will forget and never again run into it.
Restart your development environment. If that does not help restart Windows. If that does not help restart Windows again.
Locate the line of the source code that is responsible for the bug (note: this requires lots of your valuable time). Then do one of the following (or all):
Delete the function containing the line of code that produces the bug. If you remove the cause - the consequence (bug) will go away. If, for example, the result of the function was a Boolean value, just make sure that sometimes false is returned and sometimes true is returned for the function result.
Turn your head away from the monitor. Pretend that you are dead. Maybe it will go away.
Turn your head away from the monitor. Pretend that you are dead. Try to scare the bug by quickly looking into the source code saying "bu". Repeat until the bug is gone or the code fixes itself.
Delete the application source. Delete any marks the application ever existed. Pretend that you never created the program in the first place.
Run. Run as fast as you can.
Sunday, September 6, 2009
Search your HTML documentation with HelpNDoc 2.2
PDF Generation is now more robust and the external files functionality has been totally rewritten to better handle HTML and Word documentation import. It is now easier than ever to include external files in topics.
Delphi 2010 migration was a beeze; samples/slides will be uploaded soon
Delphi Win32 2010 works like a charm: it is much faster and has a much smaller footprint than any other Galileo based IDE.
In fact, it feels almost as fast as the pre-Galileo based IDE’s.
With the added benefit that all the new features make me much more productive, not the least because it has not yet crashed on me this week once.
Crashing has been a frequent thing on me since Delphi 4 (maybe I should not even mention that number ), for most IDE’s at least a couple of times a week, so this is good.
Delphi Prism 2010 works really nice too, it is rock solid, and the language as some great features not found in other .NET languages.
But it still needs a tiny bit more polishing on the Visual Studio IDE Integration part.
There are a few things not as smoothly integrated as I’m used to in C# and VB .NET (for instance when adding assembly references; C# and VB.NET allow you to do that from multiple places in the IDE; Delphi Prism from only one).
I know it is nitpicking (the same holds for the Team Foundation System integration in the Visual Studio IDE: ever tried to add files or folders? There is only one icon that allows you to do it. Ever tried to move files or folders around? No way you can drag & drop, in fact you can move only 1 file or folder at a time, and then the folder tree leaves you at the target).
The Embarcadero folks have worked hard on developer productivity in the Delphi Win32 2010 IDE.
(Did I mention the F6 key? It is an awesome way of directly jumping into configuration dialogs a zillion levels deep.
Did I mention the Ctrl-D key? It instantly reformats your source code to your formatting settings).
So maybe it is now time to put some of that effort into the Prism side as well.
PDFtoolkit VCL v3.3
PDFtoolkit does not require PDF software such as Adobe PDF library, Adobe Acrobat Professional, Adobe PDF Reader or GhostScript.
Note: PDFtoolkit works on existing documents. For creating new PDF documents in Delphi or C++Builder, you can use eDocEngine VCL. For other platforms, we have PDFOne .NET and PDFOne Java, which can create PDF documents from scratch as well as modify existing PDF documents.
Memory Manager Tool FastMM 4.94
download
TurboPower Orpheus 4.06 for Delphi 2010
download
IDE Fix Pack 2010 – ObjectInspector F1 key
Then I remembered that I saw something in ToolsAPI.pas that was called “Traits”. So I looked into it and found the IOTAPersonalityServices interface. By analyzing the GetTrait “call parameters” I was able to extract the GUID that is used for the IOTAPersonalityServices.GetTrait(ATraitGUID) parameter. It seems that in Delphi 2010 the Property-Help trait is missing or GetTrait doesn’t return it.
The next step was to try to add the “trait” by calling the IOTAPersonalityServices.AddPersonalityTrait method with the personality ID and the extracted GUID. And vólà the F1 key in the Object Inspector is back.
Thursday, September 3, 2009
Class (, Static, or Shared) Constructors (and Destructors)
How many times have you needed to initialize something related to a specific class? What if your class is using the singleton pattern and you need to create that single instance? How about registering the class with a “class-factory?” Prior to Delphi 2010, there were several options available to you, none of which were really in keeping with the OOP notion of encapsulation. You could put all the code into the initialization section of the unit in which the class lived, add a regular class method and call it explicitly, or manually integrate the init code into your application’s startup. By moving all this code into a class constructor, the mere act of “touching” the class will cause the class constructor to run in keeping with the above rules.
Better control over smart-linking
Loosely coupled with improved encapsulation is the notion that by moving any initialization code into the class constructor you have a little more control over what code is linked into your application. You can now take advantage of the fact that as long as the class type isn’t referenced referenced anywhere in your code, none of the code related to the class type is linked in, including the VMT, virtual methods, RTTI, etc.. If you had placed this init code into the initialization section of its containing unit, then at least the VMT, virtual methods and RTTI would have been linked into your application. Even though none of your code references it. The new enhanced RTTI throws an interesting wrinkle to this theory, but that will have to be covered later.
Better language compatibility with Delphi Prism
As Delphi Prism gains popularity, more people will need to maintain some of their code in a manner that they can compile with either compiler. There are still many language constructors that both compilers have that the other doesn’t, but this is a continuing effort to close that gap where it makes sense. Expect to see more work in this area, in both compilers.
In my next post, we’ll “pop the hood” (or bonnet, for our good friends across the pond) and take a peek at how they work and exactly when they are invoked. I will say that many people tend to read way, way too much into this simple statement describing when a class constructor is invoked; “Called automatically to initialize the class before the first instance is created or any static members are referenced.” The only temporal reference in that statement is “…before…”. I will leave you to consider the implications of that word, lest many of you begin to talk about “threading issues” and “code-bloat.” Class constructors and destructors are to classes as initialization and finalization are to units.
SmartInspect now supports Delphi 2010
As usual, existing SmartInspect customers can download the new release from our customer portal. If you are interested in giving SmartInspect a try, please download the updated trial version with Delphi 2010 support from our website.
Wednesday, September 2, 2009
Delphi 2010’s RTTI and Active Scripting
That said, reading the small amount of documentation, I happened across mention of an Invoke method, which set me thinking — is it finally possible to cleanly (and generically!) wrap Delphi objects into IDispatch ones for use in Active Scripting? Well, apart from the events issue and the difficulties of surfacing set types in a script-friendly manner, my answer is: for sure! Check out this compiled EXE if you’re interested (screenshot here).
Basically, what I’ve done is to write a TCustomDispProxy class with TObjectDispProxy and TClassDispProxy descendants; in the demo, these are then used to directly surface the Application, Mouse and Screen objects, together with the form instance (object properties and fields are handled automatically) and some metaclasses (TButton, TLabel, etc.).The idea is that no specific object or class type requires a specific wrapper — all just use TObjectDispProxy and TClassDispProxy respectively, with wrappers for sub-objects created on the fly. Now unlike Allen Bauer, I’m not a masochist in these things, and so have used the MS Script Control rather than implemented the Active Scripting interfaces directly; because of this, be warned that if the control isn’t installed (which is unlikely these days, though could be the case), the EXE won’t run.
Anyhow, if anyone’s interested in the actual code, add a comment saying so and I’ll endeavour to do it up for public consumption. Alternatively, if you’re not interested, then don’t leave a comment and I won’t bother
Custom Marshalling/UnMarshalling in Delphi 2010
Introduction
Some days ago, Embarcadero has presented the new version of RAD Studio, 2010.
The are many new features, but you can find in a lot places around the web, so
I won’t repeat them here.
One of the things widely requested from all Delphi programmers all over the world over the past few years, including myself, is
certainly a new and more powerful RTTI.
The new system of RTTI has finally arrived, and pave the way for a large number of applications.
One area that has benefited from the new RTTI is for sure the marshaled objects.
Marshaling is defined as follows:
“In computer science, marshalling (similar to serialization) is the process of
transforming the memory representation of an object to a data format suitable for
storage or transmission. It is typically used when data must be moved between
different parts of a computer program or from one program to another.
The opposite, or reverse, of marshalling is called unmarshalling (demarshalling) (similar to deserialization).”
–WikiPedia
In Delphi 2010 the process of serialization and deserialization is handled respectively by a Marshaller and an Unmarshaller.
The built-in format for the serialization of any Delphi object is JSON.
There are 2 main classes responsible for serializing objects into JSON, both present in the unit DBXJSONReflect:
- TJSONMarshal
- TJSONUnMarshal
Let’s say you have an object defined as follow:
- type
- TKid = class
- FirstName: String;
- LastName: String;
- Age: Integer;
- end;
To serialize and deserialize an instance of TKid it requires the following steps:
- var
- Mar: TJSONMarshal; //Serializer
- UnMar: TJSONUnMarshal; //UnSerializer
- Kid: TKid; //The Object to serialize
- SerializedKid: TJSONObject; //Serialized for of object
- begin
- Mar := TJSONMarshal.Create(TJSONConverter.Create);
- try
- Kid := TKid.Create;
- try
- Kid.FirstName := 'Daniele';
- Kid.LastName := 'Teti';
- Kid.Age := 29;
- SerializedKid := Mar.Marshal(Kid) as TJSONObject;
- finally
- FreeAndNil(Kid);
- end;
- finally
- Mar.Free;
- end;
- //Output the JSON version of the Kid object
- WriteLn(SerializedKid.ToString);
- // UnMarshalling Kid
- UnMar := TJSONUnMarshal.Create;
- try
- Kid := UnMar.UnMarshal(SerializedKid) as TKid;
- try
- //now kid is the same as before marshalling
- Assert(Kid.FirstName = 'Daniele');
- Assert(Kid.LastName = 'Teti');
- Assert(Kid.Age = 29);
- finally
- Kid.Free;
- end;
- finally
- UnMar.Free;
- end;
- end;
Simple, isn’t it?
To access the JSON string that is our object, we must call the method ToString.
The JSON representation of this object SerializedKid can be saved to file,
sent to a remote server, used by a Web page from a web service, stored on a database or sent into space (!!!).
The Delphi application re-read the JSON string, you can recreate the object as it was at the time of serialization.
But anyone with a JSON parser can still read the data in our object, even non Delphi client.
These are the advantages of having used an open format and standard.
So far the simple part …
How serialize a field differently from the default?
Suppose we add the date of birth to our TKid:
- type
- TKid = class
- FirstName: String;
- LastName: String;
- Age: Integer;
- BornDate: TDateTime;
- end;
Serialize a TDateTime, localized and that I have in JSON string is a float, because for Delphi TDateTime is a decimal number.
If I read the data from another program Delphi, no problem, but if I wanted to read a script in JavaScript? or. NET? or Ruby?
Then I use a format “DATA” to understand, even for these languages.
The new engine provides the serialization too.
Is needed, however, to tell the Marshaller and UnMarsheller how to represent and reconstruct a particular
object field by two statements like the following:
- //marshaller
- Marshaller.RegisterConverter(TKid, 'BornDate',
- function(Data: TObject; Field: string): string
- var
- ctx: TRttiContext; date : TDateTime;
- begin
- date := ctx.GetType(Data.ClassType).GetField(Field).GetValue(Data).AsType;
- Result := FormatDateTime('yyyy-mm-dd hh:nn:ss', date);
- end);
- //UnMarshaller
- UnMarshaller.RegisterReverter(TKid, 'BornDate',
- procedure(Data: TObject; Field: string; Arg: string)
- var
- ctx: TRttiContext;
- datetime:TDateTime;
- begin
- datetime := EncodeDateTime(StrToInt(Copy(Arg, 1, 4)),
- StrToInt(Copy(Arg, 6, 2)),
- StrToInt(Copy(Arg, 9, 2)),
- StrToInt(Copy(Arg, 12, 2)),
- StrToInt(Copy(Arg, 15, 2)),
- StrToInt(Copy(Arg, 18, 2)), 0);
- ctx.GetType(Data.ClassType).GetField(Field).SetValue(Data, datetime);
- end);
-
The anonymous method is called when the marshaller serializes the field ‘BornDate’ is called “Converter” while Unmarshaller anonymous method that calls when he has to reconstruct the object from the JSON string is the “Reverter”.
Thus serializing a TKid assure you that my object is readable both by Delphi from another language without loss of information.
But what happens when I have to serialize a complex type?
Suppose we extend TKid this:
- type
- TTeenager = class(TKid)
- Phones: TStringList;
- constructor Create; virtual;
- destructor Destroy; virtual;
- end;
We must define a Converter and a Reverter for the TStringList class.
We can do it this way:
- var
- Marshaller: TJSONMarshal;
- UnMarshaller: TJSONUnMarshal;
- Teenager: TTeenager;
- Value, JSONTeenager: TJSONObject;
- begin
- Marshaller := TJSONMarshal.Create(TJSONConverter.Create);
- try
- Marshaller.RegisterConverter(TTeenager, 'BornDate',
- function(Data: TObject; Field: string): string
- var
- ctx: TRttiContext; date : TDateTime;
- begin
- date := ctx.GetType(Data.ClassType).GetField(Field).GetValue(Data).AsType;
- Result := FormatDateTime('yyyy-mm-dd hh:nn:ss', date);
- end);
- Marshaller.RegisterConverter(TStringList, function(Data: TObject): TListOfStrings
- var
- i, count: integer;
- begin
- count := TStringList(Data).count;
- SetLength(Result, count);
- for i := 0 to count - 1 do
- Result[i] := TStringList(Data)[i];
- end); //TStringList Converter
- Teenager := TTeenager.CreateAndInitialize;
- try
- Value := Marshaller.Marshal(Teenager) as TJSONObject;
- finally
- Teenager.Free;
- end;
- finally
- Marshaller.Free;
- end;
- // UnMarshalling Teenager
- UnMarshaller := TJSONUnMarshal.Create;
- try
- UnMarshaller.RegisterReverter(TTeenager, 'BornDate',
- procedure(Data: TObject; Field: string; Arg: string)
- var
- ctx: TRttiContext;
- datetime: TDateTime;
- begin
- datetime := EncodeDateTime(StrToInt(Copy(Arg, 1, 4)),
- StrToInt(Copy(Arg, 6, 2)),
- StrToInt(Copy(Arg, 9, 2)),
- StrToInt(Copy(Arg, 12, 2)),
- StrToInt(Copy(Arg, 15, 2)),
- StrToInt(Copy(Arg, 18, 2)), 0);
- ctx.GetType(Data.ClassType).GetField(Field).SetValue(Data, datetime);
- end);
- UnMarshaller.RegisterReverter(TStringList, function(Data: TListOfStrings): TObject
- var
- StrList: TStringList;
- Str: string;
- begin
- StrList := TStringList.Create;
- for Str in Data do
- StrList.Add(Str);
- Result := StrList;
- end); //TStringList Reverter
- Teenager := UnMarshaller.Unmarshal(Value) as TTeenager;
- try
- Assert('Daniele' = Teenager.FirstName);
- Assert('Teti' = Teenager.LastName);
- Assert(29 = Teenager.Age);
- Assert(EncodeDate(1979, 11, 4) = Teenager.BornDate);
- Assert(3 = Teenager.Phones.Count);
- Assert('NUMBER01′=Teenager.Phones[0]);
- Assert('NUMBER02′=Teenager.Phones[1]);
- Assert('NUMBER03′=Teenager.Phones[2]);
- finally
- Teenager.Free;
- end;
- finally
- UnMarshaller.Free;
- end;
- end;
-
There are different types of Converter and Reverter.
In the the DBXJSONReflect there are 8 types of converters:
-
- //Convert a field in an object array
- TObjectsConverter = reference to function(Data: TObject; Field: String): TListOfObjects;
- //Convert a field in a strings array
- TStringsConverter = reference to function(Data: TObject; Field: string): TListOfStrings;
- //Convert a type in an objects array
- TTypeObjectsConverter = reference to function(Data: TObject): TListOfObjects;
- //Convert a type in a strings array
- TTypeStringsConverter = reference to function(Data: TObject): TListOfStrings;
- //Convert a field in an object
- TObjectConverter = reference to function(Data: TObject; Field: String): TObject;
- //Convert a field in a string
- TStringConverter = reference to function(Data: TObject; Field: string): string;
- //Convert specified type in an object
- TTypeObjectConverter = reference to function(Data: TObject): TObject;
- //Convert specified type in a string
- TTypeStringConverter = reference to function(Data: TObject): string;
Each of them deals with a particular conversion object representation in the final serialization, in our case we will use them to convert to JSON.
Also in the DBXJSONReflect unit are defined many “Reverter” dealing with retrieving
the serialized version of the data and use it to reconstruct the object previously serialized.
Because they are complementary to the Converter, I will not copy them here.
As a final example, we derive from TProgrammer by TTeenager adding a list of Laptops in the properties.
Is therefore necessary to introduce a new pair of Converter / Reverter.
In this example I have defined all the converter and reverter in another unit in
order to have more readable code:
- type
- TLaptop = class
- Model: String;
- Price: Currency;
- constructor Create(AModel: String; APrice: Currency);
- end;
- TLaptops = TObjectList;
- TProgrammer = class(TTeenager)
- Laptops: TLaptops;
- constructor Create; override;
- destructor Destroy; override;
- class function CreateAndInitialize: TProgrammer;
- end;
- // Implementation code…
- var
- Marshaller: TJSONMarshal;
- UnMarshaller: TJSONUnMarshal;
- Programmer: TProgrammer;
- Value, JSONProgrammer: TJSONObject;
- begin
- Marshaller := TJSONMarshal.Create(TJSONConverter.Create);
- try
- Marshaller.RegisterConverter(TProgrammer, 'BornDate', ISODateTimeConverter);
- Marshaller.RegisterConverter(TStringList, StringListConverter);
- Marshaller.RegisterConverter(TProgrammer, 'Laptops', LaptopListConverter);
- Programmer := TProgrammer.CreateAndInitialize;
- try
- Value := Marshaller.Marshal(Programmer) as TJSONObject;
- finally
- Programmer.Free;
- end;
- // UnMarshalling Programmer
- UnMarshaller := TJSONUnMarshal.Create;
- try
- UnMarshaller.RegisterReverter(TProgrammer, 'BornDate', ISODateTimeReverter);
- UnMarshaller.RegisterReverter(TStringList, StringListReverter);
- UnMarshaller.RegisterReverter(TProgrammer, 'Laptops', LaptopListReverter);
- Programmer := UnMarshaller.Unmarshal(Value) as TProgrammer;
- try
- Assert('Daniele' = Programmer.FirstName);
- Assert('Teti' = Programmer.LastName);
- Assert(29 = Programmer.Age);
- Assert(EncodeDate(1979, 11, 4) = Programmer.BornDate);
- Assert(3 = Programmer.Phones.Count);
- Assert('NUMBER01′ = Programmer.Phones[0]);
- Assert('NUMBER02′ = Programmer.Phones[1]);
- Assert('NUMBER03′ = Programmer.Phones[2]);
- Assert('HP Presario C700′ = Programmer.Laptops[0].Model);
- Assert(1000 = Programmer.Laptops[0].Price);
- Assert('Toshiba Satellite Pro' = Programmer.Laptops[1].Model);
- Assert(800 = Programmer.Laptops[1].Price);
- Assert('IBM Travelmate 500′ = Programmer.Laptops[2].Model);
- Assert(1300 = Programmer.Laptops[2].Price);
- finally
- Programmer.Free;
- end;
- finally
- UnMarshaller.Free;
- end;
- finally
- Marshaller.Free;
- end;
- end;
-
Unit CustomConverter.pas contains all needed Converters/Reverts as anon methods.
- unit CustomConverter;
- interface
- uses
- DBXJSONReflect,
- MyObjects; //Needed by converter and reverter for TLaptops
- var
- ISODateTimeConverter: TStringConverter;
- ISODateTimeReverter: TStringReverter;
- StringListConverter: TTypeStringsConverter;
- StringListReverter: TTypeStringsReverter;
- LaptopListConverter: TObjectsConverter;
- LaptopListReverter: TObjectsReverter;
- implementation
- uses
- SysUtils, RTTI, DateUtils, Classes;
- initialization
- LaptopListConverter := function(Data: TObject; Field: String): TListOfObjects
- var
- Laptops: TLaptops;
- i: integer;
- begin
- Laptops := TProgrammer(Data).Laptops;
- SetLength(Result, Laptops.Count);
- if Laptops.Count > 0 then
- for I := 0 to Laptops.Count - 1 do
- Result[I] := Laptops[i];
- end;
- LaptopListReverter := procedure(Data: TObject; Field: String; Args: TListOfObjects)
- var
- obj: TObject;
- Laptops: TLaptops;
- Laptop: TLaptop;
- i: integer;
- begin
- Laptops := TProgrammer(Data).Laptops;
- Laptops.Clear;
- for obj in Args do
- begin
- laptop := obj as TLaptop;
- Laptops.Add(TLaptop.Create(laptop.Model, laptop.Price));
- end;
- end;
- StringListConverter := function(Data: TObject): TListOfStrings
- var
- i, count: integer;
- begin
- count := TStringList(Data).count;
- SetLength(Result, count);
- for i := 0 to count - 1 do
- Result[i] := TStringList(Data)[i];
- end;
- StringListReverter := function(Data: TListOfStrings): TObject
- var
- StrList: TStringList;
- Str: string;
- begin
- StrList := TStringList.Create;
- for Str in Data do
- StrList.Add(Str);
- Result := StrList;
- end;
- ISODateTimeConverter := function(Data: TObject; Field: string): string
- var
- ctx: TRttiContext; date : TDateTime;
- begin
- date := ctx.GetType(Data.ClassType).GetField(Field).GetValue(Data).AsType;
- Result := FormatDateTime('yyyy-mm-dd hh:nn:ss', date);
- end;
- ISODateTimeReverter := procedure(Data: TObject; Field: string; Arg: string)
- var
- ctx: TRttiContext;
- datetime :
- TDateTime;
- begin
- datetime := EncodeDateTime(StrToInt(Copy(Arg, 1, 4)), StrToInt(Copy(Arg, 6, 2)), StrToInt(Copy(Arg, 9, 2)), StrToInt
- (Copy(Arg, 12, 2)), StrToInt(Copy(Arg, 15, 2)), StrToInt(Copy(Arg, 18, 2)), 0);
- ctx.GetType(Data.ClassType).GetField(Field).SetValue(Data, datetime);
- end;
- end.
Last hint…
Every serialization/unserialization process can create “warnings”.
Those warnings are collected into the “Warnings” property of the Ser/UnSer Object.
Conclusions
In this post I tried to introduce the basics of the new serialization engine in Delphi 2010.
During the next ITDevCon to be held in Italy next November 11.12, I’ll have a talk in which I will extensively talk about serialization and RTTI.
All interested smart developers are invited
ITALIAN P.S.
Se qualche programmatore italiano volesse avere la versione in italiano di questo post può lasciare un commento e vedrò di accontentarlo
You can find the DUnit project Source Code