
Why Should Anyone Use C/C++ DLLs With Delphi
by Eric W. Engler


This is a simple introductory paper to help you understand
the role of DLLs, and how Delphi programmers can easily
take advantage of them.  


Why use C/C++ DLL's in Delphi?

Although C/C++ is NOT inherantly better than any other 
language, we must acknowledge that there is a large volume
of high quality C/C++ code being used today.  Much of this
code is available as freeware for Unix.  It makes sense to 
re-use much of this code without first translating it into 
Delphi's native language. By not translating the code, we
eliminate the time required to do the translation, and
more importantly, we don't need to debug the code again!

One of the easiest ways to combine source code of different
languages into a single project is to isolate the code of
each language by having your "secondary language" generate 
it's object code into library files that can be linked 
together with the "primary" language's object files. I'm 
using the term "primary" language to refer to the language
used to build the GUI, Delphi in this case.  The "secondary"
language in this example is C/C++.  

This isn't intended to be a slam against C/C++ to say that
it is "secondary".  I'm just using this term to indicate 
that the "controlling" language for the application is going
to be Delphi, since it can quickly generate high quality 
GUI's that are easily maintained.  The "meat and potatoes" 
language in this example is C/C++, since we have lots of 
highly functional C/C++ code that's already been debugged.

The two methods of linking code together into libraries are:
static linking (.LIB's) and dynamic linking (.DLL's).  Due 
to many factors, the dynamic link format is the best 
linking method when mixing languages.  The biggest reason
for this is guaranteed compatibility due to the highly 
standardized DLL interface specifications.  Lesser factors
include the ability to have several programs re-use one 
copy of the code, and the modularity which helps to debug
applications.

For example, my first DLL project is to encapsulate the
excellent multi-platform (originally Unix) code done by
the "Info-Zip" group of developers.  Their freeware (with
some minor restrictions) versions of ZIP and UNZIP have 
proven to be very compatible, reliable and portable.  By
wrapping this code up into Win32 DLLs, Delphi programmers
can take advantage of their many man-years of effort.


The list of excellant freeware C source code that could be 
used in a Win32 DLL is virtually endless.  It would not be
wise to translate this code into Delphi because we'd lose
the ability to easily incorporate new updates into our
project.  By keeping the code in C/C++, we can easily 
incorporate new software changes, and we can stay on the
cutting edge with a minimum of effort!  


DLL Charateristics

Dynamic Link Libraries are used to allow Windows programs to
share executable code and/or data.  By sharing code and/or
data, the overall memory requirements can be greatly diminished.
In addition to memory savings, there are other benefits of using
DLLs:
  
  - Programs can be updated more easily, since the functionality
    is broken down in a modular manner.
  - DLL's that are provided by other vendors can be updated 
    on a user's computer, and your non-updated program 
    can immediately take advantage of the updated DLL.
  - DLL's can be used to facilitate memory sharing between
    different calling programs.  

DLL's typically contain:

  - executable code (procedures/functions)
  - resources (fonts, bitmaps, icons, etc)
  - static data values (state/city names, zip codes, etc)
  - user-oriented prompts and messages (especially useful
     for international programs: you can have a DLL for 
     each language you support)


VBX controls are very similar to Win16 DLL's, except that they
have a standardized interface to their properties, events, 
and methods.  Think of a VBX as being a combination of a 16 bit 
DLL and a VCL control wrapper for Delphi v1.  As you likely know
by now, VBX controls have been superceded by OCX (aka ActiveX) 
controls.


ActiveX controls are basically an enhanced version of a 32 bit
DLL that has standardized OLE interfaces (Microsoft defined a 
16 bit version of OCX controls, but it never caught on).  One
area of concern, however, is that the newest "light weight"
version of ActiveX controls contain pseudo-code to be 
interpreted at runtime!  


