DLLs in general

hi
well, im starting to get sick of making huge one-file .exe programs that require a full readthrough/recompile of the entire program code everytime a small change is needed. Therfore the need for DLLs, but im new in that area, so some questions.......

(also, a link to a help webpage/file to download  that can answer these questions and more would be nice)

what i like to ask is:
1) what exactly are DLLs? a code storage file for function calls (with/without variables for itself? ) ? a program by itself that is called by other programs? or a hybrid of both?
2) what exactly can be run on a DLL? graphics? GUIs? Database access? etc?
3) how do you startup/init variables/run a DLL?
4) anything i should know about the interface between main-program and DLL (can use this, dont use that type answers)?

thanks
LVL 1
wkzAsked:
Who is Participating?
I wear a lot of hats...

"The solutions and answers provided on Experts Exchange have been extremely helpful to me over the last few years. I wear a lot of hats - Developer, Database Administrator, Help Desk, etc., so I know a lot of things but not a lot about one thing. Experts Exchange gives me answers from people who do know a lot about one thing, in a easy to use platform." -Todd S.

goldragonCommented:
I guess you better have a look at this site : http://delphi.about.com/library/weekly/aa080803a.htm
it has everything you need to know about dll I guess.

Dll is ussually used to store functions and also very useful to modulrised your program.

I hope that website helps.. also I better suggest you to call dll using stdcall, don't follow that website example which is using export cos stdcall is more general and can be read and run anywhere as far as I know.
philisophicalphilCommented:
Hi,

My first point: DLLs are usually used for making it possible for other programs (not written by you) to intoerface with your programs, by providing a set of functions to perform different tasks. Therefore, it is recommended that unless you wish to do this, you should use Delphi Packages.

