Tag Archives: FileMaker

Writing FileMaker Plug-ins

I put this on my website a long time ago, maybe around 2002, as an HTML page. This is it moved to my blog.

Contents

Introduction

A FileMaker plug-in allows programmers to create new external functions to perform

calculations or other processing. These plug-ins must be written in C or C++. The following explanation assumes that you are using C++. There is very little difference when using C, though your code will be less easy to read.

The plug-in is a dynamically linked library, which integrates with FileMaker by means of some custom functions and data structures. The FileMaker Developer CD contains source code examples and a template which take care of most of the interaction with FileMaker – leaving you to write the external function code.

I would like to include full source code with this text. Unfortunately, large parts of the source code (in particular, the header files which are necessary to build plug-ins) are copyright protected by FileMaker Inc, who have decided that only people who have bought the Developer Edition should be allowed to write plug-ins. If you do not have access to the FileMaker Developer Edition then I suggest you contact one of the many FileMaker consultancy companies who will have it but will probably not have the C/C++ skills necessary to write Plug-ins.

How FileMaker interacts with the plug-in

FileMaker calls the FMExternCallProc() function (or FMExternCarbonCallProc() on the Mac) in the plug-in, passing it a pointer to a FMExternCallStruct data structure. The FMExternCallProc() function examines this struct to choose between one of the following operations:

  • Init: Allows the plug-in to do any special setup which it requires.
  • Idle: Allows the plug-in to make use of idle time.
  • External: Calculates an external function.
  • Shutdown: Allows the plug-in to do any special shutdown which it requires.
  • DoAppPreferences: Allows the user to set any plug-in options.

The Plug-in template project contains appropriately named functions to deal with each of these operations. You can modify these functions, but in most cases you will only need to change the Do_External() function.

The Do_External function is passed a function ID (see External Function Names), a Handle to a parameter, and a Handle to the result. A switch block in the Do_External function calls the appropriate function with the parameter and result Handles.

Handles

A Handle is a pointer to a pointer. The use of a Handle allows the actual data to move in memory as long as the pointer changes. We can still access the data as long as we do a fresh double dereference.

The following functions should be used to manage Handles:

  • FMX_NewHandle(size): Return Handle to new data allocation.
  • FMX_SetHandleSize(h, size):Set size of allocated data at the Handle.
  • FMX_GetHandleSize(h): Returns size of allocated data at the Handle.
  • FMX_DisposeHandle(h): Frees the data at the Handle.
  • FMX_LockHandle(h): Prevents the allocated data from moving in memory.
  • FMX_UnlockHandle(h): Allows the allocated data to move in memory.
  • FMX_MemoryError(): Checks for errors resulting from the use of these functions.

There are other memory management functions listed in FMExtern.h but their use is not demonstrated in the example source files and they are not documented.

Changing the Plug-in template to C++

I advise that you use C++ rather than C. Even if you do not understand the more advanced features of C++, the simpler improvements will make your code much easier to read.

To do so, you will need to change the template project from C to C++. For instance, with Visual C++ 5 simply change the file extensions of .c file to .cpp, and replace these files in the File tab of the project workspace. At this stage you may wish to rename the FMTemplate files to better reflect the name of your plug-in.

Mac

To use C++, you will need to add the following libraries to your Carbon target:

  • MSL_All_Carbon.Shlib
  • MSL_ShLibRuntime_PPC.lib

And you will need to specify the UseDLLPrefix.h prefix file in the
Language Settings / C/C++ Language part of the target’s settings.

And add these libraries to your PPC target:

  • MSL_All_PPC.Shlib
  • MSL_ShLibRuntime_PPC.lib

And you will need to specify the UseCarbonDLLPrefix.h prefix file in the
Language Settings / C/C++ Language part of the target’s settings.

Version String

In the FMTemplate.c (or, for instance, FMAbstractions.cpp) file you will find need to
change the #define statements which tell the plug-in what text to return from the Version
external function.

For instance, in the FMAbstractions.cpp I have used:

#define kVersionLength    24
#define kVersionString    "Abstractions Plug-in 1.1"

Note that you must count the number of characters manually.

The resource file

The example project contains these files, in the “Resources”
folder::

  • FMResource.h: constants used by the resources and the source code.
  • FMExample.r: A Macintosh resource file.
  • FMExample.rc: A Windows resource file.