Be aware that installation and use of OCX controls is far more
sophisticated than it is for simple DLLs.  Delphi can shield
you from the complexities of OLE, but you'll still have to 
live with the installation hassles, and the performance 
limitations of OLE, compared to the native DLL interface.  
Despite all the publicity coming out of Microsoft about why 
everyone should use ActiveX, they continue to use DLLs to glue
together the most critical APIs used "under the hood" in
all modern versions of Windows.


For these reasons, I strongly recommend the use of DLLs over
OCXs, unless you're working with a Web application that 
requires the use of OCX controls.


What Kind of Interface Does a DLL Have?

Programs written for the Windows platform will normally have
some way to let you call functions in DLLs, and use data from
DLLs.  DLLs can also call other DLLs (most commonly to execute
Windows API functions, or C/C++/MFC run time library 
functions).  

To begin with, a program has to ensure that the DLL is loaded.
This is very simple if your program is using implicit linking,
which is the most common type.  In this case, Windows will load
it for you, and issue an appropriate error message if the DLL
can't be found.

Once the DLL is loaded, you need a way to execute functions
in the DLL.  If you're using implicit linking, this is also 
simple: your DLL import unit will simply declare the DLL
functions in a way that lets you call them directly with no
hassles.  With explicit linking, you will first need to call
a Win API function to get the address of each DLL function 
you want to call.

There are two popular ways to access data inside the DLL.  I
want to stress here that you can NOT directly read data from
a DLL and manipulate it! 

In the simplest case, the DLL author provides you with
functions that will fetch the data you want, sometimes 
allowing you to  specify "search parameters" to fetch the 
specific data you want.  For example, you might have a DLL
function that will return all the zip codes for the Chicago 
area.  The same DLL may have a function to test a given zip
code to see if it is in the state of Illinois.

The other way to access data in a DLL is to classify
the data as a Windows resource (bitmap, font, etc).  Then,
the Windows API functions for handling resources can be used
to access the data. 
 

DLL Error Handling

One area of DLLs that is often overlooked in the program
design phase is error-handling.  There are basically two 
schools of thought here.  The first model is to allow the
DLL to report errors directly to the user via Windows dialog
boxes, etc.  In this case, there is only one error code, and 
that is fatal - it tells the calling program to quit.

The other error-handling model is based on a principle of
"let the calling program handle the errors".  The DLL returns
a detailed error code to the calling program, and the calling
program decides what action to take, based on what specific 
error was encountered.

It isn't easy to select one of these error models because
both have their advantages and disadvantages.  I've found 
that the model of having the DLL report errors directly to 
the user seems to be easier to design and test, while the 
method of having the DLL return a detailed error code is 
more flexible.

If the DLL is going to be distributed to a large number of
developers, who will then design their own calling programs,
you are pretty much forced to include at least the option
of having the DLL report errors to the user.  With today's
rapid development tools, like Delphi, the DLL is most 
convenient to the developer if you don't force him to
design a complex error handling strategy.  Most developers
simply want to "plug and play".

In cases where more strict control of error handling is 
required, it often makes sense to design a DLL that will 
have a robust set of data validation and error detection 
routines.  In this case, ALL errors should be returned to
the calling program. and additional functions should be
included to allow processing to resume after an error has
been reported (in case the user wants to ignore).  This
type of interface to DLLs is clearly the model of choice
for all DLLs used in background processing (where there's
no user to click on a dialog button), "mission essential"
processing, etc.


DLL Differences Between Win16 and Win32

To help you understand DLLs and their usage, I want to clear
up some misconceptions first.  Many programmers learned 
about DLLs with Windows v3.x (16 bit API), and they haven't
studied the changes that came with the 32 bit API.  I'll
explain the most important 32 bit changes for the sake of
those old timers (like myself).

If this is a little too technical for you, just consider
the bottom line: Win32 DLLs are VERY easy to use in your
Delphi programs, and you don't have to worry about the
technical constraints that were important under Win 3.1.


