Function Definition

From FM Plugin Wikipedia
Jump to: navigation, search

By default, the FM 7 API requires you to define each Function Definition in multiple files. This means duplication on Mac & Windows. It is possible to modify the main FMExample_Plugin.cpp file to include the definitions all in the one file, removing the duplication.

NOTE : The disadvantage of this is you are limiting your plugin to a single language (English in my case)


In this example, I have replaced the use of the 'FMExample.strings' file (Mac) and 'FMExamplePlugin.rc' file (Windows) to define each Function.

Step One - Plugin Name

Define the Plugin Option String, Name and Description fields.

#define IDMA_Plugin_Name			"IDMA Plugin"
#define IDMA_Plugin_Description			"This plug-in provides additional functionality to FileMaker."

#define IDMA_Plugin_OptionString		"IDMA1nnYYnn"
//						 ^--^ || | |---- // No Win32s support
//						 |    || |------ // Want idle time 
//						 |    ||-------- // No old external functions
//						 |    |--------- // Has preferences dialog
//						 |-------------- // 'IDMA' plug-in ID

Note : I just copied these from the .strings or .rc file, then appended the #define name to the start instead.


Step Two - Function Definitions

Define each of our plugins details.

For each Function Definition, I have created a set of definitions as below. The 'ID' as far as I can tell can be any number, but once you have set it for a function, you cannot change it or else it will no longer link for any FileMaker database which calls that function. I have used increments of 10 so that I can insert a function at a later date if wanted.


// Function IDs				====WARNING :  ID Values are important.  Do not change once used, or will affect FileMaker linkage !!! ===
#define IDMA_Version_Name		"Version( \"Version\" )"
#define IDMA_Version_ID			201
#define IDMA_Version_Min		0
#define IDMA_Version_Max		1
#define IDMA_Version_Flags		fmx::ExprEnv::kMayEvaluateOnServer | fmx::ExprEnv::kDisplayInAllDialogs

#define IDMA_DoScript_Name		"idma_DoScript( Get(FileName) ; scriptname ; paramater )"
#define IDMA_DoScript_ID		211
#define IDMA_DoScript_Min		2
#define IDMA_DoScript_Max		3
#define IDMA_DoScript_Flags		fmx::ExprEnv::kMayEvaluateOnServer | fmx::ExprEnv::kDisplayInAllDialogs


Repeat the above for each additional Function Definition.


Step Three - New Do_GetString

We need to create a couple of new functions to replicate the 'Do_GetString' function that comes with the API to read our define's instead. These will be used in Step Four for our modified Function Definition.

static fmx::TextAutoPtr _n(char * S)
{ // return the function name only as a FMX::TextAutoPtr.  eg. returns  'Version' from  'Version( \"Version\" )'

	fmx::TextAutoPtr	R;
	R->Assign(S);

	// The string for this whichStringID is a Function Prototype, but all the plug-in needs now is the Function Name by itself.
	fmx::TextAutoPtr		parenToken;		parenToken->Assign("(");

	unsigned long		originalSize = R->GetSize();
	unsigned long		firstParenLocation; 

	firstParenLocation = R->Find(*parenToken, 0);
	R->DeleteText(firstParenLocation, originalSize-firstParenLocation);

	return R;

} // Do_GetString (TextAutoPtr version)

static fmx::TextAutoPtr _p(char * S)
{ // return the function definition as a FMX::TextAutoPtr.  eg. returns  'Version( \"Version\" )'

	fmx::TextAutoPtr	R;
	R->Assign(S);

	return R;

} // Do_GetString (TextAutoPtr version)

Step Four - Modify Do_PluginInit

Modify the Do_Plugin_Inti() function as below

/* ::=- Do_PluginInit =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=:: */
static FMX_Long Do_PluginInit(FMX_Short version) 
{
	// Check the app API version
	if ((version < k70ExtnVersion) || (version > kMaxExtnVersion)) 
	{
		// This version of FileMaker is not supported; let FileMaker disable this 
		// plug-in and report the problem to the user.
		return (kBadExtnVersion);
	}

	// Initialise any variables as necessary.
	
	// Register plug-in functions
	fmx::QuadCharAutoPtr	pluginID('I', 'D', 'M', 'A');
	fmx::errcode			err;

	err = fmx::ExprEnv::RegisterExternalFunction(*pluginID, IDMA_Version_ID, *_n(IDMA_Version_Name), *_p(IDMA_Version_Name), IDMA_Version_Min, IDMA_Version_Max, IDMA_Version_Flags, Do_Version );
	err = fmx::ExprEnv::RegisterExternalFunction(*pluginID, IDMA_DoScript_ID, *_n(IDMA_DoScript_Name), *_p(IDMA_DoScript_Name), IDMA_DoScript_Min, IDMA_DoScript_Max, IDMA_DoScript_Flags, Do_StartScript	 );	
	
	// Return kCurrentExtnVersion to enable the plug-in in FileMaker.
	return (kCurrentExtnVersion);

} // Do_PluginInit

