The following is a typical
Introduction to plugin technology (no record, sorry)
Delphi
Plug-Ins created, debugging and using application extensions
Keywords: Delphi controls miscellaneous
Have you used Adobe Photoshop? If you have used, you will
The concept of plugin is more familiar.
For foreign pedestrians,
The plugin is only the code block supplied to the application from the outside (for example,
In a DLL). One
The difference between the plugin and a normal DLL is
The plugin has an extended parent application function
Ability. For example, Photoshop itself does not have a large number of image processing functions.
The plugin is added
Get strange effects such as blur, spots, and all other styles, and any of these features
Not the parent application itself.
This is very good for image processing programs, but why do you want to get bigger?
Insert business
Application? Suppose we give an example, your app will generate some reports. Your customers will definitely
Always ask for updates or add new reports. You can use an external report such as Report Smith
The device, this is a unsatisfactory solution, you need additional documents to make additional training,
and many more. You can also use QuickReport, but this will make you at your own nightmare - if every
Change the font. You have to Rebuild your application.
However, as long as you do the report
In the plugin, you can use it. Neat a new report?
No problem, just install a DLL, you will see it when the next application starts. Another example is processing
Applications from external equipment (such as barcode scanners), in order to give users more choices, you
Hemorant various devices have to be supported. By writing each device interface processing routine
Plug-in, no need to apply
The maximum scalability can be obtained for any change.
getting Started
The most important thing before starting writing code is to figure out how you need to expand your app.
Some functions. This is because
The plugin is interacting with the parent application through a specific interface, and this interface will
Define according to your needs. In this article, we will build 3
Plugin for display
Plugin and parent application
Several ways to interact.
We will put
The plugin is made into a DLL. However, before doing this work, we have to make an outside.
Shells to load and test them. Figure 1 shows the first one
The test program after the plugin. First
The plugin does not have a big demonstration, in fact, it is just returning a character that describes your own
string. However, it proves that it is very important - no matter what
Plug-in applications can run normally.
if there is not
Plugin, it will not appear in installed
In the plugin list, the application can still normal
Exercise function.
figure 1:
Plug-in test shell
our
The only difference between the plug-in housing and the ordinary application is that the project source file
The ShareMem unit and load appearing in the USES clause
The code of the plugin file. Any in itself and sub-DLL
Between the application of string parameters requires a ShareMemM.dll (Delphi)
The interface is provided). To test this case, you need to use the delphimm.dll file from Delphi / bin
The directory is copied to the path included in the PATH environment variable or in the directory where the application is located. Publish the final version
This file is also required at the time.
The plugin is loaded into this test housing through the loadPlugins process, this process is in the main window
Formcreate events, see Figure 2. This process uses the FINDFIRST and FINDNEXT functions to find in the directory where the application is located
Plugin file. After finding a file, use the loadPlugins shown in Figure 3.
Cheng will load it.
{Look up in the application directory
Plugin file}
Procedure tfrmmain.loadPlugins;
VAR
SR: TSEARCHREC;
PATH: STRING;
Found: integer;
Begin
PATH: = ExtractFilePath (Application.exename);
Try
Found: = FindFirst (Path CPlugin_mask, 0, SR);
While Found = 0 Do Begin
LoadPlugin (SR);
Found: = FindNext (SR);
END;
Finally
FindClose (SR);
END;
END;
Figure 2: Looking for
Plug-in
{Load specified
Plugin DLL.}
Procedure tfrmmain.loadPlugin (SR: tsearchrec);
VAR
Description: String;
LibHandle: Integer;
DescribeProc: TPLugindescribe;
Begin
LibHandle: = loadingLibrary (pchar (sr.name));
IF libhandle <> 0 THEN
Begin
Describeproc: = getProcaddress (librandle,
CPLUGIN_DESCRIBE);
IF assigned (describeproc) THEN
Begin
DescribeProc (Description);
Memplugins.Lines.Add (Description);
end
Else
Begin
Messagedlg ('file " sr.name
'"IS not a valid plug-in.',
Mtinformation, [Mbok], 0);
END;
end
Else
Messagedlg ('An Error Occurred Loading The Plug-in "'
Sr.name '"", mTerror, [Mbok], 0);
END;
Figure 3: Load
Plug-in
LoadPlugin method shows
The core of the plugin mechanism. First of all,
The plugin is written as a DLL. Secondly,
It is dynamically loaded through the LoadLibrary API. Once the DLL is loaded, we need an access to it.
The route of the process and function included. The API calls GetProcAddress provides this mechanism, it returns one
Pointer to the desired routine. In our simple demonstration,
The plugin only contains one name
DescribePlugin process, specified by constant cPlugin_Describe (the case case is very heavy)
To pass to the name to getProcAddress must be completely consistent with the routine names included in the DLL). Such as
If you don't find the requested routine in the DLL, getProcAddree will return nil, so that it is allowed to make
Determine the return value with an assigned function.
In order to store a pointer to a function in an easy-to-use manner, it is necessary to use the variable
Create a specific type. Note that the return value of getProcAddress is stored in a variable.
DescribeProc, belongs to the TPLugindescribe type. Here is its statement: Type
TPLugindescribe = procedure (var desc: string); stdcall;
Since the process exists inside the DLL, it compiles all export routines through standard calls.
So you need to use the stdcall indicator. This process uses a VAR parameter, when the process returns it
Contain
Description of the plugin.
To call the process just got, only the variables that save the address are required as the process name, followed by
Any parameters. As far as our example is concerned, statement:
DescribeProc (Description)
Will call
The description process obtained in the plugin is used, and the description
Plug-in function string Description Description
variable.
structure
Plug-in
We have created a parent application, and now this round is now created. We hope to load.
Plugin.
The plugin file is a standard Delphi DLL, so we create a new DLL project from the Delphi IDE.
Save it. Due to the exported
The plugin function will use the string parameters, so it is necessary to put in the Uses clause of the project.
The Sharemen unit is placed in front. Figure 4 is listed in our simple
Plugin engineering source file.
Uses
Sharemem, Sysutils, Classes,
Main in 'main.pas';
{$ E PLG.}
Exports
Describeplugin;
Begin
End.
Figure 4: Simple
Plugin engineering source file
although
The plugin is a DLL file, but there is no need to give it a .dll extension.
In fact, one reason is enough to let us have reason to change the extension: When the parent application finds the text to be loaded
The new extension can be a specific file mask. By using other extensions (our example
With * .plg), you can confirm that the application will only load the corresponding file. Compile instruction
Word $ x can achieve this change, or you can set up by the Project Options dialog
Place the expansion name.
First example
The code of the plugin is very simple. Figure 5 shows the generation included in a new unit
code. Note that the DescribePlugin prototype is consistent with the TPLugindescribe type in the housing application.
Specifying the process using an additional Export reserved word will be exported. The derived process name will also appear in the main
The Exports segment of the project source code (listed in Figure 4).
Unit main;
Interface
Procedure DescribePlugin (Var Desc: String);
Export; stdcall;
IMPLEMENTATION
Procedure DescribePlugin (Var Desc: String);
Begin
Desc: = 'Test Plugin V1.00';
END;
End.
Figure 5: Example
Plug-in main program
Test this
Before the plugin, copy it to the path to the primary application. The easiest way
It is created under the subdirectory of the main directory.
Plugin, then set the output path as the main path (Project Options
Directories / Conditionals in the box can also make this setting).
debugging
Now introduce a better function in Delphi 3: debug the DLL's ability to debug the DLL from the IDE. In DLL
The program can specify a program as a host application through the Run Paramaters dialog, which is to point to
Use the path to the application of the DLL (in our example, it is the test shell just created). Then you can set a breakpoint in the DLL code and press F9 to run it - just like it in a normal application.
kind. Delphi will run the specified host program, and guide you through the DLL with debugging information.
The breakpoint within the DLL code.
Delphi
Plug-Ins created, debugging and using application extensions (continued)
Keywords: Delphi controls miscellaneous
Extends the father application
This simple
The plugin is good, but it can't do what useful. The second example is correcting this problem.
This one
The goal of the plugin is to join a project in the main menu of the parent application. This menu item, when
When hit, it will be performed.
Some of the code in the plugin. Figure 6 shows the improvement version of the shell program, two
Plugins have been added
Load. In this version of the housing, a new menu item called Plug-in is added to the main menu.
The plugin will join a menu item at runtime.
Figure 6: Loading two
Improvement version of the plug-in shell
In order to achieve this, we must
The second interface is defined in the plugin DLL. Existing DLL only exports one
Process, DescribePlugin. the second
The plugin will declare a process called Initplugin. However,
This process can be seen in the primary application, you must modify loadplugin to cooperate with it.
The code shown in Figure 7 shows an improved process.
Procedure tfrmmain.loadPlugin (SR: tsearchrec);
VAR
Description: String;
LibHandle: Integer;
DescribeProc: TPLugindescribe;
Initproc: tplugininit;
Begin
LibHandle: = loadingLibrary (pchar (sr.name));
IF libhandle <> 0 THEN
Begin
// Find DescribePlugin.
Describeproc: = getProcaddress (librandle,
CPLUGIN_DESCRIBE);
IF assigned (describeproc) THEN
Begin
// Call DescribePlugin.
DescribeProc (Description);
Memplugins.Lines.Add (Description);
// Find Initplugin.
INitproc: = getProcaddress (libhandle, cplugin_init);
IF assigned (initproc) THEN
Begin
// Call InitPlugin.
INTPROC (Mnumain);
END;
end
Else
Begin
Messagedlg ('file " sr.name
'"is not a valid plugin.',
Mtinformation, [Mbok], 0);
END;
end
Else
Begin
Messagedlg ('An Error Occurred Loading The Plugin "
Sr.name '"", Mtinformation, [Mbok], 0);
END;
END;
Figure 7: Improved LoadPlugin method
As you can see, when getProcaddress looks for the first time to find the call description process, it is called again.
GetProcAddress. This time, we have to find constant cPlugin_init, defined as follows: const
CPLUGIN_INIT = 'Initplugin';
The return value is stored in a variable of the TPLuginInit type, is defined as follows:
Type
TpluginInit = procedure (ParentMenu: TmainMenu); stdcall;
When the InitPlugin method is executed, the main menu of the parent application is passed as a parameter to it. This one
The process can modify the menu according to your own will. Since all GetProcAddress returns use assigned
Test, the new version of the loadPlugin process still loads the first one that does not contain the initplugin process
Plugin. in
The first call in this process is looking for the DescribePlugin method, the second time I find the initplugin will
No response failed.
Now the new interface is already defined, you can write code for the new InitPlugin method. Like the same,
new
The implementation code of the plugin exists in a separate unit. Figure 8 shows the modified INitplugin method
Main.pas.
Unit main;
Interface
Uses dialogs, menus;
Type
Tholder = Class
public
Procedure ClickHandler (Sender: TOBJECT);
END;
Procedure DescribePlugin (Var Desc: String);
Export; stdcall;
Procedure Initplugin (ParentMenu: TmainMen);
Export; stdcall;
VAR
Holder: tholder;
IMPLEMENTATION
Procedure DescribePlugin (Var Desc: String);
Begin
DESC: = 'Test Plugin 2 - Menu Test';
END;
Procedure Initplugin (ParentMenu: TmainMen);
VAR
i: tmenuitem;
Begin
// Create a new menu item.
i: = newitem ('Plugin & Test', Scnone, False, True,
Holder.ClickHandler, 0, 'mnutest');
ParentMenu.Items [1] .ADD (i);
END;
Procedure tholder.clickhandler;
Begin
ShowMessage ('Clicked!');
END;
INITIALIZATION
Holder: = tholder.create;
Finalization
Holder.free;
End.
Figure 8: Second
Plugin code
Obviously, the original
The first change in the plugin is to increase the initplugin process. Like the original, with
The prototype of the export key is added to the list of the top of the unit, and the process name is also added to the project source code.
Exports subseext list. This process creates a new menu item using the NewItem function, and the return value is
TMenuItem object. The new menu item is added to the application main menu by the following statement:
ParentMenu.Items [1] .ADD (i);
Items [1] on the test outer shell main menu is the menu item Plug-in, so this statement is in the Plugin menu
Add a menu item called PLUG-IN TEST.
In order to deal with the response to the new menu item, as its fifth parameter, NewItem can accept one
The process of TNOTIFYEVENT types, this process will call when the menu item is clicked. Unfortunately, according to
Righteousness, this type of process is an object method, however in our
There is no object in the plugin. If we want
With the usual pointer to the function, then the resulting will only be the complaint of the Delphi compiler. So, the only one
The solution is to create an object that handles the menu. This is the use of the THOLDER class. It only has one party
Method, is a process called ClickHandler. A global variable called Holder, in the modified main.pas
The VAR segment is declared as the Tholder type and is created in the unit's initialization segment. Now let's now
There is an object, we can take its method (Holder.ClickHandler) as the parameter of the newItem function.
In addition to displaying a "Clicked!" Message dialog, ClickHandler does not do it.
Maybe this is not very interesting, but it still proves a little:
The plugin DLL successfully modified the main menu of the parent application,
It shows its new use. And like the first example, regardless of this
The plugin can be executed without the application.
Since we created an object to handle the menu click, then you no longer need this
When the plugin is plug-ins,
Object. The modified unit will handle this matter in the Finalization section. Finalization
The Initialization section corresponds, if there is an Initialization section in front, then in the application end
Termination of the Finalization section will be executed. Put the following statement
Holder.free
Add to the Finalization section to ensure that the Holder object will be released correctly.
Obviously, although this
The plugin simply modifies the main menu of the shell application, but it can easily manipulate the biography.
Hand any other objects in the initplugin process. if necessary,
The plugin can also open your own dialog,
Add a project to the list box (List Boxes) and Tree Views, or Canvas
Chinese painting.
Event driver
Plug-in
To now we have described the techniques we describe a generic extension application. Increasing
New menus, forms, and dialogs, you can implement a new feature without having to make any modifications to your parent. But still
There is a limit: this is just a single-Sided mechanism. As seen, the system relies on the user's
Some operations can be started
Plug-in code, such as clicking on the menu or similar action. After the code is running,
Relying on another user action to stop it, for example, close
The plug-in may have already opened the form. Overcome this lack
A feasible method of trap is to make
The plugin can respond to the action in the parent application - imitate working in Delphi
Very good event-driven programming model is indeed valid.
In the last example
In the plugin, we will create a mechanism.
Plug-in can take this response to things generated in the parent application
Part. Typically, it can be triggered by determining what events need to be triggered, and one is created for each event in the parent application.
TLIST object is implemented. Then each TLIST object is passed to
During the initialization of the plugin, if
Insert think
In an event, it will be added to the corresponding TLIST to be executed. Parent application
A list of these function pointers in an appropriate time, call each function in order. In this way,
A
The plug-in is possible to perform action in the same event.
Events generated by the application are completely dependent on the program determined. For example, a TCP / IP network application
May wish to inform the plugin data arrival through TClientSocket's OnRead event, and a graphical application can
Can be more interested in changes in the palette.
To illustrate event-driven
The concept of plug-in answers, we will create a minimum size for restriction main windows
of
Plugin. This example is a bit made because this feature will be more simple than this.
However, this example has the advantage of being easy to encode and is easy to understand, and this is what you want to do this article.
Obviously, what the first thing we have to do is deciding which events should be generated. In this example, the answer
Very simple: To limit the size of an application window, it is necessary to capture and modify Windows messages
WM_GETMINMAXSINFO. Therefore, to create a completion of this feature
Insert, we must capture this elimination
Interest and call in this message processor
Plug-in routine. This is the event we have to create.
Next we have to create a TLIST to handle this event. In the initialization section of the main form
Create a LSTMINMAX object, then create a message processor to capture Windows messages
WM_GETMINMAXINFO. The code in Figure 9 shows the message processor.
{Capture WM_GETMINMAXINFO. Call for each message
Plug-in routine.
Procedure tfrmmain.minMaxInfo (var Msg: tMessage);
VAR
M: PminMaxInfo; file: // Definition in Windows.PAS.
i: integer;
Begin
m: = Pointer (msg.lparam);
For i: = 0 to lstminmax.count -1 do begin
TresizeProc (LSTMINMAX [I]) (M.PTMINTRACKSIZE.X,
m.ptmintracksize.y);
END;
END;
Figure 9: Message processor for WM_GETMINMAXINFO
The LoadPlugin process of the shell application must be modified again to call the initialization routine. This new initialization
The function takes our TLIST as a parameter, and add the function address of the modified message parameter. Figure 10
The final version of the loadPlugin process is displayed, which can be implemented to all of the people discussed so far.
Plug-in
Initialization work.
{Load specified
Plugin DLL.}
Procedure tfrmmain.loadPlugin (SR: tsearchrec);
VAR
Description: String;
LibHandle: Integer;
DescribeProc: TPLugindescribe;
Initproc: tplugininit;
INITEVENTS: TinitPluginevents;
Begin
LibHandle: = loadingLibrary (pchar (sr.name));
IF libhandle <> 0 THEN
Begin
// Find DescribePlugin.
Describeproc: = getProcaddress (librandle,
CPLUGIN_DESCRIBE);
IF assigned (describeproc) THEN
Begin
// Call DescribePlugin.
DescribeProc (Description);
Memplugins.Lines.Add (Description);
File: // Find Initplugin.
INitproc: = getProcaddress (libhandle, cplugin_init);
IF assigned (initproc) THEN
Begin
File: // Call InitPlugin.
INTPROC (Mnumain);
END;
/ / For third parties
Plugin Find Initplugineventsinitevents: = getProcaddress (librandle,
CPLUGIN_INITEVENTS;
IF assigned (inTevents) THEN
Begin
// Call InitPlugin.
INITEVENTS (LSTMINMAX);
END;
end
Else
Begin
Messagedlg ('file " sr.name
'"is not a valid plugin.',
Mtinformation, [Mbok], 0);
END;
end
Else
Begin
Messagedlg ('An Error Occurred Loading The Plugin "
Sr.name '"", Mtinformation, [Mbok], 0);
END;
END;
Figure 10: Final version of LoadPlugin
The final step is to create
The plugin itself. As in the previous example,
Plugin display a marking itself
process. It also has an initialization routine, which only accepts a TLIST as a parameter in this example. Finally, it still
Contains a process without lead (Export), called Altermintracksize, which passes the modification to it
Numerical value. Figure 11 shows the final
The full code of the plugin.
Unit main;
Interface
Uses Dialogs, Menus, Classes;
Procedure DescribePlugin (Var Desc: String);
Export; stdcall;
Procedure Initpluginevents (LSTRESIZE: TLIST);
Export; stdcall;
Procedure Altermintracksize (VAR X, Y: Integer; stdcall;
IMPLEMENTATION
Procedure DescribePlugin (Var Desc: String);
Begin
DESC: = 'Test Plugin 3 - MinMax';
END;
Procedure Initpluginevents (LSTRESIZE: TLIST);
Begin
Lstresize.add (@altermintracksize);
END;
Procedure Altermintracksize (VAR X, Y: Integer);
Begin
x: = 270;
Y: = 220;
END;
End.
Figure 11: Final
Plugin code
The InitPluginevents process is this
The initialization routine of the plugin. It accepts a TLIST as a parameter. This
A TLIST is a list of saved corresponding function addresses created in the parent application. The following statement:
Lstresize.add (@altermintracksize);
Add the address of the ALTERMINTRACKSIZE function to this list. It is declared as type stdcall
It is matched with other processes, but it is not necessary to use the export indicator. Since the function is called directly through its address,
Therefore, it is not necessary to take it from the DLL in the usual way.
Therefore, the event sequence is listed below:
1. Create a TLIST object when the application is initialized.
2, this list is passed when starting
Initialization process in the plugin Initpluginevents.
3,
The plugin process adds the address of a process to the list.
4. Windows message generated by each window size is changed by our application
Sequence capture.
5. This message is processed by our message processor tfrmmain.mainMaxInfo, see Figure 10.6, message processor traverses list and calls the function it contains, using the current X and Y minimum window sizes as
Parameter passing. Note that the TLIST class is just a storage pointer, so what if you want to save the address?
If we have to convert pointers into the type of desired - in this example, it is converted to TRESizeProc.
TresizeProc = Procedure (VAR X, Y: Integer); stdcall;
7.
Plug-in process ALTERMINTRACKSIZE (pointed to the pointer in the list), accepting X and Y values as variable
VAR parameters and modify them.
8. Control returns the message processor to the parent application, follow the new value of the minimum window size to continue running.
9. TLIST will be released at the finalization section of the primary code when the application exits.
in conclusion
When using this architecture, it is a good idea that Package features provided by Delphi can be used. In communication
Under normal circumstances, I am not a fanatic enthusiast for splitting the runtime module, but when you think any one contains a lot of generations.
When the Delphi DLL of the code is more than 200KB, it has become meaningful.
This article should still have some use, at least it allows you to think about some programming problems,
How to make it more flexible. I know if I use this technology in the previous application.
If I can save a lot of work in modification. I don't want to put
Plug-in as a general solution
Decision. Obviously, in some cases, additional complexity cannot verify its correctness, or the application is rooted
It is not intended to combine itself into several scalable units. There are also some other methods that can also achieve the same effect.
Delphi itself provides an interface to create a module that can be integrated into the IDE, compared to the technology I have explained.
Method is more objects (or more "clean"), and I am also convinced that you can imitate this skill in your own application.
Surgery. Loading a Delphi package at runtime is not done. Explore this possibility.
[The technique described in this article is very good in Delphi 4. In fact, Delphi 4 adds engineering options to make
This type of application enhances the development of the DLL (Application-Plus-DLL) to become easier. ]