DLLs in Win16 had these characteristics:

 - all applications share the same DLL data segment;
   inadvertant conflicts between programs that share this
   data area would sometimes bring disasterous results
   (reentrancy was hard to achieve)
 - the DLL uses each application's own stack space; putting
   an extra burden on the application program
 - DLLs were coded in the "large" C model, which was a
   big incentive to use them (most Win3.0/3.1 programs 
   were developed in the medium C model - by using a DLL
   they could get the advantages of the large model)
 - DLLs needed a WEP (Windows Exit Point) function, which 
   is normally trivial, but a frequent source of errors
   for developers
   
DLLs in Win32 have been enhanced in these ways:

 - each application (program or thread) that uses the DLL 
   will get a separate copy of the DLL's data segment 
   (initialized separately for each program/thread)
 - each application will have a separate DLL stack segment 
   allocated for it; the DLL won't put a burden on 
   application's own stack space
 - sharing of DLL data by different programs has become
   better controlled; and no sharing of dynamic data is 
   allowed by default


Imports vs. Exports

Functions that you call from another module (a DLL, VBX, OCX,
or even a static library) are called "imported" functions to
your program.  From the point-of-view of their own module, they
are called "exported" functions.  Likewise, any external 
functions that exist outside of that module, but are called 
from within that module are called "imported" functions to 
that module.

If your main program supplies a DLL with the address of a
function within your program, then that function is an exported
function from your program, and an imported function to the DLL.
This type of function is usually called a "callback" function,
because the DLL will call that function when it detects the
necessary conditions have been met.  For example, many DLL's
have the ability to call one of your functions every time an
error occurs while executing one of the functions in the DLL.

Every Windows API function you call is considered an import 
to your program, and an export of the native Windows DLL that
supplies that function.  Therefore, when you use the Win95 
Quick Viewer to view the executable version of your program,
it will show you all the imported functions your program 
needs in order to work, and it will show which DLL, etc. is
supposed to provide that function.  If you have any "callback"
function that you want another module to call, then you may
see those functions as exports.


Delphi Import Units

Using a C DLL in Delphi is most often done by using a Delphi
import Unit.  This is Object Pascal code that contains 
declarations of the DLL's exported routines, as well as all 
types and constants used by, or returned by, these routines.

For example, when your Delphi program calls a Win API function,
it simply needs to include the appropriate Delphi Import Unit 
in it's "uses" clause.  This makes the Windows DLL's exported
functions become available to you just as if those functions 
were coded directly in your program.

It's no different whether you want to call a function in a
standard Windows DLL, or another DLL, perhaps designed in
a different language (even Delphi!).


NOTES:

These notes may help you in diagnosing various DLL-related
problems:

   1) You should put the DLL in the \WINDOWS\SYSTEM directory,
or whatever equivalent directory is being used.  If this isn't
possible, then put the DLL in your application directory.

   2) You should always check version numbers before replacing
an exisitng DLL.  You can easily check version numbers by
checking the properties of the DLL.  In Explorer, locate your
DLL.  Right click on it, and select Properties.  Then, select
the Properties tab to see this info.
   As a less attractive alternative to determine the DLL's
version, use any file viewer program to view the last part
of the DLL file - the version info is there and is easy to 
see because there is a space between each letter, and there
is text that you can easily read.

   3) With some DLL's, it can be helpful to check it's import
and export tables.  Most modern DLL's will only show you their
import tables using this particular method: In Explorer, locate
your DLL and Right click on it, and select "Quick View".  Then, 
scroll down thru this list of DLL info.  NOTE: Quick View is an
optional program in the Win95 install process - some computers 
may not have it installed.

NEW: A program presented in the February 1997 issue of 
Microsoft Systems Journal (called DEPENDS.EXE) will show you 
all file dependancies for a given .EXE or .DLL.  However, 
this will only work if implicit linking was used!