DLLs are basically a special type of unit - they are a compiled set of C style functions. These functions can be passed variables known to c++ (ie numbers, boolean, PChar strings (NOT Delphi strings), and pointers to delphi records.

You should only return numbers and booleans, along with anything that has been passed by reference.

DLLs contain a number of functions and procedures, which can be written in any language.
You can use functions and procedures inside the DLL, but you can only call functions from outside of the DLL.

Inside of a DLL's function is normal delphi code.

To create a DLL, go File/new/other, then choose DLL Wizard (Delphi 7)

Heres an example of a DLL i wrote a few weeks ago...


// DLL code

library StockDLL;

uses
  SysUtils,
  Classes;

{$R *.res}

function StockValue(StockDesc : Pchar; cost, onHand : double; var strValue : PChar; maxStrLen : integer) : double; stdcall;
var
    tempstr1 : string;
begin
    tempstr1 := (StockDesk + FloattoStr(onhand));
    strValue := pChar(tempstr1);
    result := ((onHand * Cost) / 9) * 8;
end;

exports
    StockValue;
end.

 
Note that the DLL is being passed a normal pchar string, 2 doubles, a PChar string by reference, then an integer. I am returning a PChar string by reference, and also a double using the normal delphi 'result'.

The exports keyword specifies which Functions in the DLL are visible outside of the DLL and are therefore able to be called.
The stdcall keyword means that the variables passed are read left to right instead of right to left.

// Program Unit Code


//top of class
......
 public
    { Public declarations }
  end;

    function StockValue(StockDesc : Pchar; cost, onHand : double; var strValue : PChar; maxStrLen : integer) : double; stdcall; external 'StockDLL.DLL';

var
  frmStock: TfrmStock;
  Modified : bool;
  currentpath : string;

implementation
.....



procedure TfrmStock.PopulateControls;
var
    aStock : TStock;
    ps1, ps2 : pChar;
    maxLength : integer;
begin
        ps1 := StrAlloc(Length(aStock.description) + 1); // Allocate memory for PChar String

        // Find max length of string
        maxLength := Length(aStock.description) + 16 + length(inttostr(aStock.onHand)) + length(floattostr(SimpleRoundTo(aStock.cost, 0)));

        ps2 := StrAlloc(maxLength);  // allocate memory for second pchar string
        strcopy(ps1, pchar(aStock.description)); // fill first pchar string
        // call function in dll
        lblPoH.Caption := floattostr(StockValue(ps1, aStock.Cost, aStock.onHand, ps2, maxLength));
        label2.Caption := ps2;

        // deallocate memory for strings
        strDispose(ps1);
        strDispose(ps2);
end;

Function Declarations are made in the interface section, outside of the form's class.
Note the use of the external keyword in the Function declaration - this specifies that the fuction can be found outside of the unit, in the file 'StockDLL.DLL'

MAKE SURE that you put the stdcall keyword both on the function's forward declaration inside the program, AND on the function itself inside the DLL. If you don't you get the program passing data one way, and the function reading it the other. This can lead to some VERY untraceable bugs in your program! (From experience :-( )

This should give you a good start with DLLs, any questions just ask...

hope this helps,

Philip
Wim ten BrinkSelf-employed developerCommented:
Okay, first of all, DLL's will make the total size of your binaries larger since each DLL needs it's own runtime library. If you don't use forms then the runtime library is about 40 to 100 KB per DLL. (It can be smaller if you only use the Windows API but you can also end up with DLLs of 300 to 400 KB.)

Second of all, DLL's can introduce more bugs in your application since you can get version conflicts between procedure calls. Say, in the DLL you declare:
procedure Whatever(Data: Integer); stdcall;
and in your application you declare it as:
procedure Whatever(Data: DWORD); safecall;

Then you've already created two conflicts. First of all, the Integer/DWORD conversion which might create funny results. (Negative values becoming positive, for example.) and the calling mechanism (stdcall/safecall) difference tend to lead to bugs that are often very silent, yet they keep crashing your application. So you need to take extra care about this all.

Another problem is the memory manager within Delphi. With DLL's you suddenly have two memory managers in your application. One in your main application and one in your DLL. This will lead to conflicts when you send over long strings or objects. Because the execuable might allocate the memory and the DLL might try to release it again. But the DLL has no entry for this string/object in it's memory management and this leads to several conflicts. To solve this memory allocation problem you can use two techniques. Either use ShareMem to redirect the memory manager within the DLL or declare one method in your DLL that returns an interface object. (A low-level COM object actually.) The last method requires a bit of explanation. First define a unit with the interface. Simple example:
---------------------------------------------------
unit untSayHello;
interface
type
  ISayHello = interface
    procedure SayHello(const Name: string);
  end;
implementation
end.
---------------------------------------------------
Basically, you define the interface for an object here. You can add procedures and functions to an interface. Even properties can be added but the properties do neet get/set methods. An interface cannot contain any data. Now, with above interface you first create the DLL, e.g. like this:
---------------------------------------------------
library Hello_DLL;
uses Windows, untSayHello in 'untSayHello.pas';
type
  TSayHello = class(TInterfacedObject, ISayHello)
    procedure SayHello(const Name: string);
  end;
procedure TSayHello.SayHello(const Name: string);
begin
  MessageBox(GetDesktopWindow, PChar('Hello, ' + Name + '.'), 'Greetings', MB_OK);
end;
function GetSayHello: ISayHello; stdcall;
begin
  Result := TSayHello.Create;
end;
exports GetSayHello;
begin
end.
---------------------------------------------------
Again, not real rocket-science... The object inherits from TInterfacedObject since this object can deal with interfaces quite nicely. It also supports the interface we created in the unit. We give some code to the single method this example has and then the object is finished. All we need now is a routine that is exported to the outside world which returns an object of our interface type. I can't say that GetSayHello is a very complex method, but do keep this method simple. If you need any further initialization of the object, just call an object method from the EXE afterwards. Just let the export function be as simple as possible.
Finally, we get to the executable itself. Since we don't use any COM, we don't have to initialize it either. I just use a simple DPR for this sample which only creates the object, calls the method, then frees it again:
---------------------------------------------------
program Hello_EXE;
uses untSayHello in 'untSayHello.pas';
var
  SayHello: ISayHello;
function GetSayHello: ISayHello; stdcall; external 'Hello_DLL.dll';
begin
  SayHello := GetSayHello;
  SayHello.SayHello('stranger');
  SayHello := nil;
end.
---------------------------------------------------
As you see, I do need the unit where I declared the interface but this unit is just shared between the DLL and the EXE. Therefore, any change to the interface will affect both DLL and EXE. (Leads to less errors.) Not many people use DLL's like this because it soon leads to COM/DCOM development or even COM+. But I prefer this technique because it leads to less errors in my code. Besides, it allows me to share objects between DLL's and executables. The use of interfaces does provide a very powerful tool when you start developing DLL's.

Now, back to your questions:
1) what exactly are DLLs? a code storage file for function calls (with/without variables for itself? ) ? a program by itself that is called by other programs? or a hybrid of both?
A DLL can be a hybrid sometimes. I once had an executable that could be called as if it was a DLL but which could also be executed as a stand-alone executable. Later, I considered this bad practice because it made things a bit too complex. From a technical point of view, a DLL is just a code library, containing compoled code. On it's own, it could be considered the early form of a component since it contains data and has methods to read/write this data.

2) what exactly can be run on a DLL? graphics? GUIs? Database access? etc?
Depending on the tool that you use to create a DLL, you could use a DLL to do anything that a normal executable can do. With Delphi, I do try to avoid to include forms inside DLL's since a DLL-form is a bit difficult to handle correctly. With DLL's you do have to be careful with memory management because the DLL has it's own memory manager. As I said before, use interfaces or ShareMem of you want to exchange more than simple datatypes between EXE and DLL. You could arrange database access from a DLL but keep in mind that a simple TTable is an object that can cause trouble if you send it to the main executable. The DLL can handle the TTable quite nicely, inserting, deleting, modifying and selecting data. But as soon as when you want to send it to the main executable, you have to deal with the memory management. (I solved this by using the ADO API directly, using the _Recordset datatype to send records between EXE and DLL. Again, the _Recordset is an interface and interfaces are smart enough to be deallocated by the right memory manager.

