In-depth DLL programming in Delphi
Author: CEN Xin
Introduction
I believe that some friends with computer knowledge should have heard of "dll ". Especially those who have used the Windows operating system should have had the "miserable" experience of reinstalling the system many times-no matter how careful, no driver damage, no virus intrusion, after using (installing) The software for a period of time, I found that the windows system is getting bigger and bigger, and the operation is getting slower and slower. Sometimes the software that I can use is unavailable, as a result, the system has to be reinstalled. This situation is often caused by the installation and conflict of DLL files. This shows the shortcomings of the DLL, and the important position of the DLL, so that we cannot put an end to its use.
DLL (Dynamic Link Library) is a compiled code module that can be called and executed. DLL is an early product of windows. At that time, the main purpose was to reduce the memory usage of applications. It is called from the hard disk to enter the memory only when a function or process needs to be used. Once no program calls the DLL again, it is cleared from the memory. Simply put, the entire Windows system includes hundreds of DLL files. Some DLL files are specialized in functions (such as network and database drivers) and may not be installed. If all these functions are included in an application program, windows will be an EXE file of several hundred MB. This simple example can easily explain the role of DLL, and the performance loss caused by calling DLL becomes negligible.
Multiple applications call the same DLL and there is only one code copy in the memory. Instead of loading all the files in a static compiled program. When a DLL is loaded, it is mapped to the address space of the process, and the dynamic link of the DLL is used not to copy the library code, but to record the function entry points and interfaces.
At the same time, DLL also brings the benefits of sharing. Different software developed by a company may need some common functions/processes which may directly use some internally developed DLL; some common functions can directly use Windows Standard DLL. We often say that Windows API is a function/process included in several windows public DLL files; theoretically (if the author's copyright is not involved), know the Declaration and function of a DLL (input parameters and return values of function definitions ), we can use it directly without knowing its implementation (algorithm or compilation method.
If the algorithm of a function/process in a DLL is updated, the bug is corrected, and the entire DLL file is upgraded. In general, to ensure downward compatibility, the call declaration and returned results should remain unchanged. But in fact, with the improvement of functions, it is difficult to ensure that the execution of a single call remains unchanged. This is even worse when using DLL developed by others. For example, if I used a DLL package of an earlier version of a famous graphics software vendor in a drawing program, all my calls were executed based on the earlier version of the statement released by him. Assume that you have installed a new software from the software vendor, and some of the DLL files have been updated and upgraded. If these DLL files have been changed, the direct consequence will be that my software is no longer stable or even unable to run! Don't underestimate this situation. In fact, it is quite common. For example, Windows keeps changing the DLL it contains when fixing bugs and upgrading. The new version of DLL does not simply add new functions/procedures, but changes or even cancels the original declaration. At this time, we can no longer ensure that all programs run normally.
In addition to improving computer resource utilization, increasing development efficiency, and hiding implementation details, dll can also contain data and various resources. For example, if you develop a multi-language version of the software, you can use the DLL to separate language-dependent functions and resources, and then allow users in different regions to install different corresponding DLL, to obtain support for local character sets. For example, a software resource, such as graphics and icons, can also be directly stored in the DLL file for unified installation and management.
Create a DLL
Before proceeding with the subsequent explanations, I think we should first clear the concept that the routine declares a pointer variable, calling a function/process, actually, it is the Execution Code of the function/process transferred through the pointer.
We first try to use Delphi to create a DLL file of our own. This dll contains a standard directory deletion (including subdirectories and files) function.
Create DLL
It is easy to create a DLL through Delphi. Select DLL wizard for a new project, and a very simple unit is generated. This unit does not start with program as a general engineering file, but starts with library.
The engineering unit references sysutils and classes by default. You can directly follow the uses of this unit, begin... Add the function/Process Code before the end part, or add the unit containing the Code in the project. Then the unit will be automatically uses.
The next step is to compile the DLL routine code. If it is a routine in the reference unit, you need to add the export suffix when declaring it. If it is written directly in the Library Unit, you do not have to write the export again.
The last step is to add the exports section under the uses section and function definition above the begin statement of the Library Unit, and list the names of the routines to be extracted. Note that it is only a name that does not contain the procedure or function keyword, and does not require parameters, return values, or suffixes.
The syntax after the exports statement has three forms (routines refer to specific functions/Procedures ):
Exports routine name;
Exports routine name index value;
Exports routine name new name;
The index value and new name can be used by other programs to determine the function address. You can also leave this parameter Unspecified. If the index keyword is not used, Delphi will automatically assign the index number starting from 1 in the order of exports. Exports can be separated with multiple routines by commas.
Compile and build the final DLL file.
Format
To ensure that the generated DLL is correctly compatible with C ++ and other languages, pay attention to the following points:
Try to use simple types or pointers as the type of parameters and return values. The simple type here refers to the simple type of C ++, so it is best to convert the string type to the pchar character pointer. It is no problem to directly use the string DLL routine to call the program developed by Delphi (it is pointed out that sharemem should be added as the first unit to ensure correctness ), however, if you use a program developed in C ++ or other languages to call the API, you cannot ensure that the parameter is passed correctly;
Although the process is allowed, you 'd better get used to writing all functions. If the execution is correct, true/false is returned;
For parameter indicators such as const (read-only) and out (write only), it is best to use the default method (VAR by default) to ensure the call compatibility );
Use stdcall to declare the suffix to ensure correct exception handling. 16-bit DLL cannot handle exceptions in this way, so try... Handle t to process the exception;
Generally, the far suffix is not used unless to maintain 16-bit compatibility.
Sample Code
DLL engineering unit:
Library fileoperate;
Uses
Sysutils,
Classes,
Udirectory in 'udirectory. pa ';
{$ R *. Res}
Exports
Deletedir;
Begin
End.
Function Implementation Unit:
Unit udirectory;
Interface
Uses
Classes, sysutils;
Function deletedir (dirname: pchar): Boolean; export; stdcall;
Implementation
Function deletedir (dirname: pchar): Boolean;
VaR
Findfile: tsearchrec;
S: string;
Begin
S: = dirname;
If copy (S, length (s), 1) <> '/'then S: = S + '/';
If directoryexists (s) then begin
If findfirst (S + '*. *', faanyfile, findfile) = 0 then begin
Repeat
If findfile. ATTR <> fadirectory then begin
// Delete the object
Deletefile (S + findfile. Name );
End
Else begin
// The Directory is nested.
If (findfile. Name <> '.') and (findfile. Name <> '..') then
Deletedir (pchar (S + findfile. Name ));
End;
Until findnext (findfile) <> 0;
Findclose (findfile );
End;
End;
Result: = removedir (s );
End;
End.
Initialize and release resources
There are several methods for initialization in Delphi. One is to use the initalization and finalization sections of unit (do not know the "unit section "? You should first describe the Delphi syntax) to initialize the variables in the unit. Note that although the DLL has only one copy in the memory, the routine is affiliated to different process spaces of the caller. If you want to initialize public variables to achieve multi-process sharing, it is not feasible. You must also pay attention to conflicts caused by public variables.
Second, in the library unit's begin... End to initialize the DLL. If you want to have the corresponding code at the end of the DLL, you can use the exitproc process variable automatically created by the DLL, which is a pointer to the exit process. Create a process and assign the address of the process to exitproc. Because exitproc exists when the DLL is created... This step should be performed in the end part. At the same time, a temporary pointer variable is created to save the original exitproc value, and the exitproc value is assigned back during the exit process. In this way, the default DLL exit operation can be completed after the exit operation is performed. (The meaning of inherated is the same as that of the destory method in the overloaded destory method, the default parent class destory is also required ).
Example:
Library mydll;
...
VaR
Oldexitproc: pointer; // public variable to save the original exitproc pointer so that it can be assigned back
Procedure myexitproc;
Begin
... // End code of initialization
Exitproc: = oldexitproc; // remember to assign exitproc back when you exit.
End;
...
Begin
... // Initialization code
Oldexitproc: = exitproc;
Exitproc: = @ myexitproc;
End.
The third method is similar to exitproc. A pointer variable dllproc is pre-defined in the System Unit (this method must reference the Windows unit ). When using dllproc, you must first write a program with the following prototype:
Procedure dllhandler (dwreason: DWORD); stdcall;
And the execution address of the dllhandler program is assigned to dllproc between begin... end. In this case, corresponding processing can be made based on the reason value. Example:
Library mydll;
...
Procedure mydllhandler (dwreason: DWORD );
Begin
Case dwreason
Dll_process_attach: // when the process enters
Dll_process_detach: // when the process exits
Dll_thread_attach: // when the thread enters
Dll_thread_detach: // when the thread exits
End;
End;
...
Begin
... // Initialization code
Dllproc: = @ mydllhandler;
Mydllhandle (dll_process_attach );
End.
It can be seen that dllproc is more powerful and flexible than exitproc in processing multiple processes.
Static (implicit) Call of DLL
The DLL already exists. Next, let's see how to call and debug it. Normal DLL files do not need to be registered, but must be included in the Windows Search Path before they can be found. The search path is in the following sequence: Current Directory, Path, Windows directory, and widows system directory (system, system32 ).
How to introduce DLL routines
Predefine the function/process before using the code of an external routine (DLL function/process. It is declared as is according to the definition in DLL, and only needs to be declared. At the same time, the extension external is introduced, which corresponds to export. Based on the three index syntaxes of exports, there are also three methods to determine the routine (taking function declaration as an example ):
Function Name (parameter table): return value; External 'dll filename ';
Function Name (parameter table): return value; External 'dll filename 'index index number;
Function Name (parameter table): return value; new name of external 'dll filename 'name;
If you are not sure about the routine name, you can use the index method. If a conflict occurs when you import data by the original name, you can use a new name.
After the declaration, the use of the DLL function is the same as that of the general function. The static call method is simple, but the DLL is called as a backup process when the call program is started. If the DLL file does not exist, an error is prompted at startup and the program is terminated immediately, regardless of whether the definition is used or not.
You can use the tool tdump.exe (in the bin directory of Delphi or BCB) provided by borlandas to define the quick release dllexample. The example is as follows:
Tdump C:/Windows/system/user32.dll> user32.txt
Open the user32.txt file and find the exports from user32.dll line. The following part is defined by the DLL routine, for example:
RVA Ord. Hint name
--------------------
00001371 1 0000 activatekeyboardlayout
20175c20 2 0001 adjustwindowrect
2017161b 3 0002 adjustwindowrectex
The name column is the name of the routine, and the Ord column is the index number of the routine. Note that the tool cannot obtain the parameter table of the routine. If the parameter is incorrect, calling the DLL routine will cause a stack error and cause the calling program to crash.
Call Code
Create a common project, place a tshelltreeview control (samples page) on the main form, and then place a button. Add the following code:
Function deletedir (dirname: pchar): Boolean; stdcall; External 'fileoperate. dll ';
Procedure tform1.button1click (Sender: tobject );
Begin
If directoryexists (shelltreeview. Path) then
If application. MessageBox (pchar ('Are you sure you want to delete the directory' + quotedstr (shelltreeview. Path) +? '), 'Information', mb_yesno) = idyes then
If deletedir (pchar (shelltreeview. Path) then
Showmessage ('deleted successfully ');
End;
This example calls the previously created DLL.
Note that the stdcall suffix must be included in the Declaration to ensure that the parameter values similar to pchar In the DLL routine called by Delphi are correctly transmitted. If you are interested, you can try it out. If you do not add the stdcall or safecall suffix to execute the above Code, you will not be able to pass the string parameter to the DLL function.
Debugging method
Select parameters from the run project on the Delphi main menu to open the "run Parameters" dialog box. Enter a Host Program in Host application (the program calls the DLL to be debugged), and you can also enter parameters in parameters. Save the content, and then you can set the breakpoint, tracking, and single-step execution in the DLL project.
Run the DLL project and then run the Host Program. The execution will call the DLL operation, and then you will be able to trace the code that enters the DLL. The subsequent debugging operations are the same as those of common programs.
Due to the impact of the operating system or other software, the above steps may still fail to track or interrupt the DLL code. In this case, select include td32 debug info and include remote debug symbols in EXE and DLL options on the linker page of the menu project | Options dialog box.
If it still cannot be interrupted-_____-|, you have to create another application that references the Execution Code unit, compile the DLL After coding and calling routine debugging is complete (in fact, this method is sometimes very convenient, but sometimes it is very troublesome ).
Import File
When a DLL is complicated, you can create an import unit for its declaration, which makes it easier to maintain and view the DLL. The format of the introduced unit is as follows:
Unit mydllimport; {import unit for mydll. dll}
Interface
Procedure mydllproc;
...
Implementation
Procedure mydllproc; External 'mydll 'Index 1;
...
End.
In this way, if you want to use a routine in mydll, simply add mydllimport to the uses clause in the program module. In fact, this is only a convenient development technique. When you open windows and other modules that introduce windows APIs, You can see similar practices.
Dynamic (explicit) Call of DLL
As mentioned above, the DLL will be called when the call program is started. Therefore, this method can only serve as a public DLL and reduce the size of the running file, and the DLL loading error will immediately lead to the termination of the entire startup process, even if the DLL is running only plays a negligible role.
By dynamically calling a DLL, the DLL is loaded to the memory only when an external routine is called (the DLL is automatically cleared from the memory when the reference count is 0), thus saving the memory space. In addition, you can determine whether the load is correct to avoid calling the program crash, at most the loss of the routine function.
Although dynamic calling has the above advantages, but for frequently used routines, due to the DLL call and release there will be additional performance loss, so such routines are suitable for static introduction.
Call example
The principle of DLL dynamic calling is to declare a function/process type and create a pointer variable. To ensure that the pointer is consistent with the external routine pointer to ensure correct value assignment, the declaration of the function/process must be compatible with the original declaration of the external routine (compatible means 1. The parameter name can be different; 2. The parameter/return value type can at least be assigned to each other. For example, if the original type is declared as word, the new declaration can be integer. If the passed real parameter is always within the word range, ).
Next, use the Windows API function loadlibrary to introduce the specified library file. The loadlibrary parameter is the DLL file name, and a thandle is returned. If this step succeeds, use another API function getprocaddress to obtain the entry address of the routine. The parameters are the pointer of loadlibrary and the name of the routine, and the entry pointer of the routine is returned. Assign the pointer to the pre-defined function/process pointer, and then you can use this function/process. Remember to use the API function freelibrary to reduce the DLL reference count to ensure that the memory can be cleared after the DLL is used. The Delphi declaration of these three API functions is as follows:
Function loadlibrary (libfilename: pchar): thandle;
Function getprocaddress (module: thandle; procname: pchar): tfarproc;
Procedure freelibrary (libmodule: thandle );
Change the code of the preceding static DLL call routine to dynamic call, as shown below:
Type
Tdllproc = function (pathname: pchar): Boolean; stdcall;
VaR
Libhandle: thandle;
Delpath: tdllproc;
Begin
Libhandle: = loadlibrary (pchar ('fileoperate. dll '));
If libhandle> = 32 then begin
Try
Delpath: = getprocaddress (libhandle, pchar ('deletedir '));
If directoryexists (shelltreeview. Path) then
If application. MessageBox (pchar ('Are you sure you want to delete the directory' + quotedstr (shelltreeview. Path) +? '), 'Information', mb_yesno) = idyes then
If delpath (pchar (shelltreeview. Path) then
Showmessage ('deleted successfully ');
Finally
Freelibrary (libhandle );
End;
End;
End;
Dynamic transfer of 16-bit DLL
The following example shows how to call a 16-bit DLL routine, which is a hidden API function in Windows9x. The Code is a mixture of static and dynamic calling methods. In addition to getting familiar with it, you can also see the solution for calling 16-bit DLL. First, explain the problem:
The function I want to implement is to obtain Win9x "system resources ". There is no "system resource" concept in winnt/2000, because the stack and handle in winnt/2000 are no longer limited to 64 K size as Win9x. To obtain this value, you can use a hidden API function getfreesystemresources in user DLL of Win9x.
This dll routine must be dynamically introduced. If the static statement is declared, an error occurs immediately when running the statement in Win2000. This compatibility cannot be solved. Therefore, the system version must be determined first. If it is Win9x, it must be dynamically loaded. The code used to check the operating system version is:
VaR
Osversion: _ osversioninfoa;
Fwinveris9x: Boolean;
Begin
Osversion. dwosversioninfosize: = sizeof (_ osversioninfoa );
Getversionex (osversion );
Fwinveris9x: = osversion. dwplatformid = ver_platform_win32_windows;
End;
The preceding call to the API function has been declared in the Windows unit.
Function loadlibrary16 (libraryname: pchar): thandle; stdcall; External Kernel32 index 35;
Procedure freelibrary16 (hinstance: thandle); stdcall; External Kernel32 index 36;
Function getprocaddress16 (hinstance: thandle; procname: pchar): pointer; stdcall; External Kernel32 index 37;
Function twinresmonitor. getfreesystemresources (sysresource: Word): word;
Type
Tgetfreesysres = function (value: integer): integer; stdcall;
Tqtthunk = procedure (); cdecl;
VaR
Prochandle: thandle;
Getfreesysres: tgetfreesysres;
Procthunkh: thandle;
Qtthunk: tqtthunk;
Thunktrash: array [0 .. $20] of word;
Begin
Result: = 0;
Thunktrash [0]: = prochandle;
If fwinveris9x then begin
Prochandle: = loadlibrary16('user.exe ');
If prochandle> = 32 then begin
Getfreesysres: = getprocaddress16 (prochandle, pchar ('getfreesystemresources '));
If assigned (getfreesysres) then begin
Procthunkh: = loadlibrary (pchar ('kernel32. dll '));
If procthunkh> = 32 then begin
Qtthunk: = getprocaddress (procthunkh, pchar ('qt _ thunk '));
If assigned (qtthunk) then
ASM
Push sysresource // push arguments
MoV edX, getfreesysres // load 16-bit procedure pointer
Call qtthunk // call thunk
MoV result, ax // Save the result
End;
End;
Freelibrary (procthunkh );
End;
End;
Freelibrary16 (prochandle );
End
Else result: = 100;
End;
First, three APIs, such as loadlibrary16, are declared statically (or dynamically, to reduce code ). Because loadlibrary cannot properly call 16-bit routines (Microsoft !), Therefore, use loadlibrary16, freelibrary16, and getprocaddress16. They are consistent with loadlibrary, freelibrary, and getprocaddress in meaning, usage, and parameters. The only difference is that they must be used to correctly load 16-bit routines.
In the definition section, the function pointers tgetfreesysres and tqtthunk are declared. The stdcall and cdecl parameters define the behavior of the stack. They must be defined according to the original function and cannot be changed.
If the call method is similar to the general routine call method, this step is tracked: if assigned (getfreesysres) then begin getfreesysres has been correctly loaded and the function address is available, getfreesysres (INT) cannot be used normally )!!!
So here, dynamic loading (the reason is that it cannot be executed in Win2k) is a seemingly redundant process qt_thunk. For a 32-bit external routine, qt_thunk is not required. However, for a 16-bit routine, the above assembly code must be used (for unclear friends, refer to the Delphi syntax)
ASM
Push sysresource
MoV edX, getfreesysres
Call qtthunk
MoV result, ax
End;
It is used to press the parameter into the stack, find the getfreesysres address, use qtthunk to convert the 16-bit address to 32-bit, and finally execute it correctly and return the value!
FAQs about DLL development in Delphi
String Parameters
As mentioned above, pointers or basic types should be used whenever possible to ensure the correctness of DLL parameter/return value transfer, especially for host programs developed for C ++ and other languages, because the variable storage allocation methods in other languages and Delphi may be different. In C ++, the character is the basic type, and the string is the Linear Linked List of character type. Therefore, it is best to forcibly convert string to pchar.
If the DLL and the Host Program are developed using Delphi and the string (with dynamic arrays, their data structures are similar) is used as the parameter/Return Value of the export routine, add sharemem as the first reference unit of the uses statement in the project file. Sharemem is the interface unit of borlndmm. dll, the memory manager shared by Borland. The release of DLL that references this unit must include borlndmm. dll. Otherwise, you must avoid using string.
Create and display a form in DLL
All forms-based Delphi applications automatically contain a global object application, which is familiar to everyone. It is worth noting that the DLL created by Delphi also has an independent application. Therefore, if the form created in the DLL is to become the application's mode form, the application must be replaced with the application; otherwise, the result is unpredictable (after the form is created, operations on it, such as minimization, will not belong to any main form ). In DLL, MessageBox should be used instead of showmessage.
It is relatively simple to create a mode form in DLL. Pass the application. Handle attribute as a parameter to the DLL routine, assign the handle to the application. Handle Of the DLL, and then use the application to create a form.
Mode-free forms are more complex. In addition to creating a display form routine, a corresponding release form routine must also be provided. You must be very careful about the mode-free form. The call to create and release routines must be controlled in the calling program. This has two meanings: 1. To prevent multiple creation of the same form instance; 2. To create a mode-free form, the application must release it, otherwise, if there is another code in the DLL that is released first, then calling the release routine will fail.
Below is the DLL form code:
Unit usampleform;
Interface
Uses
Windows, messages, sysutils, variants, classes, graphics, controls, forms,
Dialogs, extctrls, stdctrls;
Type
Tsampleform = Class (tform)
Panel: tpanel;
End;
Procedure createandshowmodalform (apphandle: thandle; caption: pchar); export; stdcall;
Function createandshowform (apphandle: thandle): longint; export; stdcall;
Procedure closeshowform (aformref: longint); export; stdcall;
Implementation
{$ R *. DFM}
// Mode form
Procedure createandshowmodalform (apphandle: thandle; caption: pchar );
VaR
Form: tsampleform;
STR: string;
Begin
Application. Handle: = apphandle;
Form: = tsampleform. Create (application );
Try
STR: = Caption;
Form. Caption: = STR;
Form. showmodal;
Finally
Form. Free;
End;
End;
// Non-mode form
Function createandshowform (apphandle: thandle): longint;
VaR
Form: tsampleform;
Begin
Application. Handle: = apphandle;
Form: = tsampleform. Create (application );
Result: = longint (form );
Form. show;
End;
Procedure closeshowform (aformref: longint );
Begin
If aformref> 0 then
Tsampleform (aformref). release;
End;
End.
DLL engineering unit introduction statement:
Exports
Closeshowform,
Createandshowform,
Createandshowmodalform;
Application call declaration:
Procedure createandshowmodalform (handle: thandle; caption: pchar); stdcall; External 'fileoperate. dll ';
Function createandshowform (apphandle: thandle): longint; stdcall; External 'fileoperate. dll ';
Procedure closeshowform (aformref: longint); stdcall; External 'fileoperate. dll ';
In addition to common forms, how do I create tmdichildform in DLL? In fact, it is similar to creating a common form, but this time you need to pass the application. mainform of the calling program as the parameter:
Function showform (mainform: tform): integer; stdcall
VaR
Form1: tform1;
PTR: plongint;
Begin
PTR: = @ (application. mainform); // Save the mainform handle of the DLL without releasing it.
PTR ^: = longint (mainform); // Replace the mainform of the DLL with the mainform of the calling program
Form1: = tform1.create (mainform); // create with Parameters
End;
The reason why a temporary pointer is used in the Code is that application. mainform is a read-only attribute. You do not need to set the formstyle of the MDI form to fmmdichild.
Initialize the com Library
If a COM component such as tadoconnection or ActiveX control is used in the DLL, an error such as "mark no reference storage" will be prompted during the call, because the COM is not initialized. The DLL does not call coinitilizeex. initializing the com library is considered the responsibility of the application. This is Borland's implementation policy.
What you need to do is 1. Reference ActiveX units to ensure that the coinitilizeex function is correctly called.
2. Add initialization and exit code at the unit level:
Initialization
Coinitialize (NiL );
Finalization
Couninitialize;
End.
3. Remember to close the connection and dataset at the end. Otherwise, an address error will be reported.
Extracts the objects in the DLL.
From the DLL form example, we can find that the handle is passed to the DLL as a parameter, and the DLL can point to the instance of this handle. In the same way, the basic idea of extracting an object from a DLL is to return the pointer of the object in the DLL through a function and assign the pointer to the variable of the Host Program, point the variable to the address of an object in the memory. The operation on this variable is the operation on the objects in the DLL.
This article does not explain the code in detail, but only describes the rules that need attention:
Applications can only access virtual methods in objects, so the object methods to be referenced must be declared as virtual methods;
The same object and method definition are required in the DLL and application, and the method definition sequence must be consistent;
Objects in DLL cannot be inherited;
Object instances can only be created in DLL.
The purpose of declaring a virtual method is not to overload the method, but to add the method to the virtual method table. Object methods are different from common routines. In this way, the application can obtain the method pointer.
After all, DLL is a product of the era of structured programming. function-level code sharing makes it impossible to achieve objectiveness. Nowadays, similar DLL functions, but new methods that provide powerful support for objects have been widely used, such as interfaces (COM/DCOM/COM +) and other technologies. In-process server programs are a DLL file from the External table, but they provide support by registering and releasing a series of interfaces instead of using external routines. There are two major differences between it and DLL usage: You need to register it and call the service by creating an interface object. It can be seen that although dll can also lead to objects through some techniques, it is inconvenient to use, and it is often forced to convert the object to a procedural method. In this case, it is best to consider a new implementation method.
Note: The code in this article has been debugged in DELPHI6 and 7.
Appendix: This document provides the "delphi5 developer Guide" and other books and materials.