HOME - - - / - - / - - / TUTORIALS INDEX - - - / - - / - - / - - / - - - Other material for programmers

Delphi: Making and using external units, DCUs

This page is information rich, and a has search button at the bottom of the page.

Please don't dismiss it because it isn't full of graphics, scripts, cookies, etc!

Click here if you want to know more about the source and format of these pages.
This is a long tutorial... if you read it to the end. You may be pleased to know that you can "do things" with just a third of the tutorial. It starts with an answer to how you can set up things to re-use and re-use, and then goes forward from there into details which not everyone will need.

As your programming skills develop, you will eventually find yourself putting certain things into program after program. The good people at Borland have provided "off the shelf" answers to many, many problems, but one of the great virtues of computers is their flexibility. What you use yours for is likely to be different from what you use yours for. Even if Borland could anticipate everyone's needs and wants, it would have been foolish of them to inflate Delphi to incorporate every programmer's every want.

Happily, there are several ways for you to "add words" to the language.

The simplest, of course, is to declare and define a procedure. This is a basic skill that you should already have, given the fact that you are reading a Level 3 tutorial.

The most elegant solution is to add a new component... but that's beyond Level 3.

So what about a happy solution from the middle ground? Coming up!

For the sake of an example, let's say that you want to "add" CopyrightAndVersion(sVer:string) to the language. When your program executes CopyRightAndVersion('1.0.0'), a messagebox will pop up on the screen saying

    This program copyright Sheepdog Software.
    1.0.0
You could, of course, have a constant in your program....
        const sVersion='1.0.0';

... and then call...
        CopyRightAndVersion(sVersion);

... at some suitable point.


Just to be sure we're clear on the basics of how to do what is wanted, we'll do it first as part of the program it is used from. The technique for "packaging" code for use by many programs which this tutorial presents is amenable to having development done first within a program, and then having the polished code moved to the "general access" container.

Start a project. Call the form DD52f1. Save the project in a folder called DD52. Call the unit DD52u1. Call the project DD52.

Just before the "var" in the unit's code... NOT within the TDD52f1 class definition... add....


procedure CopyrightAndVersion(sVer:string);
Just before the "end." at the end of the code, add...

procedure CopyrightAndVersion(sVer:string);
begin
showmessage('This program copyright Sheepdog Software.'+
    chr(13)+'Version: '+sVer);
end;
Add a button to your form. Make it's OnClick handler say...

procedure TDD52f1.Button1Click(Sender: TObject);
begin
CopyrightAndVersion('1.0.0');
end;
That should now work as advertised!


Two "minor" tweaks....

a) The first is something I would always recommend. It isn't special to what we are doing in this tutorial. Just after the {$R *.DFM} which Delphi provides, add...

{$R+}// switch on range checking
These two "{$R.." lines have nothing to do with each other, despite their similarity. Turning on the range checking is always worthwhile. It sometimes slows a program's execution, but you can take it out after the program is debugged if speed is being impaired. If it is causing speed problems, I'd take the trouble to turn range checking on and off in different parts of the program, leaving it on everywhere possible.

b) Near the start of the program (where it is easy to find and keep up to date), just after the "uses" clause, add....

const sVersion='1.0.0';
In the button's OnClick handler, replace '1.0.0' with sVersion Why bother? It is just an example of the sort of "tidiness" which leaves bugs few places to hide. Having sVer='1.0.0' where we've put it makes it easily accessible. Putting it in a constant means that if we accidentally try to change the value in it somewhere, the compiler will complain.

Because of the wonders of "scope", you could in fact use the name sVer for both the constant, and where we've seen it used already, and there would be no problems... and still little room for confusion. You can reflect on that, derive the learning available from it, or let it pass... the point isn't critical just now.


Congratulations! You now have a working CopyrightAndVersion message generator. Now we will move it an external unit. I'll show you how to use it from there in a moment.

Leaving your existing application open, use the Delphi menu to do File|New|Unit.