The resources file must contain the following text resources:

  • 128: Plug-in Name (Note that the Plug-in Name must not begin with F)
  • 129: Description shown in Application Preferences
  • 130: Empty String
  • 131: Feature String (see below)
  • 145: External Function 0 Name
  • 146: External Function 1 Name
  • 147: etc

These resources are already present in the template project. You simply need to change them from their default entries.

Remember to modify the resource files in a text editor rather than your IDE (such as Visual C++’s resource editor), because the IDE may corrupt the file. FileMaker say this is because the resource file is intended to be “cross-platform”, but it’s not clear what they mean by that because they supply separate resource files for Macintosh and Windows. However, the IDE should compile the resource file without problems.

Feature String

The feature string gives FileMaker information about the plug-in and is made up as follows:

  • <4 char creator>: Registered Creator Code
  • <always 1>
  • <app prefs>: ‘Y’ if there are preferences, ‘n’ if not.
  • <externals>:  ‘Y’ if there are any external function, ‘n’ if not. Usually Y of course.
  • <always Y>
  • <idle> : ‘Y’ if you wish the plug-in to get FMXT_Idle messages, ‘n’ if not.
  • <always n>
  • <Win32s>: ‘Y’ if the plug-in only uses the Win32s API subset, ‘n’ if not.

For instance, the Abstractions Plug-in has “MurF1nYYnnY”:

External Function Names

The resources file should contain the names of your external functions, starting at
resource number 144. FileMaker will display these names to the user.When FileMaker calls
an external function it sends the function’s position in this list, starting at 0 for
resource number 144, then 1 for 145, 2 for 146, etc.

The function names should be prefixed with a Function Name Prefix. This prefix must be
4 or 5 characters and must not begin with FM.

For instance, the first 2 function names in the Abstractions Plug-in are:

144 "Abstr-Version"
145 "Abstr-RemoveMultipleReturns"

Adding an External Function

Add the function name

Add the function name to the resource file. e.g. 145  “Abstr-RemoveMultipleReturns” Add a case statement to the switch block in the Do_External() function so that it catches requests for the new function. e.g

/* Second function (RemoveMultipleReturns) */
case 1:
  RemoveMultipleReturns(parameter, result);
  break;

Add the function body

Enter the body of the function as static and void, in the FMTemplate.c (or, for example, FMAbstractions.cpp) file. e.g.

static void RemoveMultipleReturns(FHandle parameter, FHandle result) {

}

If you enter the body before the Do_External() function then you will not need to add a declaration prototype.

Get parameter size

Use FMX_GetHandleSize(parameter) to get the number of characters in the parameter. If
this equals 0 then there is no need to continue. e.g.

long lInSize = FMX_GetHandleSize(parameter);
if (lInSize > 0) {
  ...
}

Lock the parameter Handle

You should lock the parameter Handle to prevent the data moving during the lifetime of your function. e.g.

FMX_LockHandle(parameter);

Alternatively you could double-dereference this Handle every time you look at any piece of its data, but that would be tedious and inefficient.

Be sure to call FMX_UnlockHandle(parameter) at the end of the function.

Dereference the parameter Handle

You must use the * operator to dereference the Handle to get a pointer to the parameter
data. e.g.

unsigned char* pucIn = *parameter;

Note that I have used a puc prefix to remind myself that I am dealing with a pointer to an unsigned character.

Examine the parameter data

You may examine the data using array notation. e.g.

unsigned char ucTest = pucIn[4];

Or you could use the * operator to dereference:

unsigned char ucTest = *(pucIn + 4);

These two statements are equivalent.

You will probably use a loop to examine each character in turn. Make sure you do not go past the end of the parameter text. Remember that the data is not null-terminated, so many utility functions will not work.

Note that all parameter data is sent in text form, so you may need to convert text to a number. Be very careful when dealing with dates because the user must use the DateToText() function whose behaviour is not constant across different locales and which currently is not year-2000 compliant.

Set the result data

As soon as you know how many characters will be in the result data, you can use FMX_SetHandleSize() to allocate space for the result data.You should then check that the
call succeeded. e.g.

