Extends the father application
This simple plugin is good, but it can't do what is useful. The second example is correcting this problem. The goal of this plugin is to join a project in the main menu of the parent application. This menu item, when you are clicked, some of the code within the plugin is executed. Figure 6 shows an improvement version of the housing, both plugins have been loaded. 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: Improved version of the outer shell loaded with two plugins
In order to achieve this, we must define the second interface in the plugin DLL. Existing DLL only exports a process, DescribePlugin. The second plugin will declare a process called Initplugin. However, in this process, you can see it 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: = LoadLibrary (Pchar (sr.Name)); if LibHandle <> 0 then begin // Find DescribePlugin DescribeProc:. = GetProcAddress (LibHandle, 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. Initproc (Mnumain); 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 previous time to call the description process, a getProcAddress is called again. This time, we have to find a constant cPlugin_init, define the following: const cplugin_init = 'initplugin'; return value is stored in the TPLuginInit type variable, defined as follows: type tplugininit = procedure (ParentMenu: TmainMenu); stdcall; When executed, the main menu of the parent application is passed as a parameter to it. This process can modify the menu according to your own will. Since all GetProcAddress returns use assigned tests, the new version of the loadPlugin process still loads the first plugin that does not contain the initplugin process.
In this process, the first call looks for the DescribePlugin method. The second time INTPLUGIN will fail to fail. Now the new interface is already defined, you can write code for the new InitPlugin method. As in the same, the implementation code of the new plug-in exists in a separate unit. Figure 8 shows the modified main.pas containing the initplugin method. 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: TMainMenu); export; stdcall; var Holder: THolder; implementation procedure DescribePlugin (var Desc: string); begin Desc: = 'Test plugin 2 - menu test'; end; procedure InitPlugin (parentMenu: TMainMenu); var i: TMenuItem; begin // create a new menu item. i: = newitem ('plugin & test', scnone, false, true, holdingr.clickhandler, 0, 'mnutest'); ParentMenu.items [1] .add (i); end; procedure strand.clickHandler; Begin ShowMessage Clicked! '); End; initialization holding Holder: = strater.create; finalization Holder.Free; end. Figure 8: The code of the second plugin is obvious, the first change in the original plugin is to increase the initplugin process. As in the same, the prototype with the export key is added to the list of the top of the unit, and the process name is also added to the list of Exports subsee of the project source code. This process creates a new menu item using the NewItem function, the return value is the 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 main menu of the test outer shell is the menu item Plug-in, so this statement is in Plugin Add a menu item called PLUG-IN TEST to the menu bar. In order to deal with the response to the new menu item, as its fifth parameter, NewItem can accept a TNOTIFYEVENT type process, which will call when the menu item is clicked. Unfortunately, according to definitions, this type of process is an object method, but there is no object in our plugin. If we want to point to functions with the usual pointer, the obtained will only be the complaint of the Delphi compiler. Therefore, the only solution is to create an object that handles the menu. This is the use of the THOLDER class. It has only one way to be a process called ClickHandler. A global variable called Holder, which is declared in the Tholder type in the VAR segment of Modified main.PAS, and is created in the unit's initialization segment.
Now we have an object, we can take its method (Holder.ClickHandler) as a parameter of the newItem function. In addition to displaying a "Clicked!" Message dialog, ClickHandler does not do it. Perhaps this is not interesting, but it still proves a little: the plug-in DLL successfully modified the main menu of the parent application and expressed its new purpose. And like the first example, whether this plugin is not executed in the application. Since we created an object to handle the menu click, then this object is released when you no longer need this plugin. The modified unit will handle this matter in the Finalization section. The Finalization end corresponds to the initialization paragraph, if there is an initialization section in front, then the Finalization section will be executed when the application terminates. Add the following statement Holder.Free to the Finalization section to ensure that the Holder object will be released correctly. Obviously, although this plugin simply modify the main menu of the enclosure application, it can easily manipulate any other object that is passed to 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 painted in Canvas. Event-driven plugin
To now we have described the techniques we describe a generic extension application. By adding new menus, forms, and dialogs, new features can be achieved without having to make any modifications to the parent. However, there is still a limit: this is just a single-sided mechanism. As seen, the system relies on some of the user to activate the plugin code, such as clicking on the menu or similar action. After the code runs, it is necessary to rely on another user action to stop it, for example, a form that closes the plugin may have opened. One feasible way to overcome this defect is to make the plug-in can respond to the action in the parent application - imitating the event-driven programming model that is well working well in Delphi. In the last example plugin, we will create a mechanism that can respond to events generated in the parent application. Typically, it can be triggered by determining which events are triggered, and a TLIST object is created for each event in the parent application. Each TLIST object is then passed to the initialization process of the plugin, if the plugin wants to perform actions in an event, it adds the function address responsible for the execution to the corresponding TLIST. The parent application cycle these function pointers in the appropriate time, and call each function as sequence. In this way, it is possible to perform motion in the same event in the same event in multiple plugins. Events generated by the application are completely dependent on the program determined. For example, a TCP / IP network application may wish to inform the plug-in data arrival through TclientSocket's OnRead event, and a graphical application may be more interested in changes in the palette. To illustrate the concept of event-driven plug-in answers, we will create a plugin for restricting the smallest size of the main window. 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 is simple: to limit the size of an application window, it is necessary to capture and modify the Windows message WM_GETMAXSINFO. Therefore, to create a plugin that completed this function, we must capture this message and call the plugin routine in this message processor. This is the event we have to create. Next we have to create a TLIST to handle this event. LSTMAX objects will be created in the initialization segment of the main form, then create a message processor to capture Windows message WM_GETMINMAXINFO. The code in Figure 9 shows the message processor. {Capture WM_GETMAXINFO. Call the plugin routine for each message. (msg.lparam); for i: = 0 to lstminmax.count -1 do begin TresizeProc (LSTMINMAX [I]) (M.PTMINTRACKSIZE.X, M.PTMINTRACKSIZE.Y); END; END; Figure 9: WM_GETMINMAXINFO message The LoadPlugin process of the processor housing application must be modified again to call the initialization routine. This new initialization function accepts our TLIST as a parameter in which the function address of the modified message parameter is added. Figure 10 shows the final version of the LoadPlugin process, which can perform the initialization of all several plugins discussed so far.
{Loads the specified plug-in DLL.} Procedure TfrmMain.LoadPlugin (sr: TSearchRec); var Description: string; LibHandle: Integer; DescribeProc: TPluginDescribe; InitProc: TPluginInit; InitEvents: TInitPluginEvents; begin LibHandle: = LoadLibrary (Pchar (sr.Name )); if LibHandle <> 0 then begin // Find DescribePlugin DescribeProc:.. = GetProcAddress (LibHandle, 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 InitProc (mnuMain); end; // Find InitPluginEvents InitEvents for third-party plug-ins: = GetProcaddress (librandle, cplugin_initevent); if Assigned (InitEvents) 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; the last step is to create a plugin itself. As in the previous example, the plugin demonstrates a description process of a marker itself. It also has an initialization routine, which only accepts a TLIST as a parameter in this example. Finally, it also contains a process of export, called Altermintracksize, which passes the value to it.
The complete code of the final 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. The code initpluginevents process of this plugin is the initialization routine for this plugin. It accepts a TLIST as a parameter. This 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 to match other processes, but do not use Export instructions. Since the function is called directly through its address, it is not necessary to take it from the DLL in a usual manner. Therefore, the event sequence is listed below: 1. When the application is initialized, a TLIST object is created. 2, this list is passed to the initialization process in the plugin in Initpluginevents during startup. 3, the plugin process adds the address of a process to the list. 4. Windows messages generated when the window size changes when the window size changes. WM_GETMINMAXINFO is captured by our application. 5. This message is processed by our message processor tfrmmain.mainMaxInfo, see Figure 10.6, message processor traverses a list and calls the function it contains, and transmits the current x and y minimum window size as parameters. Note that the TLIST class is just a storage pointer, so if you want to use the saved address, we must convert the pointer to the type of type - in this example, to convert to TresizeProc. TresizeProc = Procedure (VAR X, Y: Integer); stdcall; 7, plug-in process ALTERMINTRACKSIZE (pointer in the list), accepting the 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