Official PlugIn Development Document from NeoSoft *
Creating NeoBook for Windows Plug-Ins

This document is intended for use by experienced programmers.
Experience with Delphi, Visual Basic, C++ or similar 32-bit Windows
programming environment is recommended.

Download the
Sample PlugIn
Project


nbwplugin.zip

What's a Plug-In?

Plug-Ins provide programmers and developers with a fast and easy way to add powerful custom functions to NeoBook 4.0. These added capabilities may provide something as simple as the ability to select an item from a list or as complex as retrieving data from a database server.

Once installed, Plug-Ins are fast and virtually transparent to NeoBook authors and end users. NeoBook's compiler is even smart enough to automatically package Plug-Ins and publications into a single executable application (EXE). No extra steps are required to transport Plug-Ins along with compiled publications. Since Plug-Ins are easily installed and managed by NeoBook, developers can deploy custom Plug-Ins to customers with minimal technical support.

NeoBook Plug-In's are specially designed Windows Dynamic Link Libraries or DLLs. DLLs can be created with Delphi, Visual Basic, C++ or any other similar 32-bit Windows programming environments. What separates a NeoBook Plug-In from a normal Windows DLL is its ability to communicate directly with NeoBook. In order to accomplish this communication, Plug-Ins require some special interface functions and must be structured in a specific way. From a NeoBook user's point of view, Plug-In actions will look no different than any of NeoBook's other action commands. In fact, Plug-In actions are selected from the same list as NeoBook's internal action commands.

Plug-Ins are not to be confused with the Add-Ons introduced in NeoBook 3.2. NeoBook 4.0 still supports Add-Ons, but the extra benefits of Plug-Ins are clear. Unlike Add-Ons, NeoBook's new Plug-Ins are based on Windows DLL technology. Instead of relying on Windows' slow Dynamic Data Exchange (DDE) functions, Plug-Ins essentially become part of NeoBook. This makes Plug-Ins operate extremely fast. In fact, you probably won't even be able to tell the difference between a properly designed Plug-In and a native NeoBook action in terms of speed and ease of use.

Creating a Sample Plug-In

Our example Plug-In was created in Delphi 5, but the principals and steps are similar regardless of which programming environment you plan to use. The sample Plug-In that we'll create in the steps below won't do anything spectacular, but once you become familiar with the basic Plug-In structure, you can easily modify it to create more useful functions. For now, we'll start with a Plug-In that simply displays a standard Windows message box.

Begin by loading the sample Plug-In project included with this documentation. To open the sample Plug-In, select Open from Delphi's File menu and load TestPlugIn.dpr from the folder where you installed this document.

We'll begin by taking a look at the structure of the sample Plug-In. In it's current state, the sample Plug-In doesn't do anything. It's merely a skeleton, containing only the basic functions necessary to communicate with NeoBook. Be sure not to delete any of the code that's in the file right now. Each of these procedures, functions, variables, etc. are critical to making the Plug-In work.

At the top of the file, you'll see the normal Delphi USES statement along with the following entry:

{$R TESTPLUG.RES}

This loads a Windows resource file that contains a simple icon that NeoBook will use when displaying our Plug-In in the Action Selector. The icon is optional. In spite of the stern warning above, you can remove this line and NeoBook will simply use a default generic icon to represent the Plug-In. Alternatively, you can use a resource editor like Resource Workshop to modify the TESTPLUG.RES file to include an icon of your own design. For now lets just leave the current icon.

The next section of the Plug-In source file falls under the ominous heading of "NeoBook Interface Functions DO NOT MODIFY". Be warned, if you modify anything in this section, your Plug-In probably won't work! This portion of the file will be the same regardless of what type of Plug-In you create. Experienced programmers may find something here to tinker with, but most of us will simply heed the warnings and leave this section alone.

Skipping past the "DO NOT MODIFY" section, you should see another section called "Your Custom Plug-In Functions Go Here". Here's were we'll start customizing the Plug-In to make it actually do something. Immediately after the section heading, type the code below into Delphi's editor:

{************** Your Custom Plug-In Functions Go Here ****************}

FUNCTION DisplayMyMessageBox( aTitle, aMessage : PChar ) : BOOLEAN;
BEGIN
  MessageBox( nbWinHandle, aMessage, aTitle, MB_OK + MB_ICONINFORMATION );
  Result := TRUE;
END;

We don't need to worry too much about what this function does right now. It simply displays a Windows message box with a custom title, message and a generic icon. nbWinHandle is the handle of NeoBook's main window which it will send to our Plug-In at startup.

Next, we need to create a simple form that NeoBook authors can fill out when they select our Plug-In's action. The form isn't necessary, but it makes it easy for authors who might be unfamiliar with our Plug-In to enter our action's parameters. Our Plug-In only has one action and it's a simple one, so our form won't be very complicated. Follow the steps below to create the form:

1. Select New Form from Delphi's File menu.

2. Change the following form properties to:
 
BorderStyle: bsDialog
Caption: Define PMsgBox Action
Name: MBoxForm
Position: poScreenCenter

2. Add two TLabel objects and two TEdit objects to the form.

3. Change the first label's caption to "&Title:" and the second label's caption to "&Message:"

4. Add OK and Cancel buttons to the form.

5. Resize the form and position the objects so that they resemble the screen below:

6. Select Save from Delphi's File menu and save the form as "MBox.pas".

Now we need to create a function that will display our form. Go back to Delphi's editor and the section in the TestPlugIn.dpr file called "Your Custom Plug-In Functions Go Here". Below the DisplayMyMessageBox function that you entered earlier, type the following code:

FUNCTION EditMyMessageBox( VAR aTitle, aMessage : PChar ) : BOOLEAN;
BEGIN
  Result := FALSE;
  MBoxForm := TMBoxForm.Create( Application );
  WITH MBoxForm DO
    TRY
      Edit1.Text := aTitle;
      Edit2.Text := aMessage;
      IF ShowModal = mrOK THEN
        BEGIN
          SetStr( aTitle, Edit1.Text );
          SetStr( aMessage, Edit2.Text );
          Result := TRUE;
        END;
    FINALLY
      Free;
    END;
END;
 

This function displays our form and handles any needed user interaction. SetStr is a special procedure from the "DO NOT MODIFY" section that we use to save the text entered into our form. Since Plug-Ins are DLLs, we need to store text as a PCHAR rather than a STRING. This makes it possible to easily transfer data to and from NeoBook. Since PCHARs are pointers, they can sometimes be a little difficult to work with. However, if you use the SetStr procedure whenever you modify a PCHAR you shouldn't have any trouble.

The next section of the TestPlugIn.dpr file contains some pre-built functions that "Must be Customized by You". The first of these is the nbEditAction. In the body of this action you'll see an empty CASE statement. This function will be called by NeoBook when a NeoBook author selects our Plug-In's action. We need to respond by displaying our MBox form. To do this modify the CASE statement to look like this:

CASE IDNum OF
  1 : Result := EditMyMessageBox( Params[0], Params[1] );
END;

Our Plug-In only has one action, but if it had more the CASE statement could be expanded with more choices. IDNum is the number we're going to assign to our action. Future actions would be assigned 2, 3, 4 and so on. Result returns TRUE or FALSE to let NeoBook know if the user clicked our form's OK or Cancel buttons. The two items (Params[0] and Params[1]) passed to the EditMyMessageBox function represent the parameters that belong to our Plug-Ins action. Never try to access more parameters than your action has or Windows will respond with an access violation. We'll tell NeoBook about our action's IDNum and parameters later.

Next, we need to modify the TestPlugIn.dpr file's nbExecAction function. Like the nbEditAction function above, the nbExecAction contains an empty CASE statement. This function will be called by NeoBook when our Plug-In action is executed in a running publication. We need to respond by doing whatever it is our action is supposed to do. In our case, we're going to display a standard Windows message box with our custom title and message. Modify the CASE statement to look like this:

CASE IDNum OF
  1 : Result := DisplayMyMessageBox( Params[0], Params[1] );
END;

Like before, IDNum represents the number assigned to our action. Params[0] and Params[1] are the title and message parameters that belong to our action. Result returns TRUE or FALSE to let NeoBook know if the action was executed successfully. (NeoBook doesn't really care one way or the other and ignores Result.)

The next function in the TestPlugIn.dpr file is called nbMessage. This is an advanced function that most Plug-Ins won't use. For this example we're going to ignore it, but if you someday create a Plug-In that needs to take action when NeoBook enters or exits Run mode, moves its window, or changes pages you'll want to examine this further.

The next function is called nbInitPlugIn. NeoBook calls this function when it wants to know the Plug-In's Title, the Publisher (you, your company, etc.) and a short Description of what the Plug-In does. (Notice the use of SetStr.) We don't need to edit this now, but for future Plug-Ins you can edit the portions in bold. Otherwise, leave the rest of this function intact.

PROCEDURE nbInitPlugIn( WinHandle : HWND; VAR PlugInTitle, PlugInPublisher, PlugInHint : PChar ); FAR;
BEGIN
  nbWinHandle := WinHandle;
  
  { Title of this Plug-In (appears as heading in NeoBook's action list) }
  SetStr( PlugInTitle, 'Sample Plug-In' );

  { Publisher of this Plug-In }
  SetStr( PlugInPublisher, 'John Doe' );

  { Description of this Plug-In }
  SetStr( PlugInHint, 'Use this plug-in to display a Windows message box' );
END;
 

The final function in the TestPlugIn.dpr file that we need to modify is nbRegisterPlugIn. Don't remove or edit the first few lines of this function. Skip down to just below the following line:

{ ****** Enter your Plug-In's actions below ******* }

In order for NeoBook to work with our Plug-In, we need to register each of our actions by providing the following information:

  • An ID number. This number needs to be unique within our Plug-In. However, other Plug-Ins can use the same ID numbers without causing any conflicts. NeoBook will pass this number to the nbEditAction and nbExecAction functions to identify which of your actions needs to be processed.
  • A unique name. Unlike the ID number, the name cannot be used by any other actions (either NeoBook's built-in actions or other actions in other Plug-Ins). For this reason, it's recommended that you prefix each of your actions with something unique that identifies your Plug-In. For example, if you created a Plug-In called PowerPlug, you might prefix each of your action names with "pp". This should help limit name conflicts between Plug-Ins.
  • A description of the action's purpose. Simple text string of 255 characters or less briefly describing what the action is supposed to do. (Save the detail for the manual or help file. There isn't much room where this will be displayed.)
  • An array describing each parameter required by the action. The following parameter types may be used:
  • ACTIONPARAM_ALPHA: Parameter is a string. May contain alpha characters, numbers, punctuation, etc.
  • ACTIONPARAM_ALPHASP: Parameter is a string that can be spell checked.
  • ACTIONPARAM_NUMERIC: Parameter is a number.
  • ACTIONPARAM_MIXED: Parameter may be either numeric or alpha. May contain mathematical expression.
  • ACTIONPARAM_FILENAME: Parameter is a file name.
  • ACTIONPARAM_VARIABLE: Parameter is a variable name.
  • ACTIONPARAM_NONE: Use if action contains no parameters.
  • Finally, tell NeoBook how many parameters your action will be expecting. This should be equal to the number of items in the parameter array above or zero if ACTIONPARAM_NONE is used.
This is actually much simpler that it looks. To register an action with NeoBook, we use a special procedure called nbAddAction. The procedure simply takes the information we provide and tells NeoBook to make the action available for use. For example, if NeoBook's FileRead action were part of a Plug-In, it would be registered like this:

nbAddAction( 1, 'FileRead', 'Read something from a file.',
  [ACTIONPARAM_ALPHA,ACTIONPARAM_NUMERIC,ACTIONPARAM_VARIABLE], 3 );

An example of an action that doesn't have any parameters like NeoBook's GotoNextPage action would look like this:

nbAddAction( 2, 'GotoNextPage', 'Go to the next page.', [ACTIONPARAM_NONE], 0 );

Our sample Plug-In only has one action which we'll call "pMessageBox". The "p" in front will differentiate our action from NeoBook's normal MessageBox action. Of course, if you don't care for "pMessageBox", this is where you can change the name to something else. Now, just beneath the "Enter your Plug-In's actions below" line, type the following code:

nbAddAction( 1, 'pMessageBox', 'Displays a Windows-style message box.',
  [ACTIONPARAM_ALPHA,ACTIONPARAM_ALPHA], 2 );

We're just about finished. All that's left is to compile the Plug-In and install it into NeoBook. To compile the TestPlugIn.dpr file, select Compile from Delphi's Project menu. If you encounter an error message, go back through the steps above and see where you made a mistake. If the compilation is successful, Delphi will create a file called TestPlugIn.dll.

Installing the Plug-In in NeoBook

If you've completed the steps above you should now have a file called TestPlugIn.dll. To install the Plug-In into NeoBook, follow the steps below:

1 Use the Windows Explorer to rename the TestPlugIn.dll file to TestPlugIn.nbp

2. Copy the TestPlugIn.nbp file into your NeoBook PlugIns folder. Normally this folder is located in: c:\Program Files\NeoBook for Windows\PlugIns. If you installed NeoBook in a different folder then copy the file there instead.

3. Start NeoBook 4.0.

4. Select Install Plug-Ins from NeoBook's Options menu.

5. From the Plug-In Options screen, click the Install icon.

6. A file selector will appear. Locate and select the TestPlugIn.nbp file. Click OK to close the file selector and return to the PlugIn Options screen.

7. Our Sample Plug-In should appear in the list of Installed Plug-Ins. If you receive an error message, go back to Delphi and look for mistakes in the code. Most likely, one of the programming steps was omitted.

8. Click OK to close the Plug-In Options screen.

If the installation went smoothly, you should see the Sample Plug-In listed along with NeoBook's other actions like this:

Advanced Plug-In Options

You've mastered the basic concepts involved in creating Plug-Ins, but there are a couple of important procedures that we haven't covered yet. The nbSetVar and nbGetVar procedures provide Plug-In developers with access to NeoBook's variables.

The Plug-In nbSetVar procedure works exactly like NeoBook's SetVar action. In NeoBook you can use SetVar to store information in a portion of memory called a variable. The nbSetVar procedure does the same thing, except it works from a Plug-In. Suppose we wanted to enhance our sample Plug-In by writing something to a NeoBook variable called [MyVar]. Just insert the following code into your Plug-In's DisplayMyMessageBox or nbExecAction functions:

nbSetVar( '[MyVar]', 'Plug-Ins are cool' );

Similarly, the nbGetVar procedure can be used to obtain the contents of a variable. For example:

VAR Data : PCHAR;
BEGIN
  Data := NIL;
  nbGetVar( '[MyVar]', Data );
  ShowMessage( StrPas( Data ) );
  FreeStr( Data );
END;
 

There are a couple of important things about using nbGetVar that you'll need to remember. First of all, notice that Data is declared as a PCHAR and initialized to NIL. This is very important. Also, since Data is a PCHAR, we need delete it when we're finished. The special FreeStr procedure is included at the top of the TestPlugIn.dpr file for just this purpose.
 
 


Download the
Sample PlugIn
Project


nbwplugin.zip

 


©2001 NeoSoft Corp. All Rights Reserved.
NeoBook is a Registered Trademark of NeoSoft Corp.
Delphi and Resource Workshop are Trademarks of Inprise/Borland Corporation.

* This document is used with the permission of NeoSoft Corporation
Copyright 1999 NeoSoft Corporation

[ Return to NeoDezign Tips & Trix ]