Have you used Adobe Photoshop? If you have used, you will be more familiar with the concept of plugins. For foreign pedestrians, the plugin is only the code block supplied to the application from the outside (for example, in a DLL). The difference between a plug-in and a normal DLL is that the plug-in has the ability to extend the parent application function. For example, Photoshop itself does not have a large number of image processing functions. The plugin is added to give it a strange effect, such as blur, spots, and all other styles, and any of these features are not the parent application itself. This is very good for the image handler, but why do you want to get a big strength to complete the business application that supports plug-ins? Suppose we give an example, your app will generate some reports. Your customers will certainly ask for updates or add new reports. You can use an external report generator such as Report Smith, which is a non-sampled solution, you need to publish additional files, you want extra training for users, and so on. You can also use QuickReport, but this will make your version of the nightmare - If you change the font, you have to Rebuild your application. However, as long as you do the report to 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 an application that processes data from an external device (such as a barcode scanner), in order to give users more choices, you have to support half-playing equipment. The maximum scalability can be obtained by writing each device interface processing routine into a plug-in without any changes to the parent application.
getting Started
The most important thing before starting writing code is to figure out which functions need to be extended in your application. This is because the plug-in is interacting with the parent application through a specific interface, and this interface will be defined according to your needs. In this article, we will establish three plugins to display several ways to interact with the parent application. We will make the plug-in into a DLL. However, before doing this work, we have to make a shell program to load and test them. Figure 1 shows the test program after loading the first plugin. The first plugin does not have any big demonstration, in fact, it is just returns a string that describes your own. However, it proves that it is very important - no inserting applications can run normally. If there is no plugin, it will not appear in the installed plugin list, but the application can still exercise the function.
Figure 1: Plug-in test shell
The only difference between our plug-in shell and ordinary applications is that the SHAREMEM unit in the Uses clause is in the project source file and the code of the loading plug-in file. Any application that delivers string parameters between itself and sub-DLLs requires a ShareMem unit, which is interface between delphimm.dll (Delphi provides this file). To test this housing, you need to copy the DelphiMmmm.dll file from the Delphi / bin directory to the path included in the PATH environment variable or in the directory where the application is located. The file also needs to be distributed simultaneously when the final version is issued. The plugin is loaded into this test housing through the loadPlugins process, which is called in the FormCreate event of the main window, see Figure 2. This process uses the FINDFIRST and FINDNEXT functions to find the plugin file in the directory where the application is located. After finding a file, use the loadPlugins procedure shown in Figure 3 to load it. {Find widget application directory files} 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 {Loading the specified plugin DLL.} Procedure TFRMMAIN .LoadPlugin (sr: TSearchRec); var Description: string; LibHandle: Integer; DescribeProc: TPluginDescribe; begin LibHandle: = LoadLibrary (Pchar (sr.Name)); if LibHandle <> 0 then begin DescribeProc: = GetProcAddress (LibHandle, 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; Else Messagedlg ('An Erro R occurred loading the plug-in "' sr.name '". ', mTerror, [Mbok], 0);
Figure 3: Loading the plugin loadPlugin method shows the core of the plugin mechanism. First, the plugin is written as a DLL. Second, it is dynamically loaded through the LoadLibrary API. Once the DLL is loaded, we need a way to access the procedures and functions it contain. The API call GetProcAddress provides this mechanism that returns a pointer to the desired routine. In our simple demonstration, the plugin only contains a process called DescribePlugin, specified by constant cPlugin_Describe (the case case case is very important, the name passed to the getProcAddress must be completely consistent with the routine name included in the DLL). If the requested routine is not found in the DLL, getProcAddree will return NIL so that the return value is allowed to measure the return value using the Assigned function. To store pointers to a function in an easy-to-use manner, it is necessary to create a specific type for use. Note that the return value of getProcadDress is stored in a variable, which is the TPLugindescribe type. The following is its statement: Type Tplugindescribe = procedure (var desc: string); stdcall; due to the process exists inside the DLL, it is compiled by the standard call to transform all export routines, so you need to use the stdcall indicator. This process uses a var parameter that it contains a description of the plugin when the process returns. To call the process just got, just use the variable of the save address as the process name, followed by any parameters. As far as our example, Declaring: DescribeProc will call the description process obtained in the plugin and fill the description variable with the string of the plugin function. Texture plugin
We have created a parent application, and now the wheel is now created in a plugin we want to load. The plugin file is a standard Delphi DLL, so we create a new DLL project from the Delphi IDE to save it. Since the exported plugin function will use a string parameter, put the Sharemen unit in the forefront in the Uses clause of the project. Figure 4 is listed in our simple plug-in engineering source file. Uses ShareMem, Sysutils, Classes, Main in 'main.pas'; {$ E PLG.} Exports Describeplugin;
Figure 4: Source file of a simple plugin Although the plugin is a DLL file, it is not necessary 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 file to be loaded, the new extension can be a specific file mask. By using other extensions (our examples use * .plg), you can give a sense to a certain extent that the application will only load the corresponding file. Compilation indicator You can implement this change, or you can set the extension with the Application page of the Project Options dialog. The first example of the plugin is very simple. Figure 5 shows the code contained in a new unit. Note that the DescribePlugin prototype is consistent with the TPLugindescribe type in the housing application, and the process will be exported using additional Export reserves. The derived process name will also appear in the exports segment of the primary engineering source code (listed in Figure 4). unit main; interface procedure DescribePlugin (var Desc: string); the main example widget: export; stdcall; implementation procedure DescribePlugin (var Desc: string); begin Desc: end in FIG. 5; = 'Test plugin v1.00'; end. Before testing this plugin, you must copy it to the path to the primary application. The easiest way is to create a plugin in the subdirectory of the primary directory, and then set the output path to the main path (Directories / Conditionals, the Project Options dialog, 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 the DLL project, you can specify a program as a host application through the Run Paramaters dialog, which is pointing to the path to the application that will call the DLL (in our example, the test shell just created). Then you can set a breakpoint in the DLL code and press F9 to run it - just like it is done in a normal application. Delphi runs the specified host program, and, by compiling the DLL with debug information, point you to the breakpoint within the DLL code.