3) how do you startup/init variables/run a DLL?
You can statically link to the DLL as shown above or use the more complex LoadLibrary/GetProcAddress/FreeLibrary API calls. The latter is slightly more complex but allows you to create your own error-handling methods in case the DLL has been deletd or a method does not exist in the DLL.
With DLL's I always prefer to add at least one unit. In this unit you can add an Initialization/Finalization section to declare and free variables in your DLL. (Although the Finalization section might not be called in all situations. But since I prefer to use interfaces within DLL's I can initialize/uninitialize in the Create/Destroy methods of these objects. Keeps it simple.

4) anything i should know about the interface between main-program and DLL (can use this, dont use that type answers)?
Yes, keep it simple. Use simple datatypes like integers, chars, PChars or shortstrings. Don't use dynamic arrays, long strings, objects, components or anything else that relies on the memory manager because it might make things very complex. Try to avoid calling VCL objects in the DLL from the executable, if possible. If you do need a form in the DLL, try to keep it a modal form.
And most importantly: Test, test and again test your DLL's. Make sure you test any method inside your DLL and be aware that changes in the DLL might affect the executable, especially changes to procedure headers.
Expert Spotlight: Joe Anderson (DatabaseMX)

We’ve posted a new Expert Spotlight!  Joe Anderson (DatabaseMX) has been on Experts Exchange since 2006. Learn more about this database architect, guitar aficionado, and Microsoft MVP.

wkzAuthor Commented:
ok....reading through the posts now
will get back to you

thanks for all the input
wkzAuthor Commented:
addum questions:

5) can you give an example of a "program" DLL?
5.1) can DLLs store variables for their own use, as in can they store DLL specific information seperate from the main .exe?
5.1.1) if (5.1) variables exist, how long do they "survive"?
5.1.2) also, is there a way to initialize startup data on a DLL?
wkzAuthor Commented:
for 5.1.2, can you give an example where it goes? i think i know, but need to be sure
Wim ten BrinkSelf-employed developerCommented:
5) A program DLL is just an executable that, like a DLL, exports one or more functions. Thus, a combination of my two previous examples:
------------------------------------------------
program Hello_EXE;
uses untSayHello in 'untSayHello.pas';
type
 TSayHello = class(TInterfacedObject, ISayHello)
   procedure SayHello(const Name: string);
 end;
