Programming of the plug-in structure requires a plug-in container to control the operation of each DLL, and arrange each divided subsystem into a DLL library file. For each DLL program, interface functions need to be reserved for the container. Generally, interface functions include: functions that start calling the DLL library and functions that close the DLL library. Through the interface function, the plug-in container can pass parameters to the DLL module to achieve dynamic control. I will explain the specific implementation details and give the response code below.
You may need to first understand the structure of UNIT and the structure of projects in DELPHI. This article does not discuss the theoretical details of DLL programming in depth, but only demonstrates some practical codes. I was studying the book "DELPHI In-depth Programming" by Liu Yi.
I am also in the introductory stage of DELPHI. I just feel that there are some things worth discussing in this DLL development, so I am writing this article. I hope you can give generous suggestions on what I am not doing well.
Introduction to the sample program
In order to facilitate reading, I will use part of the program code of an MIS system to demonstrate some methods of plug-in programming. The sample program is a typical C/S structure DBMS application. Our focus will be on the control statements of the framework program (hereinafter referred to as Hall) and the response control of the dll plug-in program.
1. Program structure
The plug-in container Hall is created using an independent project. The main window of the Hall is equivalent to the MDI container form in the MDI program. The interface functions in the Dll will be explicitly called in the Hall.
Each plug-in program uses its own project independently. Different from ordinary projects, the DLL project creates Dll Wizard, and the files generated by the corresponding compilation have the suffix DLL.
=550) window.open('/upload/20080315181507424.jpg');" src="http://files.VeVB.COm/upload/20080315181507424.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';" border=0>
2. Interface design
In the example program Narcissus, we reserve two interface functions:
ShowDLLForm
This function passes the handle of the application to the DLL child window, and the DLL program will dynamically create an instance of the DLL form. You can also pass some business logic to the DLL sub-window in the form of parameters, such as the form name, the currently logged in user name, etc. Use this function to create a DLL form instance when it is called for the first time.
FreeDLLForm
This function will display the release of the DLL window instance, and call the FreeDLLForm method of each DLL form to release the created instance when exiting the application, otherwise it will cause a memory read-only error. Similarly, you can also pass some business logic that needs to be done when releasing the form to the DLL form in the form of parameters.
3. Debugging method
The DLL form program cannot be executed directly and needs a plug-in container to call it. Therefore, we need to first implement a basic Hall program, and then save Hall.exe in a fixed directory. Make the following settings for each DLL project:
1) Open the DLL project
2) Select menu Run Parameters
3) Browse to our container Hall.exe in the pop-up window
In this way, the Hall program will be automatically called when debugging the DLL program, and the DLL program will be debugged using the calling interface reserved in the Hall.
Basic implementation of plug-in program
The design method of the DLL program is not very different from that of ordinary WINAPP, except that all windows are saved in the DLL library as a special "resource" and need to be called manually, unlike in WINAPP where projects are automatically created. The method of declaring interface functions is very simple
1) Declare the function in the Implementation section of Unit
2) Add the stdcall mark at the end of the function declaration statement
3) Before the begin statement of the project code (Project View Source), use the exports statement to declare the function interface
In order to make the code concise, I personally like to add a Unit unit (File New -- Unit) independently in the project, and then define all the function bodies to be output in this unit. Don't forget to also use the Unit of the form that is referenced. Come in. I named this unit UnitEntrance, initialized the window to be displayed in the ShowDLLForm function and called the Show method to display it. HALL will pass the logged in user name as parameters. After getting the user name, some permission control can be performed, which is reflected in the interface initialization. superior.
The code is as follows
unit UnitOfficeEntrance;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms;
function ShowDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;stdcall;
function FreeDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;stdcall;
implementation
uses UnitOfficialMainForm; //Change to unit of MAINFORM
var
DLL_Form:TFormOfficialMain; //Change to the name of MAINFORM
//----------------------------------------
//Name: ShowDLLForm
//Func: DLL plug-in calls the entry function
//Para: AHandle attached program handle; ACaption title of this form
//Rtrn: N/A
//Auth: CST
//Date: 2005-6-3
//----------------------------------------
function ShowDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;
begin
result:=true;
try
Application.Handle:=AHandle; //Anched to the main program container
DLL_Form:=TFormOfficialMain.Create(Application); //Change to the NAME of MAINFORM
try
with DLL_Form do
begin
Caption := ACaption;
StatusBar.Panels.Items[0].Text := AUserID;
//Configure UI
Show ;
end;
except
on e:exception do
begin
dll_form.Free;
end;
end;
except
result:=false;
end;
end;
//----------------------------------------
//Name: FreeDLLForm
//Func: DLL plug-in calls the export function
//Para: AHandle attached program handle
//Rtrn: true/false
//Auth: CST
//Date: 2005-6-11
//----------------------------------------
function FreeDLLForm(AHandle: THandle; ACaption: string; AUserID: string):boolean;
begin
Application.Handle:=AHandle; //Anched to the main program container
if DLL_Form.Showing then DLL_Form.Close; //If the window is opened and closed first, triggering FORM.CLOSEQUERY can cancel the closing process
if not DLL_Form.Showing then
begin
DLL_Form.Free;
result:=true;
end //Still open, indicating CLOSEQUERY.CANCLOSE=FALSE
else
begin
result:=false;
end;
end;
end.
The DLL project file code is as follows:
library Official;
{ Important note about DLL memory management: ShareMem must be the
first unit in your library's USES clause AND your project's (select
Project-View Source) USES clause if your DLL exports any procedures or
functions that pass strings as parameters or function results. This
applies to all strings passed to and from your DLL--even those that
are nested in records and classes. ShareMem is the interface unit to
the BORLNDMM.DLL shared memory manager, which must be deployed along
with your DLL. To avoid using BORLNDMM.DLL, pass string information
using PChar or ShortString parameters. }
uses
SysUtils,
Classes,
UnitOfficialDetailForm in 'UnitOfficialDetailForm.pas' {FormOfficialDetail},
UnitOfficialMainForm in 'UnitOfficialMainForm.pas' {FormOfficialMain},
UnitOfficeEntrance in 'UnitOfficeEntrance.pas',
UnitOfficialClass in '../../Public/Library/UnitOfficialClass.pas',
UnitMyDataAdatper in '../../Public/Library/UnitMyDataAdatper.pas',
UnitMyHeaders in '../../Public/Library/UnitMyHeaders.pas';
{$R *.res}
exports ShowDLLForm,FreeDLLForm; //interface function
begin
end.
Once the plug-in program calls the DLL window, the window instance will remain on top of the HALL window, so there is no need to worry about occlusion.
Implementation of container programs
1. Introduction of interface functions
There are two ways to call functions in the DLL library: explicit and implicit. Explicit calling is more flexible, so we use explicit calling. In Delphi, you need to declare the function type for the interface function, and then instantiate an instance of the function type. The instance is actually a pointer to the function. Through the pointer, we can access the function, pass parameters, and obtain the return value. Add the declaration of the function class in the Interface section of the unit file:
type
//Define the interface function type, the interface function comes from the DLL interface
TShowDLLForm = Function(AHandle:THandle; ACaption: String; AUserID:string):Boolean;stdcall;
TFreeDLLForm = Function(AHandle:THandle; ACaption: String; AUserID:string):boolean;stdcall;
Display calling library functions requires the following steps:
1) Load DLL library file
2) Get function address
3) Execute function
4) Release the DLL library
Next we will discuss these steps in detail.
2. Load the DLL library file
The DLL library can be loaded into memory by calling the API function LoadLibrary. We will not discuss the impact of DLL on memory management here. The parameter of LoadLibrary is the address path of the DLL file. If the loading is successful, a CARDINAL type variable will be returned as the handle of the DLL library; if the target file does not exist or other reasons cause the loading of the DLL file to fail, a 0 will be returned.
3. Instantiate interface functions
The API function to obtain the interface function pointer is GetProcAddress (library file handle, function name). If the function is found, the pointer of the function will be returned. If it fails, NIL will be returned.
Define a function pointer variable using the function type defined above, and then use the @ operator to get the function address, so that you can use the pointer variable to access the function. The main code is as follows:
…
var
ShowDLLForm: TShowDLLForm; //DLL interface function instance
FreeDLLForm: TFreeDLLForm;
begin
try
begin
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr := GetProcAddress(APlugin.ProcAddr ,'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
if ShowDllForm(Self.Handle, APlugin.Caption, APlugin.UserID) then
Result:=True
…
4. A specific implementation method
In order to manage plug-ins in a structured manner and facilitate future system expansion, we can combine the available DLL information recorded in the database, and then dynamically access the DLL program by querying the database records.
1) System module table design
For MIS systems, you can use existing DBS conditions to establish a system module table to record DLL files and related information mapped to system modules.
Field name role type
AutoID indexINT
modAlias module alias VARCHAR
modName module name VARCHAR
modWndClass form unique identifier VARCHAR
modFile DLL path VARCHAR
modMemo note TEXT
・Module aliases are used to unify naming rules during the programming design phase, especially for team members’ reference during team development.
・The module name will be passed as the ACAPTION parameter to the SHOWDLLFORM function as the title of the DLL window.
・The unique identifier of the form is the CLASSNAME of the main window in the DLL submodule, which is used to determine the window to be controlled at runtime.
・The DLL path saves the DLL file name, which will be converted into an absolute path in the program.
2) Plug-in information data structure
Defining a data interface that records plug-in related information can centrally control DLL plug-ins. Add the following code to the Interface section:
type
//Define plug-in information class
TMyPlugins = class
Caption:String; //DLL form title
DllFileName:String; //DLL file path
WndClass:String; //Form identification
UserID:string; //Username
ProcAddr:THandle; //Library handle loaded by LOADLIBRARY
FuncAddr:Pointer; //SHOWDLLFORM function pointer
FuncFreeAddr:Pointer; //FREEDLLFORM function pointer
end;
…
Create an instance of TMyPlugins for each plug-in. The initialization methods for these instances will be discussed below.
3) Plug-in loading function
In this example, the DLL window is loaded and displayed in the event that triggers the opening of the child window in HALL. After the button event is triggered, first determine whether the DLL has been loaded according to the plug-in structure instance. If it has been loaded, control the display or closing of the window; if it is not loaded, access the data table and assign the fields to the plug-in structure, and then execute the loading. Getting the pointer works.
The partial code is as follows
…
//----------------------------------------
//Name: OpenPlugin
//Func: Plug-in information control process: Initialization==》Set permissions==》Load DLL window
//Para: APlugin-TMyPlugins; sAlias alias; iFuncValue permission value
//Rtrn: N/A
//Auth: CST
//Date: 2005-6-2
//----------------------------------------
procedure TFormHall.OpenPlugin(AFromActn: TAction ;APlugin:TMyPlugins; sAlias:string; sUserID:string);
var hWndPlugin:HWnd;
begin
//Determine whether the plug-in window has loaded hWndPlugin:=FindWindow(PChar(APlugin.WndClass),nil);
if hWndPlugin <> 0 then //The plug-in window has been loaded
begin
if not IsWindowVisible(hWndPlugin) then
begin
AFromActn.Checked := True;
ShowWindow(hWndPlugin,SW_SHOWDEFAULT); //Display
end
else
begin
AFromActn.checked := False;
ShowWindow(hWndPlugin,SW_HIDE);
end;
Exit; //Leave the plug-in creation process
end;
//Initialize plug-in class instance
if not InitializeMyPlugins(APlugin,sAlias) then
begin
showmessage('Error initializing plug-in class.');
exit;
end;
//Get the current permission value
APlugin.UserID := sUserID;
//Load DLL window
if not LoadShowPluginForm(APlugin) then
begin
showmessage('Error loading center plug-in.');
exit;
end;
end;
//----------------------------------------
//Name: InitializeMyPlugins
//Func: Initialize MYPLUGIN instance (Caption | DllFileName | IsLoaded)
//Para: APlugin-TMyPlugins
//Rtrn: N/A
//Auth: CST
//Date: 2005-6-2
//----------------------------------------
function TFormHall.InitializeMyPlugins(APlugin:TMyPlugins; sAlias:String):Boolean;
var
strSQL:string;
myDA:TMyDataAdapter;
begin
Result:=False;
myDA:=TMyDataAdapter.Create;
strSQL:='SELECT * FROM SystemModuleList WHERE modAlias='+QuotedStr(sAlias);
try
myDA.RetrieveData(strSQL);
except
on E:Exception do
begin
result:=false;
myDA.Free;
exit;
end;
end;
try
begin
with myDA.MyDataSet do
begin
if Not IsEmpty then
begin
APlugin.Caption:= FieldByName('modName').Value;
APlugin.DllFileName := FieldByName('modFile').Value;
APlugin.WndClass := FieldByName('modWndClass').Value;
result:=True;
end;
Close;
end; //end of with...do...
end; //end of try
except
on E:Exception do
begin
Result:=False;
myDA.Free;
Exit;
end; //end of exception
end; //end of try...except
myDA.Free;
end;
//----------------------------------------
//Name: LoadShowPluginForm
//Func: Load the DLL plug-in and display the window
//Para: APlugin-TMyPlugins
//Rtrn: true-created successfully
//Auth: CST
//Date: 2005-6-2
//----------------------------------------
function TFormHall.LoadShowPluginForm (const APlugin:TMyPlugins):boolean;
var
ShowDLLForm: TShowDLLForm; //DLL interface function instance
FreeDLLForm: TFreeDLLForm;
sPath:string; //Full path of DLL file
begin
try
begin
sPath:=ExtractFilepath(Application.ExeName)+ 'plugins/' + APlugin.DllFileName;
APlugin.ProcAddr := LoadLibrary(PChar(sPath));
APlugin.FuncFreeAddr := GetProcAddress(APlugin.ProcAddr,'FreeDLLForm');
APlugin.FuncAddr := GetProcAddress(APlugin.ProcAddr ,'ShowDLLForm');
@ShowDLLForm:=APlugin.FuncAddr;
@FreeDLLForm:=APlugin.FuncFreeAddr;
if ShowDllForm(Self.Handle, APlugin.Caption, APlugin.UserID) then
Result:=True
else
Result:=False;
end;
except
on E:Exception do
begin
Result:=False;
ShowMessage('Error loading plug-in module, please check whether the files in the PLUGINS directory are complete.');
end;
end;
end;
…
4) DLL window control
As the code in 3) illustrates, the opening and closing of the DLL window is only at the presentation layer. Closing the window does not actually release the DLL window. It just calls the API function FindWindow to obtain the form handle according to the window identifier (that is, Form.name). Use The nCmdShow parameter of the SHOWWINDOW function controls the display/hiding of the window.
In fact, this is a weak point in the implementation of my program. If the Self.close method is used in the DLL window, it will cause a memory error. There is no way to solve it due to limited capabilities, so this is the last resort. Therefore, the close button of the main window of each DLL program must be hidden. :-P
5) Release of DLL library
When the program exits, the DLL library must be released one by one according to the plug-in information instance. The function to release the DLL library is as follows:
procedure TFormHall.ClosePlugin(aPLG:TMyPlugins);
var
FreeDLLForm:TFreeDLLForm;
begin
if aPLG.ProcAddr = 0 then exit;
if aPLG.FuncFreeAddr = nil then exit;
@FreeDLLForm:=aPLG.FuncFreeAddr;
if not FreeDLLForm(Application.Handle,'','') then
showMessage('err');
end;
summary
The running effect of this example program is as follows:
=550) window.open('/upload/20080315181507979.jpg');" src="http://files.VeVB.COm/upload/20080315181507979.jpg" onload="if(this.width>'550')this.width='550';if(this.height>'1000')this.height='1000';" border=0>
Among the above methods, because there are many unresolved problems due to limited ability, I have adopted some cover-up methods that do not seem reasonable. I hope that everyone can design a better solution after trying a little bit. I also hope that A great way to learn more.