![]() ![]() ![]() ![]() ![]() ![]() ![]() |
![]()
Technical Information Database TI1642D.txt Importing or "Wrapping" DLL Function Calls Category :Application Interop Platform :All Product :Delphi All Description: 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 Reference: 7/16/98 4:34:18 PM |