Function Definition
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.
Contents
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.