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.
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
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
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
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.
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 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
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
I also wanted to move my Plugin Option String, Name and Description out of the .string or .rc files. I added their #define's earlier in step one. Now, I need to get the plugin to use these instead.
- 1st - 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)
- 2nd - Modify the FMExternCallProc(FMX_ExternCallPtr pb) function.
case kFMXT_GetString: { IDMA_GetString(gFMX_ExternCallPtr->parm1, gFMX_ExternCallPtr->parm2, gFMX_ExternCallPtr->parm3, reinterpret_cast<FMX_Unichar*>(gFMX_ExternCallPtr->result)); } break;