Typical plugin technology introduction

xiaoxiao2021-03-06  105

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. ]

转载请注明原文地址:https://www.9cbs.com/read-124730.html

New Post(0)