procedure TSayHello.SayHello(const Name: string);
begin
 MessageBox(GetDesktopWindow, PChar('Hello, ' + Name + '.'), 'Greetings', MB_OK);
end;
function GetSayHello: ISayHello; stdcall;
begin
 Result := TSayHello.Create;
end;
exports GetSayHello;
var
 SayHello: ISayHello;
begin
 SayHello := GetSayHello;
 SayHello.SayHello('stranger');
 SayHello := nil;
end.
------------------------------------------------
However. loading an EXE as a DLL does have a few problems. It was a long time ago when I did something like that and it was not very easy. But it can be done.

5.1) All variables in a DLL are local for the DLL. The EXE doesn't know about their existence and the EXE can't even read them. If you use dynamic variables, the memory manager of the EXE will not be aware of these even, unless you use ShareMem to force the DLL to use the same memory manager as the application. (In which case the EXE does know about memory allocations by the DLL but still doesn't know what kind of variables these are.)

In the past, global variables inside DLLs would be shared amonst multiple applications that called the DLL. Thus if you had two EXE's running using one DLL, both increasing a counter in the DLL that was initialized to 0, the first would increase it to 1 and the second to 2. DLLs used to be useful for sharing data.
But since Win32 arrived, every process has it's own memory space and data is stored in that dataspace. So in Win32 both EXEs would just increase the counter from 0 to 1. The DLL will not share any data anymore.

5.1.1) Variables exist for as long as the DLL is in memory.

5.1.2) To Initialize data in a DLL, I just add a method to it called 'Initialize_Data' and call it from the executable. Or, since I prefer to use objects, I let the create-method of the object initialize data. I do try to avoid global data inside DLLs but that's because I survived the Windows 3.1 era. ;-)

Actually, DLLs can be quite complex at times, especially if you go deep in the Windows API and try to respond to any EXE that connects/disconnects to the DLL and do stuff. But in most cases it is advised to keep things simple. Delphi is already handling a lot of stuff for you when you create a DLL. All you should need to do is declare procedures and functions that you export to the outside. I kept things even simpler by exporting functions that return interfaces around objects. You do need to understand what an interface exactly is to understand the technique but it is very, very useful.

Experts Exchange Solution brought to you by

Your issues matter to us.

Facing a tech roadblock? Get the help and guidance you need from experienced professionals who care. Ask your question anytime, anywhere, with no hassle.

Start your 7-day free trial
wkzAuthor Commented:
thanks for all the help guys. esp workshop_alex :)

er...i accepted the answer(s) but a few last questions:
6)"Variables exist for as long as the DLL is in memory.". how long will the DLL be in memory then?
7)"The DLL will not share any data anymore.". if share memory is needed, how do you go about it?
Wim ten BrinkSelf-employed developerCommented:
6) statically loaded DLL's are in memory for as long as the application itself is in memory. Dynamically loaded DLL's are in memory for as long as the developer wants them to be. LoadLibrary puts it in memory, FreeLibrary kicks it out again.
7) There are quite a few other options to share data between DLL's but you basically use the same technique as with normal applications that need to exchange data. You can write to a temporary file, use memory mapped files to allocate space in the swapfile for shared data, use the registry or use a communication protocol between two apps/DLL's like TCP/IP or named pipes. You can even send messages between applications or just store everything in a database.
Remember, the DLL does not share data between two different processes, but in a single process, no problem. Actually, it is better this way because it means applications are now more independant. The crash of one application won't bring down the others that quickly anymore.
wkzAuthor Commented:
k..

thanks once again for all the help you gave, Workshop_Alex
It's more than this solution.Get answers and train to solve all your tech problems - anytime, anywhere.Try it for free Edge Out The Competitionfor your dream job with proven skills and certifications.Get started today Stand Outas the employee with proven skills.Start learning today for free Move Your Career Forwardwith certification training in the latest technologies.Start your trial today
Delphi

From novice to tech pro — start learning today.