Latest Posts
Random Thoughts on the Passing Scene #77
- Nice article on Delphi 2009 and C++Builder 2009 in eWeek.
- Feel free to Digg This or Reddit that.
- And while I was at it over on Digg, I submitted this one, too. That headline is priceless.
- And speaking of social media sites, I’m slowly starting to get into Twitter.
- Jim McKeeth has started up a Delphi podcast at http://www.delphi.org/. Episode 1 includes a discount for BeyondCompare, another great ISV tool built with Delphi. You can even subscribe via iTunes.
Share This | Email this page to a friend
posted @ Wed, 20 Aug 2008 00:09:25 +0000 by Nick Hodges
JBuilder Day em português - duas edições - 21 e 28 de agosto
Nos próximos dias 21 e 28 a CodeGear promoverá 2 edições do JBuilder, onde você terá um dia inteiro para assistir várias apresentações sobre desenvolvimento Java através de EJB, hibernate, Struts, AppFuse e outras tecnologias e frameworks.
Mais informações clique aqui
Share This | Email this page to a friend
posted @ Tue, 19 Aug 2008 18:08:48 +0000 by Andreano Lanusse
DevTracks 2008 Roadshow in Offenbach/Ffm und Berlin

Die Roadshow geht weiter. Mit brandaktuellen Informatione zu Delphi, C++ Builder und den DatabaseGear Produkten von Embarcadero.
Weitere Infos:
Offenbach/Frankfurt ist leider schon ausgebucht!
Ihr
Matthias Eißing
Share This | Email this page to a friend
posted @ Mon, 18 Aug 2008 14:14:23 +0000 by Matthias Eissing
Tiburon Ready: TResourceAllocationChart
TResourceAllocationChart draws a chart suitable for showing the allocation of resources. Those resources could represent rooms, cars, campsites, holiday cottages… in fact any resource that can only allocated to a single recipient at a time.
"[The conversion to Unicode] was extremely easy too, as it didn’t need any code changes."
Share This | Email this page to a friend
posted @ Sat, 16 Aug 2008 20:05:21 +0000 by Nick Hodges
Multicast events using generics
Ever since before Delphi 1, the (then Delphi only) RAD Studio IDE has been full of home-grown multicast event class types. I usually refer to these as an "event bus." This is from my hardware days when I designed microcontroller based security/access control equipment. A CPU has an "address bus" and a "data bus" which can have one sender and any number of "listeners," not unlike a multicast event.
I’ve been following with interest these series of posts about creating a multicast event class in native Delphi. What struck me was that the class being presented was remarkably similar to the class and its descendants that we’ve used in the RAD Studio IDE since before Delphi 1. However the one presented in those posts does take it to another level and adds some extra housekeeping to notify the listener and/or the multicast event when each other is being freed. While that’s a nice addition, it does tend to complicate its use. From my point of view, regardless of whether you have a single-cast or multicast event, you should always balance assigning the event with clearing the event. IOW, make sure the event isn’t pointing to a dead instance. Overall, what was presented is a pretty decent design given what the Delphi language has to offer today.
One of the key new Delphi language features for Tiburón is the introduction of generics. During the development cycle, I began thinking if there was a way to create a multicast event class using generics. At first glance this seemed like an easy thing to do. After all, a multi cast event class is just a wrapper around an array of method pointers. To send out the event you iterate through the array of method pointers invoking each event in turn. So surely it’s as simple as this:
type
TMulticastEvent<T> = class
private
FHandlers: array of T;
public
procedure Add(Handler: T);
procedure Remove(Handler: T);
procedure Invoke(<uh oh>);
end;
Hmm… Do you see the problem? How can I actually invoke the event? Adding and removing handlers looks straight forward, but invoking events is a problem. The multicast event class inside the Delphi IDE starts with a base class that handles the array of method pointers and it is up to the manually created descendants to add the Add and Remove methods and the specific Invoke (or Send in our case) method that provides the right parameter list and does the job of iterating through the assigned method pointers. Lots of ugly typecasting was happening in addition to having to manually create the descendants.
Ok, now I have a challenge. The gauntlet has been thrown. I’m going to design a generic multicast event using Delphi native code, dagnabbit! The end result still looked very similar to the internal IDE multicast event classes. The way I solved the invoke problem was to declare a property of type T.
type
TMulticastEvent<T> = class
private
FInvoke: T;
<same above as>
property Invoke: T read FInvoke;
end;
Syntactically you just call Invoke like any old method:
type TMyEvent = procedure (Sender: TObject; IntValue: Integer; FloatValue: Double) of object; MulticastMyEvent: TMulticastEvent<TMyEvent> begin MulticastMyEvent := TMulticastEvent<TMyEvent>.Create; MulticastMyEvent.Invoke(Obj, 10, 3.14); end.
There is still an interesting problem here. Do you see it? So I can use the event property "simulate" a method, but what is assigned to this method pointer? Where does control go when you call through this method pointer? When is is assigned? This is when things started getting a little interesting.
The problem with all the other multicast event class implementations I’ve seen (including our own internal one) is that for type-safety they require you to manually create a descendant class with the actual type of the event. It’s tedious and error prone, and promotes gross duplication of code. These are the problems that generics are suppose to solve, right? However in this instance, it was almost like I needed to instantiate code at run-time (something that .NET actually does which solves many of these problems). Barring that, I had to reach a little deeper into my bag-o-tricks. All the multicast event classes share a couple of things; maintain a list or array of method pointers and iterate over that list and invoke each method. All method pointers are the same size (8 bytes consisting of a pointer to an object instance and a pointer to the method to invoke). I created a non-generic base class much like the existing base class we’ve had for years:
type
TMulticastEvent = class
strict protected type TEvent = procedure of object;
strict private
FHandlers: array of TMethod;
procedure Add(const AMethod: TEvent); overload;
prodedure Remove(const AMethod: TEvent); overload;
function IndexOf(const AMethod: TEvent): Integer; overload;
end;
Hmmmm… Why is the array declared here instead of the descendant? How will this be "typesafe?" You can’t cast an arbitrary "T" to a TMethod. Oh and waidaminnit… the Add, Remove and IndexOf methods are private! To address these issues, I pulled out some assembler tricks out of my proverbial bag and I added the following protected methods.
type
TMulticastEvent = class
<stuff from above>
protected
procedure InternalAdd;
procedure InternalRemove;
procedure InternalIndexOf;
end;
Uh, dude, there are no parameters on those methods. What do they do? Here’s the method body from the InternalAdd method. The others are very similar.
procedure TMulticastEvent.InternalAdd;
asm
XCHG EAX,[ESP]
POP EAX
POP EBP
JMP Add
end;
What this function does is removes itself and the immediate caller from the call chain and directly transfers control to the corresponding "unsafe" method while retaining the passed in parameter(s). As we’ll see later on, we’re going to create a generic descendant class from this one where the method bodies of the actual typesafe versions of Add, Remove and IndexOf merely call the corresponding "InternalXXX’ versions from this base class. Here’s some of what that descendant will look like and the body of the Add method:
type
TMulticastEvent<T> = class(TMulticastEvent)
public
procedure Add(const AMethod: T); overload;
procedure Remove(const AMethod: T); overload;
function IndexOf(const AMethod: T): Integer; overload;
end;
procedure TMulticastEvent.Add(const AMethod: T);
begin
InternalAdd;
end;
Ah, there’s the "T"… It’s beginning to look pretty generic. Why do it this way? The primary reason is that you cannot have any assembly code in the method body of any generic type or method, but you can refer to other methods that are written in assembly. So in this case the ancestor class merely provides those "non-generic-able" assembler functions. Another reason, which we’ll get to, is that I also want to make sure that iterating through the array of event handlers is also done in some common code, which will also need to be in assembly due to some more stack tricks that must be done.
When this generic class is instantiated with an event type, the Add, Remove, and IndexOf methods will always get a stack frame. Even though the method body doesn’t touch the parameter, the frame is still there and needs to be cleaned up because the size of the event type is > 4 bytes. Since it wont fit in a register, it is always passed by value on the stack. This is also why I declared a private TEvent type up in the base class so that the stack pattern will match. If I had merely declared the parameters as a TMethod, which is a record that allows you to "crack open" the event type, it would not have worked. This is because method pointers are passed differently from records because you can directly refer to a method and instance in code as the parameter when calling a method that takes a method pointer:
begin ... MC.Add(Obj.EventHandler); ... end;
In this case the compiler simply pushes the instance reference from the variable Obj onto the stack, followed by the address of the EventHandler method. However for a record, the compiler passes a pointer to the record, which may even be in a register. Then, depending upon whether or not the parameter is declared as "const" or not, the called method will use the pointer directly or make a local copy of the record on the stack.
I won’t bore you with the details of how the Add, Remove, and IndexOf methods actually interact with the array as that really isn’t the interesting part and is merely boilerplate code anyway. Let’s instead turn our attention to triggering or invoking the event. Let’s add back in the FInvoke field and the Invoke property:
type
TMulticastEvent<T> = class(TMulticastEvent)
strict private
FInvoke: T;
public
procedure Add(const AMethod: T); overload;
procedure Remove(const AMethod: T); overload;
function IndexOf(const AMethod: T): Integer; overload;
property Invoke: T read FInvoke;
end;
So here we are again. What is assigned to the FInvoke field? This is where yet another trick from my bag comes out. We’re going to leverage some of the dynamic dispatching code that has been laying somewhat dormant down in the ObjAuto.pas unit for many Delphi releases. If you haven’t had the chance to look at some of the little gems that live in that unit and the associated ObjComAuto.pas, there is some pretty powerful bits of code in there. ObjAuto’s primary purpose is to allow late-bound calling of object methods. It does this through some rather rich RTTI that the compiler has generated for several releases. A lot of this extra information is generated for public and published methods on classes and descendants that have been declared within a {$METHODINFO ON} block. However, for method pointer types, this information is always available regardless of that setting. While I’m not going to be generating code at runtime, I will be calculating some information about call signature of the method at runtime from this extra RTTI.
In ObjAuto, there is an interesting global function called CreateMethodPointer(). This function takes a pointer to a type data record (the compiler RTTI) and an interface. It returns a TMethod record that points to a stub object and method that knows how to "pick apart" the passed in parameters convert them to Variants and then call some methods on the interface. Don’t worry, we’re not going down that (using Variants) road! Down in the implementation of this unit was something much closer to what I wanted. I just need raw data structure that was an opaque representation of the Invoke call. I don’t care what the data is, only how large it is and where it is located. IOW, what data is in a register and what data is on the stack and how much. I won’t go into the raw details of the changes made down in ObjAuto since you’ll be able to see that once Tiburón ships. I added another overloaded CreateMethodPointer() function, that takes a method pointer callback instead of an interface and a pointer to a type data record. Here’s what the declarations look like:
type
...
PParameters = ^TParameters;
TParameters = packed record
Registers: array[paEDX..paECX] of Cardinal;
Stack: array[0..1023] of Byte;
end;
TDynamicInvokeEvent = procedure (Params: PParameters; StackSize: Integer) of object;
function CreateMethodPointer(const ADynamicInvokeEvent: TDynamicInvokeEvent; TypeData: PTypeData): TMethod; overload;
procedure ReleaseMethodPointer(MethodPointer: TMethod);
Call CreateMethodPointer with a reference to a method of type TDynamicInvokeEvent and the type data for the method pointer (T). It will return a TMethod record, which is essentially a raw method pointer. We’ll use this value to assign to FInvoke. The trick is to get around the fact that the compiler won’t allow this cast:
FInvoke := T(CreateMethodPointer(...)); // compiler chokes on this code
We have to resort to another assembler trick. Remember how I said that passing a method pointer as a parameter is a little different that with records? In this case, it’s not going to matter because we’re going to declare a method in the generic class that takes a "var" parameter. In this case the compiler passes the address of the method pointer variable rather than the value. Let’s add this method:
type
TMulticastEvent<T> = class(TMulticastEvent)
strict private
FInvoke: T;
procedure SetEventDispatcher(var ADispatcher: T; ATypeData: PTypeData);
public
< same as above >
end;
procedure TMulticastEvent.SetEventDispatcher(var ADispatcher: T; ATypeData: PTypeData);
begin
InternalSetDispatcher;
end;
There’s another "InternalXXXX" call! That one is only slightly different than the others, but the effect is the same. InternalSetDispatcher simply jumps over to the SetDispatcher method on the TMulticastEvent type, which is declared and implemented on the base TMulticastEvent class:
type
TMulticastEvent = class
strict protected type TEvent = procedure of object;
strict private
FHandlers: array of TMethod;
FInternalDispatcher: TMethod; // this class needs to keep it's own reference for cleanup later
...
procedure InternalInvoke(Params: PParameters; StackSize: Integer);
procedure SetDispatcher(var AMethod: TMethod; ATypeData: PTypeData);
protected
...
procedure InternalSetDispatcher;
end;
procedure TMulticastEvent.SetDispatcher(var AMethod: TMethod; ATypeData: PTypeData);
begin
if Assigned(FInternalDispatcher.Code) and Assigned(FInternalDispatcher.Data) then
ReleaseMethodPointer(FInternalDispatcher);
FInternalDispatcher := CreateMethodPointer(InternalInvoke, ATypeData);
AMethod := FInternalDispatcher;
end;
We’re almost there. One of the last bits to cover is the InternalInvoke method and the constructor of the generic TMulticastEvent class. InternalInvoke is called from the little stub class instance created by the CreateMethodPointer() call. It passes in the pointer to the TParameters record and the space taken up on the stack by the parameters that "spill" from the CPU registers. As you may have already guessed, we’re going to drop down to some more assembly:
procedure TMulticastEvent.InternalInvoke(Params: PParameters; StackSize: Integer);
var
LMethod: TMethod;
begin
for LMethod in FEvents do
begin
// Check to see if there is anything on the stack.
if StackSize > 0 then
asm
// if there are items on the stack, allocate the space there and
// move that data over.
MOV ECX,StackSize
SUB ESP,ECX
MOV EDX,ESP
MOV EAX,Params
LEA EAX,[EAX].TParameters.Stack[8]
CALL System.Move
end;
asm
// Now we need to load up the registers. EDX and ECX may have some data
// so load them on up.
MOV EAX,Params
MOV EDX,[EAX].TParameters.Registers.DWORD[0]
MOV ECX,[EAX].TParameters.Registers.DWORD[4]
// EAX is always "Self" and it changes on a per method pointer instance, so
// grab it out of the method data.
MOV EAX,LMethod.Data
// Now we call the method. This depends on the fact that the called method
// will clean up the stack if we did any manipulations above.
CALL LMethod.Code
end;
end;
end;
Now the constructor for the generic version of TMulticastEvent<T>:
constructor TMulticastEvent<T>.Create; var MethInfo: PTypeInfo; TypeData: PTypeData; begin MethInfo := TypeInfo(T); TypeData := GetTypeData(MethInfo); inherited Create; Assert(MethInfo.Kind = tkMethod, 'T must be a method pointer type'); SetEventDispatcher(FInvoke, TypeData); end;
Notice the Assert, that is there because we cannot "constrain" the generic type at compile-time to only accept method pointer types for "T". So we have to do that check at run-time. This is something we’ll look at for future releases. We can safely call SetEventDispatcher because FInvoke will be of type "T".
There you have it, a generic multicast event. At some point, I’ll present some extensions that will help make using this easier by adding some automatic cleanup of the multicast event instance itself and a technique for using the source event itself to be the only instance reference. For now here’s one potential use scenario:
procedure TForm1.FormCreate(Sender: TObject); var E: TMulticastEvent<TNotifyEvent>; begin E := TMulticastEvent<TNotifyEvent>.Create; // Since the Invoke property is of type "T" and T = TNotifyEvent, you can // Assign it directly to any event of type TNotifyEvent, such as OnClick. Button1.OnClick := E.Invoke; // Now you can add as many "listeners" as you want. E.Add(Button1Click); E.Add(AnotherButtonClick); end;
I will try to get the full code posted within the next few weeks or so. I still have Tiburón to get out the door and take a few days off.
There are also a lot of caveats and cases where a multicast event can really mess things up. For instance, what if the method pointer had a result type? What if it had one or more "var" or and "out" parameters? Which method invocation in the list of listening handlers decides what the result is, or assigns to an "out" parameter? What if an exception is raised? Are there any ordering dependencies or expectations? From a framework designer standpoint, these are some real questions one has to ask. Making all events multicast is akin to the shotgun approach of making all methods virtual. It doesn’t really foster extensibility in the way one would think. As a matter of fact it can serve to severely limit extensibility and evolve-ability of a given framework.
There are many types of events that lend themselves very well to supporting multicasting, and there are plenty of other cases where multicasting only creates more problems than it solves. Having multicast events as the default behavior for any event type is certainly nice from a consistency and overall availability standpoint, but it just makes it way too easy to hang yourself inadvertently. Supporting multicasting for an event source should be a decision left to the designer of the class on which the event is placed.
Share This | Email this page to a friend
posted @ Fri, 15 Aug 2008 23:12:59 +0000 by Allen Bauer
Random Thoughts on the Passing Scene #76
- Nice interview with our new CEO, Wayne Williams, about the acquisition. (Keep in mind that when Wayne says "multi-platform" in a context like this magazine, he means database platforms.)
- The Sneak Peaks for Tiburon now have their own web page. The latest addition there is "What’s new in the IDE for Delphi 2009"
- Follow Peter Below as he chases a keystroke down the rabbit hole.
- Now this is cool: Pawel is using Delphi to automate ER/Studio.
- Another Delphi for PHP website, this time for Facebook: (Holding page at http://www.quizlister.com/)
Share This | Email this page to a friend
posted @ Fri, 15 Aug 2008 21:14:28 +0000 by Nick Hodges
Tiburon — All About Native Code
Tiburon is coming pretty soon, if you haven’t already figured that out. But what exactly is it?
Well, Tiburon is all about native code development. That which is code-named "Tiburon" is really the release of two separate products: Delphi 2009 and C++Builder 2009. In addition, you’ll be able to buy the two of them bundled together for a special price. Both will develop pure, native Win32 applications. If you install both, they will integrate into a single IDE.
These two products represent our strong, continuing commitment and focus on native code development — as well as on incredibly easy database access, on powerful feature rich GUIs, and on internationalizing your applications. We know that native code is important to you, and so we have focused our team on providing just that. Delphi 2009 and C++Builder 2009 are the best native RAD development tools anywhere.
As in the past, both products will come in Professional and Enterprise editions. But this time around, we will be adding an Architect Edition. An Architect Edition is new for Delphi and C++Builder, and it will be signified by the inclusion ER/Studio Developer Edition. We’ve worked with our new DatabaseGear product line to include a special, powerful, completely working version of ER/Studio in an Architect Edition for both Delphi and C++Builder that focuses on supporting the databases that Delphi Enterprise supports. This should be a very compelling upgrade for you folks who manage, develop, and design databases. If you normally buy the enterprise, this is definitely the time for you to step up to the Architect Edition.
This time around you’ll be seeing new VCL features, new language features, new IDE features, new globalization features, and a new edition with powerful database design tools. It all should add up to the best Delphi release ever.
Some questions you might be asking:
Does this mean that the IDE won’t require the .NET Framework? No, the IDE will continue to require the .Net framework, just like it requires other DLLs and binaries that are part of Windows. When it comes to our IDE, we view .NET as a powerful, useful framework that we can leverage to the advantage of our customers. For instance, we leverage the .NET framework for our build system and our modelling support.
What about your .NET plans? We will be coming out with an updated roadmap for our .Net solution that we believe will be feature rich and very compelling. It will include up to date support for the latest Microsoft .NET based frameworks and other CLR-based technologies.
If I have RAD Studio Software Assurance, do I get Delphi 2009 and C++Builder as part of my contract? Yes.
Share This | Email this page to a friend
posted @ Thu, 14 Aug 2008 22:51:53 +0000 by Nick Hodges
Off-topic or not?
The off-topic group on our old (and now dead) NNTP server existed as a runoff for unwanted posts in the technical groups. The intent was to have a group for people who felt that they needed to communicate their off-topic thoughts outside of the technical groups, and also to have a group to re-direct off-topic (including thread drift) posts to.
We’re now debating whether or not to kill off-topic completely.
You can have your say in the following poll that we put together:
https://forums.codegear.com/poll.jspa?pollID=9
The ideas behind the possible responses are as follows:
1. You either participate in off-topic so much that you would bail if it disappeared, or you believe that the existence of off-topic keeps the technical groups clean from unwanted stuff and it would make the technical groups rot so bad that you’d bail.
2. You would post all your off-topic posts in our technical forums if off-topic went away, or you believe that others would post all their off-topic posts (that are now in the off-topic group) and make the technical groups hard to navigate.
3. You’d be just as happy with off-topic going away, and you’d keep our technical groups clean by not posting anything off-topic grade…
4. You’d live with whatever the outcome is
5. You couldn’t care less
Thanks!
Share This | Email this page to a friend
posted @ Thu, 14 Aug 2008 21:35:28 +0000 by Anders Ohlsson
Ready for Tiburon: TsiLang Components
http://www.tsilang.com — This is particularly nice given the new Globalization features of Tiburon
Share This | Email this page to a friend
posted @ Thu, 14 Aug 2008 18:44:51 +0000 by Nick Hodges
Automating ER/Studio from Delphi 2009 - Getting Started
Last month I’ve been in Toronto on Embarcadero technical kick-off session where software consultants, product managers, and all technical stuff were sharing knowledge about CodeGear and DatabaseGear products, technologies, plans, roadmaps and - most importantly - building team spirit of our new, bigger family of techies. Right after the kick-off I went for summer vacation to sunny Croatia and that is the reason for the delay in writing this post.
I would say that 95% of Delphi and C++Builder applications are using some kind of a database. While Delphi already provides everything you need to rapidly build database applications, it is not aimed at full featured cross-platform database modelling like ER/Studio. I’ve been using many of the 3rd party programs to build my databases, but I’m really impressed with the capabilities of ER/Studio.
Before joining Borland/CodeGear/Embarcadero I’ve spent 5 years writing Delphi 3-5 applications that were automating Excel for creating nicely formatted spreadsheets full of figures coming from a big data warehouse at the financial controlling department at one the biggest retail banks in Poland. Some of my colleagues were afraid that there would not be enough work left for them to do if you can have a Delphi applications that generates Excel files on a massive scale:-)
My first inspiration for writing automation code in Delphi was an excellent article "Delphi and Microsoft Office: Automating Excel and Word" by Charlie Calvert - currently working as C# Evangelist at Microsoft. Another great source of information at the time was Binh Ly’s great "Techvanguards" site. Nowadays there is a very nice collection of links to Delphi Automation resources on the "OLE/COM/DCOM/Automation/ActiveX - Delphi knowledge base" site.
It will take some time until CodeGear and DatabaseGear products have deep integrations, but there are some things possible today. ER/Studio can be automated through OLE Automation and there are lots of existing VBA macros that make use of the rich object model of ER/Studio. Why not to go one step further and automate ER/Studio using Delphi?
In this post I’m going to present step-by-step instructions on creating a Delphi VCL Forms Win32 application that starts ER/Studio, displays its version number and closes it. Nothing fancy but just the starting point for more complicated scenarios. I will be using the current shipping version of ER/Studio 7.6.0 and the beta version of Delphi 2009 aka Tiburon.
Delphi provides the "Import Component" wizard available from the "Components" menu that can take a COM type library and generate VCL components that wrap functionality provided by classes defined in a type library. If you want to install these components into the "Tool Pallette" all you need is to create a "Package" project, add to it a Delphi unit generated by the wizard, and install the package into the IDE. In Delphi 2009 the "Import Component" wizard is even easier to use as described in the "Tiburon Sneak Peek: Import Component Wizard" blog post by Chris Bensen. You can start directly from the wizard and the package project will be generated for you automatically. There is also a very handy new addition of search functionality in the "Registered Type Libraries" tab, so you do not have to scroll through the list of all registered type libraries, which typically tends to be very long.
Importing ER/Studio type library into Delphi
First make sure that ER/Studio and RAD Studio are installed on your machine and then start "CodeGear RAD Studio" and select "Import Component" from the "Components" main menu. This will display the "Import Component" wizard.
On the first "Personality, Framework and Platform" tab select "VCL for Delphi Win32" and click the "Next >>" button. Note that if you have started RAD Studio using "Delphi 2009" shortcut this tab would not appear, because the choice of the IDE personality to be used has been already made.
On the next tab select "Import a Type Library" option and click "Next >>".
The next tab is titled "Registered Type Libraries". Here we can use the new Delphi 2009 search functionality and locate "ER/Studio Type Library", which can be found in the "C:\Program Files\Embarcadero\ERStudio7.6\ERSTUDIO.exe" file.
On the next tab we can specify the name of the Palette Page, where generated components should be installed. I have entered "ER Studio" here, but this could be anything. Also make sure to check the "Generate Component Wrappers" option and click "Next>>".
On the next tab we are going to select the new Delphi 2009 option "Install to New Package", which will save us a step of creating a package project.
On the next screen we need to specify the name and the location of the new package project and also an optional description of the project that will appear in the list of installed packages. I have chosen for "D2009ERS76" project name and location under Delphi 2009 demos, but again this could be anything.
This is the last screen of the wizard. All necessary information was provided, so when we click on the "Finish" button, the wizard will generate a new "Package" project and import the ER/Studio type library into the "ERStudio_tlb.pas" Delphi unit. Now it is a good time to save the project, using either "File \ Save" menu or just press "Ctrl+S" keyboard shortcut.
Initially you will see a number of errors in the "Errors" section of the "Structure" view related to some comments in the generated unit not starting from "//". These errors are easy to fix. Just select these lines of code within generated comment blocks that do not start from "//" and use "Ctrl+/" shortcut to automatically comment them out. The fastest way to identify offending lines is through trying to compile the project. To compile the project you can either select "Project \ Compile" from menu or right click on the project node in the "Project Manager" and select "Compile" from the context menu or just do "Ctrl+F9". After commenting out all offending lines you should see the "Done: Compiled" message.
There is one more not obvious thing to do before we would be able to install the package into the IDE.
It is not allowed to have more then one component of the same name in the "Tool Palette". Delphi comes with the preinstalled "TShape" component and one of the generated component classes also happened to be named "TShape". In order to be able to install our ER/Studio package into the "Tool Palette" we need to rename the generated "TShape" component name to something else, for example "TShapeERS". This is where Delphi "Rename" refactoring comes handy.
First let’s locate the "TShape = class(TOleServer)" declaration inside the generated unit. For this just select "Ctrl+F" to display "Find Text" dialog and enter "TShape = class" into the text to find edit and click OK. Position cursor inside the "TShape" identifier, right click and select "Refactoring \ Rename" option from the context menu to display the "Rename" refactoring dialog (or just select "Shift+Ctrl+E" shortcut).
Type in "TShapeERS" in the "New name" editbox and click OK. This will display "Refactoring" tab with the list of all places that would be affected by the refactoring. Just click on the Rubic cube icon or press "Ctrl+R" to perform the rename refactoring and change all occurences of "TShape" to "TShapeERS".
Now we are ready to install the components into the "Tool Palette". Save the project for example by "Ctrl+S" and then right click on the project name ("D2009ERS78.bpl") inside the Project Manager window and select "Install" from the context menu. This will compile the project and display the information message with the confirmation that our package has been installed and with the names of all components that have been registered.
Very impressive number of components, I must say:-) If I count them correctly there are 208 new components installed. It looks like lots of things to automate;-)
At this point we are done with importing ER/Studio type library into the IDE and we can now start using generated components in Delphi VCL Forms applications. Select "File \ Close All" to close the package project.
Simple Delphi ER/Studio Automation
Let’s try to automate ER/Studio from Delphi application. Select "File \ New \ VCL Forms Application - Delphi" to create an empty application. Select "File \ Save All" and give the application and its main form any name that makes sense to you. I’ve chosen for "ERSVersionInfo" for the project and left the default "Unit1" for the the main form.
If you expand the new "ER Studio" tab on the "Tool Palette" you should see all the new components ready to be dropped on the form, but which one to start from?
In the ER/Studio installation directory there is the "ERStudio Automation Interface Reference" (AIRef.chm) help file that contains all the necessary information to get us started. The ER/Studio can be automated by external application or from inside using macros. We are going to use the first scenario and automate ER/Studio externally using our Delphi VCL Forms application. According to the reference external automation programs should start from an instance of an "Application" object.
We start from dropping the "TApplication" component on the form. Just start typing "Application" in the top of the Tool Palette to find the "TApplication" component and double-click it to add it to the form. To keep things simple we’re just going to display the version number of the currently installed version of ER/Studio.
All wrapper components generated by the "Import Component" derive from the "TOleServer" class and inherit from it some published properties that show up in the Object Inspector at the design time. After dropping the "TApplication" component on the form I have changed "AutoConnect" and "AutoQuit" from "false" to "true", so the ER/Studio starts and stops automatically when our Delphi application starts and stops.
I’ve also dropped a button component on the form to display ER/Studio version number. It is only one line of code that needs to be written in the button’s "OnClick" event.
And now we are done! Just click on the green arrow icon to run the application (or just press "F9") and we should see a little form with just one button. At the moment the application starts, the ER/Studio starts automatically as well. If you press the "Show Version" button you should see the following message:
When you close the Delphi application, the ER/Studio will close as well.
Summary
Delphi is the best tool for building native Windows applications. The support for COM Automation was introduced many years ago in Delphi 3 and it is still being improved, also in the new Delphi 2009. ER/Studio exposes very rich object model that can be accessed by external applications to automate its functionality. In this article I have demonstrated importing ER/Studio type library into Delphi and created a very simple application that is just the starting point for more useful automation scenarios.
The Delphi package project with ER/Studio VCL component wrappers ready to be installed into Delphi is available for download Code Central.
Go Delphi! Go ER/Studio! Go Embarcadero!
Share This | Email this page to a friend
posted @ Wed, 13 Aug 2008 10:38:14 +0000 by Pawel Glowacki
Server Response from: dnrh2.codegear.com
