TI3318 - Importing or "Wrapping" DLL Function Calls
- Product: Delphi
- Version: All
- Platform: Windows/Win32
Importing or "wrapping" dll function calls
Two methods exist for importing or loading functions
from a Dynamic Link Library(DLL). The first method
(which is discussed extensively in this document) is
called "implicit" loading. Implicit loading involves
loading the DLL statically at startup and accessing
the functions through an Object PASCAL interface.
This method should be used when an application is totally
dependent upon the loading of a DLL for proper functioning.
The other method available is referred to as "explicit"
loading because the DLL is loaded dynamically on demand.
This method requires a little more coding and should be
used if the application needs to run even if the DLL fails
to load properly.
What is a "wrapped" function call?
A wrapped function or set of functions consists
of an entry into the interface section and an
entry into the implementation section (along
with associated constants or types) which corresponds
to a function or set of functions to be imported from
a DLL. A wrapper is simply a declaration in a PASCAL
unit that provides an entry Point into a DLL. With Delphi,
this wrapper is represented by a unit file containing
object pascal code. The Delphi development team has
already conveniently wrapped many standard Windows
controls and functions for you. On occasion it may become
necessary to create wrappers for dll function calls
that are not already wrapped in Delphi.
The first and often the most difficult step in this
process is locating information about the function(s).
One of the best sources for locating documentation of
the function(s) in question is the World Wide Web.
An extensive search beginning with the MSDN and
extending to the numerous available search engines will
often reveal some pertinent information.Search C++ header
files in a product like Borland C++ or MS Visual C++ to
get the structure of the function calls. Type conversion
and calling conventions are generally able to be resolved
when converting between C++ and PASCAL. A good resource
for compatibility between Delphi and C++ can be found
on the Borland web site at:
"http://www.borland.com/delphi/papers/brick.html".
After locating an example or documentation of the DLL in
question the next step is to create a new unit file. The
interface of the unit will contain the types and constants
that are specific to the function calls of the DLL along
with the function headers. These function headers are the
Object PASCAL interface that is being provided for other
Delphi applications to call the DLL function. Once
the interface section of the unit is complete the next
section is the implementation. The implementation section
of the unit contains declarations of the imported external
functions. These headers are not identical to those found in
the interface section of the unit (these contain the actual
function identifiers plus other important implementation
information). For a more thorough treatment of this subject
see the help topic "DLLs:accessing procedures and functions"
in the Delphi 3 help file.
For example, say there exists a function called BOB in a DLL
called 'BLODGE.DLL'. (detailed below are the steps required to
implicitly load the DLL)
1) WWW Research on the function BOB reveals that it returns a
boolean and takes a word and a boolean as it's arguments.
2) Create a new unit file named 'UseBob.pas' via Delphi
(File|New and choose unit)
3) The following line of code would go in the interface section
of the new unit:
function BOB(Fire: Word; Dances: Boolean): Boolean; stdcall;
4) The following line of code would go in the implementation
section of the new unit:
function BOB; external 'BLODGE';
5) Save the unit, name it 'UseBob.pas'.
6) It is also necessary to make sure that UseBob.pas
is in the same directory of the current project or that it
resides in a directory that is in the Delphi search path.
7) Add the 'UseBob' unit to a new project's uses clause. The
function BOB can be called from this new project just like any
other standard function.
8) At runtime BLODGE.DLL must be in the path of the current
process's environment.
The steps required to explicitly load the 'BLODGE.DLL' are
slightly different and involve a bit of coding. As before a
knowledge of the function/procedure arguments is necessary
(and a result type if it's a function).
What follows is a unit that implements the BOB function call
on a button click event:
unit UDLLTest;
interface
uses
Windows, Messages, SysUtils, Classes, Graphics, Controls,
Forms, Dialogs, StdCtrls;
type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
{ Here's a type which points to our bob function }
TBOB = function(Fire: Word; Dances: Boolean): Boolean; stdcall;
var
Form1: TForm1;
implementation
{$R *.DFM}
procedure TForm1.Button1Click(Sender: TObject);
var
BOB: TBOB;
hDLLInst: THandle;
IsAlive,IsDancing: Boolean;
Years: Word;
begin
{ Load a handle to our BLODGE.DLL }
hDLLInst:= LoadLibrary('BLODGE.DLL');
{ If the load was unsuccessful raise a custom exception }
if (hDLLInst <= 0) then
raise exception.create('[LoadLibrary Fail]');
{ Try to load the address of the BOB function }
try
@BOB:= GetProcAddress(hDLLInst,'BOB');
if not assigned(BOB) then
raise exception.Create('[GetProcAddress Fail]');
Years:= 25;
IsDancing:= True;
{ Now we can execute the BOB function }
IsAlive:= BOB(Years,IsDancing);
finally
{ Free the handle to the DLL }
FreeLibrary(hDLLInst);
end;
end;
end.
JG