DLLs: Import dynamic or static
Both techniques have their advantages. Static importing means you define functions like this:
function f : integer;
external 'mydll.dll';
The advantage of static imports are:
* It is easy - it does not require ugly code.
* The application will only start up if all DLLs are present and can be loaded (this could also be considered a negative)
* All functions across all modules are bound during startup time.
Dynamic importing has its advantages as well.
- It basically gives you full control over the usage of the DLL. If your DLL is only needed rarely, you may not want to load it at startup of your application. Your application will start faster then.
- If the application will work without that DLL to a usable extent, you may allow the user to do so.
- If you have different DLLs for different environments, for example one for Windows NT, another one for Windows 95 etc, then you MUST bind dynamic - you determine the operating system and load the DLL that you want.
- Dynamic binding allows you to release the DLL if you do not need it anymore
The following code shows you how to do it:
Dynamic loading and binding of DLLs
Sometimes you may need to load a DLL at runtime, for example
- if you have a couple of different DLLs to choose between
- to have a concept for optional functionality.
This small source code shows how to load the DLL with LoadLibrary and use the returned handle to access (bind) the functions that are contained:
type
TfncCnx = function (s : string) : THandle;{ function declaration }
var
cnx : TfncCnx;
begin
{ load the DLL and get the function's address }
h := LoadLibrary('myDll');
if h = 0 then
begin
ShowMessage ('DLL not available');
end
else
begin
@cnx := GetProcAddress(h, 'myProc');
if @cnx = nil then
begin
{ function not found.. misspelled? }
ShowMessage ('blub');
end
else
begin
{ call the function as usually }
x := cnx('alpha');
end;
{ unload the DLL }
FreeLibrary(h);
end;
end;
Retrieve list of exported functions from a DLL
To retrieve the list of exported functions from a DLL, pass the DLL name and a TStrings object to the function ListDLLFunctions() shown below.
This does not show the parameters for each export, which you can only get from the author of the DLL.
program Project1;
uses
Forms,
Classes,
SysUtils,
Dialogs,
ImageHlp, // routines to access debug information
Windows;
// by Dmitry Streblechenko
procedure ListDLLFunctions(DLLName: String; List: TStrings);
type
chararr = array [0..$FFFFFF] of Char;
var
H: THandle;
I,
fc: integer;
st: string;
arr: Pointer;
ImageDebugInformation: PImageDebugInformation;
begin
List.Clear;
DLLName := ExpandFileName(DLLName);
if FileExists(DLLName) then
begin
H := CreateFile(PChar(DLLName), GENERIC_READ, FILE_SHARE_READ or
FILE_SHARE_WRITE, nil, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
if H<>INVALID_HANDLE_VALUE then
try
ImageDebugInformation := MapDebugInformation(H, PChar(DLLName), nil, 0);
if ImageDebugInformation<>nil then
try
arr := ImageDebugInformation^.ExportedNames;
fc := 0;
for I := 0 to ImageDebugInformation^.ExportedNamesSize - 1 do
if chararr(arr^)[I]=#0 then
begin
st := PChar(@chararr(arr^)[fc]);
if Length(st)>0 then
List.Add(st);
if (I>0) and (chararr(arr^)[I-1]=#0) then
Break;
fc := I + 1
end
finally
UnmapDebugInformation(ImageDebugInformation)
end
finally
CloseHandle(H)
end
end
end;
// the following is an example how to use the procedure
var
List: TStrings;
I: integer;
S: String;
begin
List := TStringList.Create;
ListDLLFunctions('c:\winnt\system32\mfc42.dll', List);
S := 'List of functions';
for I := 0 to List.Count - 1 do
S := S + #13#10 + List[I];
ShowMessage(S);
List.Free
end.
Dynamically load DLLs?
There are two possibilities to load a dll:
1. Static loading of a DLL means that the DLL is loaded when the application is executed.
This is the easier option to dynamic loading, as you'll see, however it means that if the DLL
is missing, your program will not run.
{Examples:}
{a. Import a function called MYImportFunction from MYLIBRARY.dll}
procedure MYImportFunction; external 'MYLIBRARY.dll';
{b. Import a routine under a different name from the one it has in the library. }
procedure MYImportFunction; external 'MYLIBRARY.dll' name 'MyNewImportFunctionName';
{c. By ordinal value: Has to be the same value as when using the exports
keyword when making the DLL}
procedure MYImportFunction; external 'MYLIBRARY.dll' index 10;
2. Dynamic loading
By dynamically loading a DLL you decide at runtime which DLL to use. This means you can give your program different functionality depending on which DLL's are present (freeware or shareware versions of a program).
Or also if you want to load a library whose name or path must be computed at run time or generated from user input.
Dynamic loading of a DLL loads the DLL in your application when it is needed and unload it once you its work is done. It requires more code to use, however it more resource friendly than static loading.}
{**********************************************************}
// Example for dynamically loading a DLL
// Beispiel um eine Dll dynamisch zu laden:
type
TDLLFunction = function(someParam: TSomeType): TSomeOtherType;
{
To execute such a function by name from a named DLL one could use a
"caller" function like
}
function CallDLLFunction(const dllname, functionname: string;
theParameter: TSomeType): TSomeOtherType;
var
hDLL: THandle;
theFunction: TDLLFunction;
buf: array [0..144] of Char;
begin
// Get a handle to the DLL module.
// das Handle zum DLL Modul ermitteln.
hDLL := LoadLibrary(StrPCopy(buf, dllname));
// If the handle is valid, try to get the function address.
// Wenn das Handle gültig ist, versuche die Adresse der Funktion zu ermitteln
if hDLL <> 0 then
begin
// Return the address of the specified exported (DLL) function.
// Adresse der Dll-Funktion ermitteln
try
@theFunction := GetProcAddress(hDll, StrPCopy(buf, functionname));
// If the function address is valid, call the function.
// Wenn die Funktion gefunden wurde...
if @theFunction <> nil then
Result := theFunction(theParameter)
else
raise EDLLException.CreateFmt('Unable to link to function %s in DLL %s!',
[functionname, dllname]);
finally
// Free the DLL module.
// Dll wieder freigeben.
FreeLibrary(hDLL);
end;
end
else
raise EDLLException.CreateFmt('Unable to load DLL %s!'#13#10 +
'Reason: %s.', [dllname, DLLErrors[hDLL]]);
end; |