HOME - - - - - - - TUTORIALS INDEX - - - - - - - - - - - - Other material for programmers Delicious  Bookmark this on Delicious    Recommend to StumbleUpon

Delphi: Using or Writing DLLs

(This page previously had information about using InpOut32.dll, which has been moved to the page the link will take you to.)

Introduction: What DLLs are; why we want them; why we don't; alternatives.

"DLL" stands for "dynamic link library". When a programmer has something of general usefulness to other programmers, he or she may choose to "package" the material in a DLL. Users must have both the DLL and the program that uses the things in it on their system, but, once they do, using the program which uses the things in the DLL works just like using any other program. Only the programmers will be aware of the fact that routines the main program uses are contained in the external library.

I have a general disinclination for things being more complex than necessary. In general, I like my applications to require just one file, the .exe file. There are times, however, when the overhead of other files is warranted. For example, help files seem to be worth the "complexity".

DLLs are especially useful if you have something that many programs will use. I often work with chips in the Dallas 1-Wire family. One of those chips convert's its temperature to a digital signal that a program can process. Is is worth putting the read-the-chip code into every program that uses one the chips? Probably not. A DLL could be created which would encapsulate all of the "messy bits", and all any programmer would need to know is how to call the relevant routines within the DLL. Working with bits of code within DLLs is very like than working with procedures and functions within a monolithic program.


DLLs are also written when a supplier wants to keep control of aspects of their work. If I release the source code for a program reading the temperature sensor chips, then my opportunities for selling that immediately shrink. I could put it into a DLL, and restrict users' access with registration keys.

DLLS are also useful when someone is selling some hardware. Printer drivers are often DLLs.

If you have some Delphi code which is going to be useful in many Delphi programming projects, alternative ways of recycling your work (a good idea, for various reasons) are...



The glory of DDLs is that they are standalone files which you put on a machine, and other applications, written in almost any language, can use things from inside the DLL. Even a spreadsheet or fancy wordprocessing document can benefit from things in DLLs. I have a separate tutorial for you which explains the details of accessing functions and subroutines in a DDL from OpenOffice's ooCalc spreadsheet, and some additional comments on the subject further down this page..

Two parts: First: Using a DLL, then Writing a DLL.

The "Writing DLLs" section was reviewed March 2011, using Delphi 4 on a Windows XP, SP3, machine.

Using a DLL

To complete this tutorial, you will need to download a zip file. Within it, you will find DD50Demo.dll. Put a copy of that in the folder you want to work in. I would suggest calling the folder DD50.

Start an application in the usual way. Call the form DD50testF1, the unit DD50testU1, and the project DD50test.

Put a button on the form. call it buTestSayHi. Make the button's OnClick event handler say....

procedure TDD50testF1.buTestSayHiClick(Sender: TObject);
begin
SayHi;
end;
... and... just after the uses line near the start of the program, add the following...

procedure SayHi; stdcall external 'DD50demo.dll';
... and try running the program! If you get odd errors ("debugger kernel...", etc), be sure you have DD50demo.dll in the same folder as your program, and check for typos. When you click the button, you should get a message box on the screen with "Hi" in it. Inside the DLL, all SayHi does is "Showmessage('Hi');

(An unimportant aside: In Delphi 2, you could put the declaration of the call just before the "var" line, but in later versions that causes problems.)

Look over your program: It has SayHi; in the button OnClick handler. Ordinarily, you'd get a compile error saying that "SayHi" is not known to the compiler.

The line saying....
        procedure SayHi; stdcall external 'DD50demo.dll';
.. tells the compiler: "If you encounter "SayHi", then go to the DLL called DD50demo to see what to do." This line "declares" the procedure.

So far so good, I hope. Let's access two more things from the DLL...

After the SayHi line, add....
        showmessage('Hi came from DD50demo, version '+pcDLLVersion);
.... and after the declaration of SayHi, add...
        function pcDLLVersion:pchar; stdcall external 'DD50demo.dll';