FMX_SetHandleSize(result, lOutLength);
if (FMX_MemoryError() == 0) {
  * pucOut = *result;
  ...

You should not dereference the result Handle to a pointer until just before you use it,
because the data may move between the time you get the pointer and the time you use it.
Alternatively you could use FMX_LockHandle().

You might set the result data as you examine the parameter data. e.g.

pucOut[x] = pucIn[y];

However you are more likely to copy the data into the result from an intermediate location. e.g.

lstrcpy((LPSTR)*result, (const char*)pucTemp);

You should have an appropriate #ifdef block for Mac code because the Mac deals with strings differently.

Be sure to delete (or free) any data which you have previously allocated dynamically using new (or malloc), to prevent memory leaks.

Accepting multiple parameters

Unfortunately a FileMaker external function can only be sent one text parameter. In order to receive more than one piece of data you must demand that the user separates the data with a special character.

It is becoming commonplace to use the ‘|’ (pipe) character as the dividing character with external functions. For instance, with the Abstractions Plug-in, a user would enter:

External(Abstr-KeyTriggeredBy, KeyField & "|" & TriggerField).

Of course this significantly complicates your external function because you must separate the parameters within your code.

Registering your plug-in

You must register your 4 character Creator Name with Apple at http://developer.apple.com/dev/cftype/ even if you are not creating a Mac plug-in.

You must then register your plug-in details at www.filemaker.com. They will then send you a registration code. You must email FileMaker Inc a copy of your plug-in, quoting the registration code.

Debugging with Visual C++

To step through your code with the Visual C++ debugger you must build your plugin, place a copy in the FileMaker System folder, then edit the Debug tab of the Project Settings. Choose ‘Additional DLLs’ from the Category drop-down, click on a new line under Local Name, then click the ‘…’ button to browse for the plugin in the FileMaker system folder.

Of course, be sure to choose Debug in Choose Active Configuration under the Build menu.

Developing on MacOS

CodeWarrior only

Although Apple provides development tools with MacOS X, including Project Builder, these can only be used to build Mach-O format executables, which run only under MacOS X. But at the moment FileMaker is not fully MacOS X native so it’s in the old PEF format (also known as CFM) and requires PEF format plug-ins. It seems that only CodeWarrior can build PEF executables on MacOS X, so you must buy it to develop plug-ins for FileMaker on MacOS X. In the future, FileMaker might change to Mach-O format.

CodeWarrior is also the best choice for C/C++ development on previous versions of MacOS, and there is a version which runs on both Mac and Windows platforms, making it easier to build cross-platform plug-ins.

MacOS X Target

You’ll need to use the “Carbon” target on Mac OS X, or the FAT target, which includes that target. See the next section, about MacOS Classic.

MacOS Classic Target

For “Classic” MacOS 8 or 9, you need to use the “FAT” target, which includes the PPC target. FAT was used in the past to contain both 68k and PowerPC executables, but in this case both executables seem to be the same apart from some “Version Info” settings in the Linker/PPC PEF part of the target’s settings.

You will need to remove the 68k target from the FAT target, because recent versions of CodeWarrior (for instance, 8 and 9) can not build 68k executables.

Note that FileMaker does not seem to be Carbonised on MacOS Classic, so you will not be able to link to CarbonLib (use InterfaceLib, AppearanceLib, UrlAccessLib, etc instead) or use any of the Carbon-only functions such as CEPreferencesSetAppValue(). Yes, this is very annoying.

Updating the CodeWarrior 6 example to CodeWarrior 8

In Edit| <target> Settings:
  • In Target/Access Paths/System Paths: Add (Compiler)MSL to avoid the “extras.h not found” error.
  • In Target/PPC Target: Change the Type to FMXT.
Re-add the resource file

Select the Project|Add Files menu item, then choose the FMExample.r file. It should now appear in the “Link Order” project tab.

Further Reading

FileMaker Templates

I put this on my website a long time ago, maybe around 1996, as an HTML page. This is it moved to my blog.

Introduction

Development and maintenance of complex FileMaker solutions is far easier when you have built your solutions from templates. Because:

  • You’ll be able to throw together solutions in a quarter of the time it normally takes to do a version 1.
  • You’ll know it is robust because the template is tried and tested.
  • You’ll be able to look at each solution and know that it obeys certain set principles, which saves time when fixing problems or adding new functionality.
  • You’ll be able to create a User Manual and Developer Documentation in a fraction of the time because most of that text is the same for all the solutions you create.
  • You’ll be able to store details such as Company Name and Address in a System Constants file and use them all over the system. Just change these System Constants when you sell the system to someone else.

You can download my own templates here (the high-level password is colonelklink):

These templates are arranged in 3 folders:

  • Basic Templates – Regular.FP3 for a regular form view file, Lines.FP3 for hidden files such as Invoice Lines, and others.
  • Functional Templates – a Contacts tracking system, an Invoicing System
  • Other Templates – including a mail-merging system.

These templates represent a considerable head start when developing FileMaker systems. You are welcome to use them but naturally I accept no liabilities and offer no guarantees. If you use them it would be nice to receive a comment.

The following is both a description of these templates and general advice about FileMaker templates. Be sure to read Murray’s FileMaker Advice to learn about basic techniques which should be used in all FileMaker development.

Layouts

  • Menu
  • Edit Details
  • View Details
  • Find
  • List
  • All Fields

Menu

Each file should have its own menu with buttons for Details, New, Find, List.

Edit Details

The buttons at the side include the standard group (Menu, Details, New, Find, List) and the navigation box with CD-type controls and record numbers etc. Specific buttons for reports etc should be place in the second group. When changed, this set of buttons should be grouped with the Navigation Box (see below) and copy-pasted into all other details screens using the Size window to ensure exact positioning.

Having the buttons on the left allows room in the main part for 2 columns of fields. The standard screen is not wide enough for three columns at a readable point size and spreading 2 columns over the whole width wastes space.

There may be several details screens to show various groups of data. All details screens should have the same bar at the top, showing the record number and one or two other particularly important fields. This helps prevent the user from losing track of what record they are looking at, and emphasises that they are looking at different fields within the same record.

View Details

The View Details layout is created by copying and pasting the form section of the Edit Details layout into the View Details layout. Use the Size window to place it in exactly the same position. This should then be sent to the back of the layout, behind the large rectangle which is defined as a button to prevent access. Create a New Tab Order with no tabs to prevent users from tabbing behind the rectangle.

Find

The Find screen is mostly a duplication of the Details screen with some differences.

The Find layout is created by copying and pasting the form section of the Edit Details layout into the View Details layout. Use the Size window to place it in exactly the same position. Remove buttons, such as the circle button in portals, which do not belong on a find screen.

  • The New button creates a new Find request instead of a new record.
  • The navigation buttons do not perform any processing. (see below).When changes are made to the details layout, the fields should be re-copied to the Find layout. The Find button uses a trapped find script which does not allow the user to ever be left on the Find layout in Browse mode. The Find layout may be in a different colour to emphasise that the user is in Find mode.

 

Once the find is performed the script will go to the Details or List layouts depending on whether more than 1 record was found.

List

All files should have a list layout. Clicking on the button on the left takes the user to the Details for that record.

The [Sort] button allows users to sort by any order. The Column headings could also be defined as Sort buttons but this is difficult to maintain.

All Fields

This layout is for general system maintenance and should be used by scripts whenever it is absolutely necessary to use cut/paste instead of set field().

This layout can be updated by creating a new layout, Copying all fields, deleting the layout, going to All Fields, Deleting All Items, and Pasting the new fields.

The Navigation Box

This set of fields and buttons allows the user to move through the records without having to open the status panel. When in browse mode, the buttons also ensure that the SUB.Process script is called on each record. The panel also includes a [Del] button.

Scripting

SUB.Process

It is sometimes necessary to ensure that data is validated or updated using scripts whenever it is changed. Ensuring that all buttons call this empty script allows the developer to insert such processing in future without having to modify all the button scripts. Therefore, all new Button scripts should be created by duplicating an existing Button script.

N.B. This method will allow the developer to create indexed versions of un-indexable fields for fast finds and extra relationships.

Scripts Menu

The scripts menu should be used as a menu to move between areas in the entire system. This also discourages use of the Window menu.

Special Fields

Sys.Found, Sys.Records

Used by the Navigation Box.

Sys.Creation Date, Sys.Creation Time, Sys Modification Date, etc

Additional Information to help the developer maintain database. Remember, these fields are usually not auto-entered in imported records.

G.Help.Code, G.Help.Applescript

Can be used for context-sensitive help. Specific Help buttons set a Help Code in G.Help.Code and a SUB script then displays the relevant text from the ‘System Help’ file. Because the FileMaker message box is of limited size, the G.Help.Applescript field can be used to generate an Applescript dialog box. The field already contains the necessary Applescript but needs to be altered to refer to your help file via a relationship.

Sys.StatusCurrentError

Used by scripts to trap errors. See the ‘BUTTON.Find’ script for example.

Sys.File Name

see ‘Global Fields’ below.

LINK

This calculation field is always 1. It can be used by a relationship to get at values in the System Constants file.

Global Fields

‘Sys.File Name’ is used on layout headings and in the label for the Primary Key. It should be entered as a singular rather than plural. The ‘s’ has been added to the layout where necessary. Calculated plural and singular fields would not show up in Find mode. Because this field is wiped when a clone is saved, the script ‘SUB.Set Sys.File Name’ is called from the Open Script. This resets the filename if it is empty. Therefore a new filename should be entered in All Fields and in this script.

Global Fields must not be used for button names because these fields will be wiped when a clone of the file is saved.

Portals

The file contains an example of a portal. This same arrangement should be used for all Portals. The portal has a circle button at the left which takes the user to the details for that item. If the portal is being used to add lines then the portal should have a Del button at the right.

Murray’s FileMaker Advice

I put this on my website a long time ago, maybe around 1995, as an HTML page. This is it moved to my blog.

Introduction

FileMaker is a relatively low-powered database program whose primary advantage is its ease of use. Compared to more ‘standard’ client-server database systems, solutions can be created very quickly. And because FileMaker focuses on a higher level of abstraction, these solutions have less unnecessary complexity and obscurity. These are serious advantages, because almost every system will need to be seriously rethought (‘refactored’) at some point in its development.

However, it is not, as claimed, fully relational, and it does not allow code reuse. These disadvantages can lead eventually to overstretched and disorganised systems. If you use FileMaker you should recognise these limitations.

The FileMaker Trap…

People get off the ground quickly with FileMaker. But often with uninformed confidence they add complication, redundancy and duplication until they are no longer able to predict the consequences of any more changes. The company’s administration may become led by the eccentricities of their system. Employees may be so confused by the bizarre sequence of actions which they perform that they no longer have any insight into their role in the business. This is not productive.

Developers do not just need technical skills, they must understand how to deal with complication itself. If you do not recognise and manage complexity it will eventually overwhelm you.

…And How To Avoid It

By using a little discipline from the start you can postpone the point at which the system becomes too complicated to develop further. If you are disciplined enough then you can postpone it indefinitely.

Self -Documentation

It is impossible to completely document a system which is continually being developed. Denying these facts can lead to confusion and cover-ups. The only way to ensure that a system’s design and operation can be understood is to:

  • Explain the general principles of your design, the principles behind its major components and any unusual techniques employed.
  • Use methods such as those below to make the interdependencies in your system obvious.

Scripts

Use prefixes to indicate how the script is used. For instance:

BUTTON.Invoice Details
SUB.Log Print Date

Rearrange the script list so the BUTTONs are grouped together appropriately. Use empty
scripts to divide scripts into groups.

Fields

Indicate the source of a lookup or reference by prefixing the field name with the
source. For instance:

Customers..Name
Customers..Address

If a field is calculated from another field, indicate this in the field name. For instance:

Order Date
Order Date.Year

Strive to identify distinct groupings of fields and identify them by giving all the
fields a prefix. For instance:

Car.Model
Car.Registration

These techniques will group the fields together when using the standard alphabetical view, making the database structure far more obvious.

Remove Garbage

It is essential to remove any unused part of a system. In the ongoing development of a system, parts will inevitably be discarded. It it important to remove these from the system. In general, they will not have any impact on performance, but their presence implies that they are still relevant to the whole. At least mark them as unused. Otherwise they are a smoke-screen to anybody trying to understand the system.

Unused Fields

Do not delete unused fields because that would affect any scripted import orders. Rename them with a z. prefix so that they show up at the bottom of the list and turn off their indexing. When adding fields in future you may simply rename these fields and redefine them.

N.B. Use the Overview in the Password sub-menu to see which layouts a field is on.

Unused Scripts

FileMaker will not warn you when deleting scripts which are used by other scripts. Using the above script name prefixes will help in determining a script’s dependencies. Instead of immediately deleting a script, mark it with a prefix of UNUSED. and insert a Show Message script at the start. After some time, if this script has not displayed its presence, you may delete it.

White Space in Calculations

Use spaces and returns to make your calculations readable. Show nested IFs like so.

IF(test1;
  resultA;
  IF(test2;
   resultB1;
   resultB2
  )
 )

The results should be indented by 1 more space than the IF and the ). Similar methods can be employed for Choose() and Case() statements. This should be second-nature to C or Java programmers.