Notice that our actual Function Definition is now a single line per definition.


Step Five - Modify DO_PluginShutdown

Update our Plugin Shutdown routine. Check that we are using the same ID's

/* ::=- Do_PluginShutdown =-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=:: */
static void Do_PluginShutdown(void) 
{
	// Unregister plug-in functions
	fmx::QuadCharAutoPtr	pluginID('I', 'D', 'M', 'A');
	fmx::errcode			err;
	
	err = fmx::ExprEnv::UnRegisterExternalFunction(*pluginID, IDMA_Version_ID );
	err = fmx::ExprEnv::UnRegisterExternalFunction(*pluginID, IDMA_DoScript_ID );
	
	// DeInitialise any variables as necessary
		
} // Do_PluginShutdown


Step Six - Plugin Name

I also wanted to move my Plugin Option String, Name and Description out of the .strings or .rc files. I added their #define's earlier in step one. Now, I need to get the plugin to use these instead.

To do this, we first need to add the following two new functions

void copyCharToUnichar(char * S, FMX_Long maxLength, FMX_Unichar * R)
{  // convert the 'char' string into a 'FMX_Unichar' string.  Assumes NULL byte terminated string passed.
	long i = 0;
	
	while ( (i < (maxLength-2)) && (S[i] != 0) )
	{
		R[i] = (FMX_Unichar)S[i];
		i++;
	}
	R[i] = 0x0000;	// terminate with a NULL
	
}


void IDMA_GetString(unsigned long whichString, FMX_ULong /* winLangID */, FMX_Long resultsize, FMX_Unichar* string)
{  // takes the requested 'whichString' and returns the appropriate response, converted to a UniChar with a NULL termination.
	switch (whichString)
	{
		case kFMXT_OptionsStr:			copyCharToUnichar(IDMA_Plugin_OptionString, resultsize, string);    break;    // the "IDMA1nnYYnn";  string
		case kFMXT_NameStr:			copyCharToUnichar(IDMA_Plugin_Name, resultsize, string);    break;    // the plugin name.  eg.  IDMA Plugin
		case kFMXT_AppConfigStr:		copyCharToUnichar(IDMA_Plugin_Description, resultsize, string);    break;    // the plugin description.  eg.  This plugin...

		default:						copyCharToUnichar("Error : Unknown Request" , resultsize, string);
	}// switch (whichString)

} // IDMA_GetString (FMX_Unichar* version)


Step Seven - Modify FMExternCallProc

Last, we now need to modify the FMExternCallProc(FMX_ExternCallPtr pb) function to call the above functions instead of the original Do_GetString which reads the .strings or .rc file.

		case kFMXT_GetString:
		{
			IDMA_GetString(gFMX_ExternCallPtr->parm1, gFMX_ExternCallPtr->parm2, gFMX_ExternCallPtr->parm3, reinterpret_cast<FMX_Unichar*>(gFMX_ExternCallPtr->result));
		}
		break;


Step Eight - Cleaning up

All going to plan, we should now be able to compile and test.

If all works, then edit your .strings and .rc files, and comment out (or delete if you want) any reference to the above items.

  • .strings file (Mac)
//"IDMAPlugin 1" =   "IDMA1nnYYnn";

//"IDMAPlugin 201" = "idma_Version( \"Version\" )";
//"IDMAPlugin 211" = "idma_DoScript( Get(FileName) ; scriptname ; paramater )";

//"IDMAPlugin 128" = "IDMA Plugin";
//"IDMAPlugin 129" = "This plug-in provides additional functionality to FileMaker.";


  • .rc file (Windows)
//STRINGTABLE 
//BEGIN
	// 1..127
//    1     "IDMA1nnYYnn"
		// ^--^ || | |---- // No Win32s support
		// |    || |------ // Want idle time 
		// |    ||-------- // No old external functions
		// |    |--------- // Has preferences dialog
		// |------------- // 'KMRp' plug-in ID
//END


I suggest you do a 'Clean All Targets' and then Build, just to be sure all is working.