//    N.B.: Do not use "string" type variables
Run the program now, and you see two messages. One comes from within the DLL, the other comes from within the program you have written.... but notice: part of the message in "your" message came from inside the DLL.

An aside: you will need to master pchar type strings to send things to or from DLLs. You can receive things from DLLs into a normal Delphi string, as we have just done, but otherwise: it is pchar time. They aren't a big hassle, just a distraction to get past.

While we're diverted from the main story anyway, let me mention:

The error....
"Debugger kernel error. Error code: 1."
... may arise if the DLL you are trying to use cannot be found. It should be in the same folder as your program, or in Windows/ system, or in Windows/System32. Alternatively, it may be that you have typos in lines like "procedure SayHi; stdcall external 'DD50demo.dll';".

Back to work!....

The things we've done to date have been pretty trivial.

Now we're going to use a more complicated function from inside the DLL. It's job is to figure out what the price of something would be with tax from what the price would be without tax, and the tax rate. In other words, if you knew something cost $100 without tax, and that the tax rate was 10%, the program should be able to tell you that the cost with tax would be $110.

The code gets a little messy, but only because to input and output numbers, we go through strings.

Just before the main "var", with the two similar lines we've already added, add the following to the program......
function singWithTax(singBeforeTax,singTaxRate:single):single; stdcall external 'DD50demo.dll';

Also add a new button, call it buCalcTax. For now, make it's OnClick handler say....
showmessage(floattostr(singWithTax(100,12)));
... and get that much running. When you run the program, and click buCalcTax, you should get a message with 112 in it. A little crude, so we'll develop it as follows... but you've just seen all of the DLL aspects of this.

Put a label on your form, and change buCalcTax's OnClick handler to...
label1.caption:=floattostr(singWithTax(100,12));
.. and now when you click the button, the answer should appear in the label.

Now add an edit box to the form. Set it's text property to 1000. Change buCalcTax's OnClick handler to...
label1.caption:=(floattostr(singWithTax(strtofloat(edit1.text),10)));
.. and now you can enter the "before tax" price in the edit box, and when you click the button, the with-tax price appears in the label. If you enter something non-numeric in the edit box, the program will fall over, but dealing with that isn't hard, just not germane to our topic: DLLs.

So! You've seen how to write programs to access things from within a DLL. How do you know what is in them? The suppliers of the DLL will provide that information, and / or there are software tools you can use to learn something of what's available.

If you want to try working with "real" DLLs, you could try to crack using cards.dll, which will be on any computer with Solitaire (silly game), or Freecell (well worth learning.) With cards.dll you can draw cards from within your own programs, with very little effort. For some good information on cards.dll visit Paul Kimmel's article on Developer.com. He works in VB... poor chap.. but what he has written should translate to Delphi. There are only three things to access within the DLL! I have to admit defeat in the half hour I spent trying to do it... if you succeed please let me know? (And no, I don't want to do it with the VCL wrappers that Google will show you.)

All of this started because I wanted to use things from within InpOut32.dll, which lets you access your PCs ports. I've written a tutorial on that, which I was able to do!

Anyway... you've completed the first part of my tutorial... you've seen how to use things from within a DLL. Next we'll look at writing a DLL, and what better DLL to write than the one you've just been using??!


Writing a DLL



If you have not already done so, create a new folder to work in, I was using one called DD50. If you created a DD50 for the first part of this tutorial, you can continue working there.

Start Delphi, do File| New| DLL

Just before the "begin" and "end." at the end of the library (which is much like the units we more frequently work with), insert...