Invoke a Save All, and a Save As dialog will pop up in respect of the unit you just created. Create a new folder called DD53, and in that folder, save the new unit as DD53sau. (The new folder can have any path, but it makes sense to put it at the same level as DD52 in your hard disc's tree of folders.) It gets a different index (53, instead of 52) because, although 52 will use things from 53, this unit is not "part of" 52. That's the whole point, after all! DD53sau is a general resource, which many programs will use. The "sau" part of the name, like the "DD" ("Delphi Demo"), is arbitrary, but I use "sau" to say that this is a "stand alone unit", one to "plug in" to many projects.

To unit DD53sau, just after "interface", add uses Dialogs;

(You've had to do this by hand because... ummm... I'm not sure! But "showmessage" is defined in the Dialogs unit... which Delphi usually adds to units for you at compile time, but not when you're doing what we're doing. To find out what unit anything comes from, put the cursor in the problem item and press ctrl-F1.)

Use cut/ paste to remove things from DD52u1 and put them in DD53sau, until the latter looks like...

unit DD53sau;

interface

uses Dialogs;

procedure CopyrightAndVersion(sVer:string);//from DD52u1

implementation

procedure CopyrightAndVersion(sVer:string);//from DD52u1
begin                                      //from DD52u1
showmessage('Copyright...'+sVer);          //from DD52u1
end;                                       //from DD52u1

end.
Note I said cut and paste... You don't want the marked lines to appear in DD52u1 as well.

To the "Uses" clause in DD52u1, add DD53sau

Your program should run, doing what it did before.... apparently. It is just doing it by a different method.


Now we embark upon a little work to make the unit generally available.

N.B.: You will have to be careful to keep control of various versions of the unit which will sprout like mushrooms, if you are not careful. Part of the joy of using re-cycled units is that is you find a bug, or make an improvement, all the programs using that unit, once re-compiled, benefit from the improvement... but that sword has two edges.

First, if you don't have one already, I'd recommend creating a folder to hold your current "stable- and- working" versions of any SAUs you intend creating. Here you will keep the version to be used when compiling something that uses any of your SAUs. If you decide to do some work on the internals of a given SAU, I would do that elsewhere, only copying the result to your "stable- and- working- version" SAU folder when you're sure that you haven't "broken" things which were working. I'm going to call the folder ToUseCopiesSAUs

You must also choose a path for the folder. While I hate having things in C:'s root, for the sake of simplicity, I'm going to make an exception for this folder. There are more clever solutions which you may wish to use.

So... having created the relevant folder, you must then copy DD53sau.dcu to it. You must also tell Delphi to look for SAUs there. Complete the former task first. Then the latter task is accomplished by making an entry in the "Search Path" list box on the Directories/Conditionals tab of the dialog box you get via Project|Options. Note: This setting is saved with the project. When you open a new project, you will have to repeat the "tell Delphi where to look" step, but it will remember what you've told it from then on, for that project.

You only need to have DD53sau.dcu in ToUseCopiesSAUs. You need to keep a copy of DD53sau.pas in case you want to alter the code inside DD53sau, but by not placing a copy of DD53sau.pas in ToUseCopiesSAUs, you reduce the risk of inadvertently creating multiple, conflicting versions of the SAU.

We're now going to take a highly unusual step. Do View|Units, and you will see not only DD52u1, but also something called just DD52. Click on that, click okay, and you'll see a file that you usually tamper with at your peril....

program DD52;

uses
  Forms,
  DD52u1 in 'DD52u1.pas' {DD52f1},
  DD53sau in 'DD53sau.pas';

{$R *.RES}

begin
  Application.Initialize;
  Application.CreateForm(TDD52f1, DD52f1);
  Application.Run;
end.
Take out the reference to DD53sau, making the uses clause....

uses
  Forms,
  DD52u1 in 'DD52u1.pas' {DD52f1};
(Note that we didn't just delete the DD53sau line... the comma at the end of the previous line had to go, and the semi-colon from the end of the DD53sau line took the comma's place.

It is highly irregular to tamper with the .dpr file, but it seems the right thing to do here.


So! We have our sau. We have the dcu from it in a folder for future use. Lets be sure we can do another project, using the CopyrightAndVersion procedure painlessly.

Close everything that is open at the moment. Restart Delphi, start a new project, call it DD54. Put a button on it. Make the button's OnClick handler say...

CopyrightAndVersion('testing from DD54');
Save everything.

If you try to run it now, you will get "Undeclared identifier: CopyrightAndVersion", which should be no surprise, if you think about it.

To DD54's uses clause, add DD53sau

Use Delphi's Project|Options|Directories/Conditionals to put C:\ToUseCopiesSAUs in the Search Path.

Save everything. Now the program should run!!!

(You won't have to tamper with the .dpr file of DD54. The extraneous entry that arose when we were first building DD53sau does not arise when you merely set out to use a pre-existing SAU.)


The "How" of setting up SAUs is not so terribly difficult,

Deciding what things to put into SAUs is a more advanced art. Be too enthusiastic, and you'll put things in SAUs that never get used in more than one program. Be too timid, and you'll be re-copying similar code into program after program... and then having to edit all the different copies of that as improvements are discovered.

If something is a candidate for an SAU, try hard to generalize the function or procedure. Your SAUs will often be helped by good use of parameter passing, both simple passing, as in our CopyrightAndVersion procedure (value parameters), and the more complex passing of "var" type parameters (variable parameters). You will also benefit from mastering passing user defined records.


So! You've got this great little SAU called DD53sau up and working. There's a copy of it's .dcu in C:\ToUseCopiesSAUs. You've used it in 5 applications.... and now you decide to rename your company "Border Collie Software", so you want the copyright message to read 'Copyright Border Collie Software...". How do you proceed? If you have an old DD53 folder, with the "to work on" copy of DD53.pas in it, fine... you can skip some of what follows... But don't get DD52 involved in any revising of DD53! If you took shortcuts earlier, didn't put DD53 in it's own folder from the start,. it would be as well to put DD53sau.pas in an otherwise empty folder at this time. You did save DD53sau.pas somewhere, didn't you?? It would also be an idea to create a backup copy of DD53sau.pas.

Close everything that is open in Delphi.

Make a folder called DD53. Put DD53sau.pas in it.

Do File|New|Application in Delphi.

Save it. DD53DevU1.pas for the unit, DD53Dev for the project.

Add a button. Make the OnClick handler say....

CopyrightAndVersion('1.0.5');
Add DD53sau to the Uses clause.

IT should run at this point. You don't need the usual tweak of Project|Options|Directories... because DD53sau.pas is in the same folder as your current application.

Do File|Add To Project. You should see DD53sau.pas in the list that arises. Double click on it.

If you can't see the project's source code at the moment, press F12 to get it visible. select the DD53sau tab. Change the text in the showmessage call to "Copyright Border Collie Software, version: ".

Save all. Run the program. The copyright message should show the changes you made.

You now have a revised dcu. When you are sure all is well, copy it to C:\ToUseCopiesSAUs. From the moment you do that, if you compile any new project, or re-compile any old project, any calls of CopyrightAndVersion will result in the new text.


Hurrah!! That's it!!!


For extra credit: If you don't like the copyright message appearing in the middle of the screen, but would rather have it in the middle of your application's window, use the showmessagepos procedure. The basic idea is...

showmessagepos('Copyright...'+sVerson,10,200);
Try that, just to see what it gives you. Play with the numbers if all is not clear. Now refine it as follows...

showmessagepos('Copyright...'+sVersion,dd52f1.left+100,dd52f1.top+10);
Try that, make sure that much is working. And finally...

showmessagepos('Copyright...'+sVersion,
    dd52f1.left+(dd52f1.width div 2)-50,
    dd52f1.top+(dd52f1.height div 2)-50);
If you look at how that evolved, what it all means should be clear. The two "-50"s are fudges... The have to be fine tuned by hand, depending on the size of the message box. Without them, it is the message box's upper left corner which is in the center of the form. If you know a way to calculate "the right" amount to subtract, so that the messagebox's center is in the center of the form, I'd be pleased to hear from you.

If you want to use showmessagepos in DD53sau, A little complication will arise. "Inside" DD53sau, the compiler will be confused by the references to dd52f1. After all, you have made the SAU with a view to using it with other programs. dd52 may not be involved in any way. The answer is to pass information about the form that is involved.... You can proceed as follows. The "Magic" is in the new, extra parameter: objForm:TForm

DD53sau becomes....

unit DD53sau;

interface

uses Dialogs, Forms;

procedure CopyrightAndVersion(sVer:string;objForm:TForm);

implementation

procedure CopyrightAndVersion(sVer:string;objForm:TForm);
begin
showmessagepos('Copyright Border Collie Software'+sVer,
    objForm.left+(objForm.width div 2)-50,
    objForm.top+(objForm.height div 2)-50);
end;

end.
To call CopyrightAndVersion from a form called DD53DevF1, you would invoke...

CopyrightAndVersion('1.0.5',DD53DevF1);


Enjoy!




Click here if you're feeling kind! (Promotes my site via "Top100Borland")
   Search this site or the web        powered by FreeFind
 
  Site search Web search
Site Map    What's New    Search
Ad from page's editor: Yes.. I do enjoy compiling these things for you... hope they are helpful. However.. this doesn't pay my bills!!! If you find this stuff useful, (and you run an MS-DOS or Windows PC) please visit my freeware and shareware page, download something, and circulate it for me? Links on your page to this page would also be appreciated!

Click here to visit Sheepdog Software (tm), editor's freeware, shareware page.


Link to Tutorials main page
How to email or write this page's editor, Tom Boyd



Valid HTML 4.01 Transitional Page WILL BE tested for compliance with INDUSTRY (not MS-only) standards, using the free, publicly accessible validator at validator.w3.org


If this page causes a script to run, why? Because of things like Google panels, and the code for the search button. Why do I mention scripts? Be sure you know all you need to about spyware.

....... P a g e . . . E n d s .....