procedure SayHi;stdcall;
begin
showmessage('Hi');
end;//SayHi
(I would add rems to the "begin" and "end" which were provided as part of the DLL skeleton....
begin //start of DLL

end.//of DLL
BE VERY CAREFUL to include the ";stdcall;" at the end of the first line, and in comparable places later on in this work. The Delphi error trapper doesn't seem as able to spot errors when you are making DLLs, at least in Delphi version 2. Absence of that ";stdcall;" cost me considerable wasted time while creating this tutorial!

Save your work. Be sure to navigate to the folder you set up, say DD50, and save the project as DD50.dpr. You won't be asked about saving anything else. Don't try to run or compile it yet.

The "Showmessage" procedure is one that you are likely to have used frequently. It is provided for you in the Delphi Dialogs unit. Unlike working with application units, you will need to add "Dialogs" to the uses clause of the DLL manually, making it....

uses
  SysUtils,
  Classes,
  Dialogs;
You don't ordinarily have to add the unit name for things provided by Delphi as you build your code, but you do while building DLLs.

Now you must create an "exports" clause. It is as follows...

exports SayHi;
It goes just before the...
begin //start of DDL

end. //of DLL
...at the end of the library.

Now try to compile your DLL. You can either use ctrl-F9, or, go though the menus, using Project| Compile DD50. If there are no errors in your code, you won't see much. If there are errors... fix them!

By the way: If you try to run the code, you'll get a message saying you can't. This is normal. Don't try to run the code, but don't worry if you try, and get the "can't" message. This isn't something for you to fix. You will eventually compile your code, and then access the DLL that has been created.. but access it with an separate, full, .exe application. DLLs can't run on their own, like an .exe.

With Windows Explorer or similar, you should now see a file called DD50.DLL in your DD50 folder. The program you wrote for the first part of this tutorial should work with it. You can proceed in either of two ways...

Option 1: Rename the old "DD50demo.DLL" as "DD50demo.old", and rename "DD50.DLL" (Which you just created) "DD50demo.DLL"

Option 2: Re-open DD50Test, and change the references to "DD50demo.DLL" to "DD50.DLL"

Option 2 has the advantage of allowing you to easily make further changes to either the DLL or DD50Test to see what happens.

In any case, so far so good. Now we will add the sDLLVersion function. Just after the "SayHi" function, add....

function pcDLLVersion:pchar;stdcall;
begin
result:='1.0.0';
end;
(The pchar type is similar to the string type. You can't use string type variables for passing things to and from DLLs. If you do, you may get an "access violation" message when you run the program.)

You must also and, extend the "exports" clause as follows...

exports SayHi,
        pcDLLVersion;
Yes... you list just the routine's name in the exports clause... you don't indicate in any way the parameters that go with it.

Forgetting to add the function or procedure to the exports clause is another way to get the "Debugger kernel" error message.


Calling our DLL from OpenOffice

So! We have a DLL. The "innards" don't matter now... That's one of the delights of DLLs!! But what is it good for?

The DLL we've written? Not a lot... except it does give us a "tame" DLL we can use to test our grasp of using things within DLLs.

The DLL we've created (you can just harvest the completed DLL from the dd50 DDL tutorial zip archive, if you wish) can be used with OpenOffice. In, for instance, the spreadsheet module, ooCalc, you can set things up so that you can have a cell display the string returned by the pcDLLVersion function inside the DLL. Now, in our "for exercises" DLL, that is always merely "1.0.0" But there is no reason that you couldn't write (or buy) a different DLL which would return the temperature sensed by some electronics monitored by the software in the DLL.

In addition to supplying the ooCalc spreadsheet with values, the DLL can be used to extend the capabilities of an ooCalc worksheet. Again, the trivial example which can be demonstrated with dd50demo.dll is the display of "Hello World" on the screen in a message box. Of course, you can get ooCalc to do this without resorting to a subroutine in a DLL, but if you can get a message box to pop up, imagine the other things that could be taken care of inside a DLL?

I have a separate tutorial which explains the details of accessing functions and subroutines in a DDL from an OpenOffice ooCalc worksheet, which, Oh Google, some people call a "spreadsheet", of course. (I prefer to reserve "spreadsheet for VisiCalc, ooCalc, Excel, etc... the programs, and "worksheet" for the sort of document manipulated with a spreadsheet.)


An extra delight...

So! I hope you feel you've got the basic idea? To wrap up, we'll be a little more adventurous.

To make a start, we'll do something not using a DLL, but putting the code in our program. Then we'll move it out to the DLL, just to see how easy it can be.

We're going to create a function that works out the price "with tax" of something from it's "before tax" price. If the tax rate is 12, and the before-tax price is $100, then the with-tax price is $112.

Before you read on: In the wise words that Mr. Dent had trouble abiding by: Don't panic! The following code looks a little convoluted, but it is 99% because of the "joys" of working with percents, and of moving things from strings to numbers and back again. Just forge ahead to where it says "Breath again".....

Add the following function to the program you've been using to test your DLL:

function singWithTax(singBeforeTax,singTaxRate:single):single;
begin
result:=singBeforeTax*((singTaxRate/100)+1);
end;

As with any function, you'll also need to declare it in the interface section, so, just after "function pcDLLVersion:pchar; stdcall..." add...

function singWithTax(singBeforeTax,singTaxRate:single):single;

Put an edit box on the form, and a button. Make the edit box's text property 100. Call the button buCalcTax. Make the buttons's OnClick handler....

procedure TDD50testF1.buCalcTaxClick(Sender: TObject);
begin
label1.caption:=floattostr(singWithTax(strtofloat(edit1.text),12));
end;

Okay! Breath again!

That code should work. It will throw up errors if you have something other than a number in the edit box when you click "CalcTax", but you'd expect that, I hope? (It isn't a big issue, and a distraction from out quest to master DLLs... Let it go.)

Get it working!

Now we're going to move the function over to the DLL. Why not leave it in the program? You could... but if you wanted to write a "singWithTax" once and for all, never have to fuss with it again, no matter how many programs you write, you could do so, put it in a DLL, and henceforth just bundle the DLL with any new programs, and just call singWithTax from the DLL. (There are other ways to "package" things you want to use again and again for your own purposes... but that's a story for another day. You are writing this DLL so that you'll have a better grasp of what you are doing when you use other people's DLLs.)

So... to move the function to the DLL...

Don't try running anything until further notice.

In the program that tests the DLL, move the first line saying....

function singWithTax(singBeforeTax,singTaxRate:single):single;

... to just after the program's uses clause, and modify it to say....

function singWithTax(singBeforeTax,singTaxRate:single):single; stdcall external 'DD50demo.dll';

From the interface part of your program that tests your DLL, cut (select, then press ctrl-X) all of the following:

function singWithTax(singBeforeTax,singTaxRate:single):single;
begin
result:=singBeforeTax*((singTaxRate/100)+1);
end;

Save your program that tests the DLL.

Open the DLL itself for editing and re- compiling.

Put your insertion point just before the word "exports", paste (ctrl-V) what you cut a moment ago, and add...

;stdcall;

...to the first line of the material you just imported into the DLL.

Add "singWithTax" to the "exports" clause.

Save the DLL. Compile it (ctrl-F9). Close that; Run the program that tests the DLL... it should run okay! You've done it!




            powered by FreeFind
  Site search Web search
Site Map    What's New    Search This search merely looks for the words you enter. It won't answer "Where can I download InpOut32?"


Click here if you're feeling kind! (Promotes my site via "Top100Borland")

If you visit 1&1's site from here, it helps me. They host my website, and I wouldn't put this link up for them if I wasn't happy with their service. They offer things for the beginner and the corporation.www.1and1.com icon

Ad from page's editor: Yes.. I do enjoy compiling these things for you. I hope they are helpful. However... this doesn't pay my bills!!! Sheepdog Software (tm) is supposed to help do that, so if you found this stuff useful, (and you run a Windows or MS-DOS 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 editor's freeware, shareware page.

Link to Tutorials main page
How to contact the editor of this page, Tom Boyd


Valid HTML 4.01 Transitional Page 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 .....