4DPluginDevGuide1 PDF
4DPluginDevGuide1 PDF
4DPluginDevGuide1 PDF
Abstract
------------------------------------------------------------------------------------------------------------------------------------------------------------------
This 2-part series describes how to create 4D 2004/v11 SQL Plug-in functions that are cross platform, Unicode aware and Universal Binary. Some basic information on how to use the Xcode and Visual Studio IDE (Integrated Development Environment) are also included. Advanced topics such as the use of plug-in areas, event handling and printing are NOT discussed. The main objective is to present the reader with guidance on how to write simple plug-ins that can successfully handle text, picture and numeric data on each of the platforms supported by 4D. Part 1 of this series herein covers many of these issues in theory. Part 2 will present some specific hands-on examples.
Introduction
------------------------------------------------------------------------------------------------------------------------------------------------------------------
No doubt one of great features of 4D is its ability to accommodate external routines as plug-ins. In some cases a particular task may seem quite daunting or outright impossible with the standard 4D language, but fairly achievable using an open source library or a simple system call. Such will be a good reason to develop your own plug-in command. Once compiled plug-in commands can be used in the 4D environment, making them much easier (in general) to manage than just a raw binary. One could say that over dependency on plug-ins might compromise the benefits of the cross platform integrated development environment that is 4D, or even jeopardize rapid application development and overall sustainability. However, the vast majority of 4D developers will agree that plug-ins are valuable assets that should definitely be appreciated. Although the art of plug-in development is nothing new in 4D v11 SQL, there are substantial changes to the API (application programming interface) that the developer should be aware of. Unicode, endianness and native picture handling are arguably among the most important, although other issues such as Altura (the library used to port Mac OS code to Windows) dependency and the sunset of QuickDraw are also factors that deeply affect plug-in development for 4D v11 SQL. The main objective of this document is to provide practical information on how to write simple plug-ins that can successfully handle text, picture and numeric data. More specifically, the subject matter will mainly focus on Unicode compatibility and platform/version differences. Sample code written for both platforms is provided.
A good understanding of the C language will certainly help. C is a programming language that can directly interfere with the system - memory management in particular and readers are recommended to have standard levels of C programming skills before testing any of the provided information in real life projects.
Project Number: the name and number combined will specify a plugin.
Adding a Command
Select the theme in the main panels hierarchical list and click Add Command to define a command name. You should respect the naming conventions as outlined in the documentation (Language Definition chapter, Identifiers page). In principle you should start the name with an alphabetic character (although you are entitled to begin with an underscore), followed by alphabetic characters, numeric characters, the space character or an underscore. You should not name a command after an SQL keyword for future compatibility. If the same name is used by another 4D object, the plug-in command will have priority over constants and variables but not over methods, commands or fields. Of course, the best practice is to avoid such naming conflicts in the first place, for example by prefixing. Remember that the wizard will eventually export C subroutines with the same name (space characters will be substituted by underscores). Command names are case-insensitive in 4D but not in C.
Defining Parameters
You can exchange 4D variables of any type except for Boolean with a plug-in. Click Add to define a plug-in parameter. Click on the LONGINT icon beside the parameter name (Param1 by default) to switch its type. The arrow indicates whether the parameter is of type in, out or both seen from the plug-ins perspective. Click on it to change directions. The plug-in can return 4D variables of any type except for Boolean. Click on the icon next to Return value: if you want the plug-in to return a value.
Command Name: typically you would give all commands the same prefix. commands of a plugin.
Parameters: you should refrain from using names that are illegal C tokens.
Close: You must close the dialog to save the changes to a project.
When you reopen the wizard you will see that the first row is highlighted, but that may not mean that it is selected yet. Click on it explicitly, then the Edit button to work on an existing project.
The necessary software for compiling C/C++/Objective-C source codes and building a 4D plug-in for the Mac OS X platform are included in the Xcode IDE, which can be downloaded for free from the Apple Developer Connection web site or installed with the Developer Tools DVD. Double click on the ".xcode" file produced by the Plug-in Wizard (2004). You will most probably be prompted to upgrade the project, since the latest version of Xcode uses a higher format with the file extension "xcodeproj".
Follow the instructions and upgrade the project file. By this point you should have created a test structure with 4D (2004 and/or v11 SQL).
Alternatively you can select Standard (32-bit Universal Binary) for the same effect.
Deployment Target: the plugin will not be loaded for earlier OS editions.
10
Build Configuration: duplicate and modify one for each minor variant.
11
Drag and drop the "PkgInfo" file to the "Sources" folder in the "Groups and Files" pane of Xcode. You will be asked whether to copy or move, so select copy. The file will be added to the project. Dropping to the Resources folder will take the extra step of adding the file to the list of bundle resources, that is, files to be copied to the products Resources folder. What we really want is the "PkgInfo" to be copied to the "Contents" folder, not "Resources", so find "Targets" in the same pane and select "Add/New Build Phase/New Copy Files Build Phase" from the context menu. Double click on the newly created build phase, select "Resources" for Destination and type ".." (2 dots) for Path. This means the files in the build phase will be copied to the directory above the "Resources" folder, which is the "Contents folder" during this build phase. Move the "PkgInfo" file to this newly create folder. When the plug-in is rebuilt, the package will look like an opaque file, not a folder.
12
Copy Files: you can add custom build phases for non-standard procedure.
13
14
Product Name: This property is only present when you open the information palette with the product 4D Plugin selected.
15
"French.iproj", "ja.iproj" or any other valid name according to IANA. Open the "InfoPlist.strings" file with Xcode or any suitable text editor and translate the content. After saving the change, drag and drop the file (NOT the ".lproj" folder itself) to the "Resources" folder in the "Groups and Files" pane. When the plug-in is rebuilt, the package will automatically be given additional ".lproj" folders.
InfoPlist Strings: drag and drop the file, not the container lproj folder.
16
2004 and 4D v11 SQL. By adding this file to the project (as described above) and adding the line shown below to first section of the file "4DPlugin.c", you can receive and return CFStringRef as plug-in parameters.
#include "PA_strings.h"
17
Using the standard functions, we can complete the code so that the passed valued is echoed back as the return value and the second parameter.
void echo_string( PA_Plug-inParameters params ) { long Param1_len; char Param1[32000]; Param1_len = PA_GetTextParameter( params, 1, Param1 ); // --- write the code of echo_string here... PA_SetTextParameter( params, 2, Param1, Param1_len ); PA_ReturnText( params, Param1, Param1_len ); }
Using the routines in PA_strings.c, the same code will look like this:
void echo_string( PA_Plug-inParameters params ) { #if VERSIONWIN long len; _TCHAR* str; _TCHAR* ret; len = PA_GetCharParameter( params, 1, &str ); PA_DuplicateChar( str, &ret, len ); PA_SetCharParameter( params, 2, str, len ); PA_ReturnChar( params, ret, len ); #else CFStringRef cfstr, ret; cfstr = PA_CFStringCreateFromParameter( params, 1 ); ret = CFStringCreateCopy( kCFAllocatorDefault, cfstr ); PA_CFStringSetAndReleaseParameter( params, 2, cfstr ); PA_CFStringReturnAndRelease( params, ret ); #endif }
Notice we receive the text parameter as CFStringRef. This way the source code can be ported directly to the version 11 framework with no modification and is Unicode compatible by nature. It is also much more convenient to have the string in this form since practically every string function in Core Foundation deals with CFStringRef.
18
intend to test the same plug-in with both 4D 2004 and v11 SQL, change the "Active Build Configuration" and build again.
Break point: place one on a line that is not blank or just comments.
19
20
21
To delete a file (and/or its reference) from a project, select one in the Groups and Files pane and press delete. In fact, we could have removed the files directly with Xcode alone. Now go back to the 2004 project, locate the rsrc and 4 source files and drag and drop them to the version 11 projects Groups and Files pane. Do not copy the files but rather simply register their aliases. This way we let both projects link to the same source and rsrc files.
22
The project will build just fine, because the source codes use CFStringRef to handle text regardless of the API version. You can see how this is achieved by checking the PA_strings.h file. The flag __FOURDPACKEX__ is set to 1 when the version 11 API is used and 0 when the 2004 API is used. In a version 11 context we simple create a CFStringRef object from the PA_Unistring passed. In a 2004 context we create one by first calling the EX_CHANGE_STRING entry point to convert the content into Unicode.
To link a particular framework, choose Add/Existing Frameworks from the context menu. Navigate to System/Library/Frameworks and select the intended framework. Add the line #include <Framework_name/ Framework_name.h> to the source code.
23
You can take advantage of the Objective-C language by linking to the Foundation framework or the Cocoa framework. Note that the source files should be renamed with the .m extension for mixed C. Keep in mind that some frameworks are limited to particular versions of Mac OS X and dependency on such might require specific version checking on your side. You can refer to external resources by calling its bundle identifier. Likewise you can refer to the plug-in itself by calling its own identifier (assuming its uniqueness). Objective-C example:
[ NSBundle bundleWithIdentifier: ( NSString* )kPluginSignature ]
C example:
CFBundleRef bundle = CFBundleGetBundleWithIdentifier( kPluginSignature );
Note that NSBundle and CFBundleRef are NOT cross-exchangeable objects. External resources like images can be included in the plug-in bundle by simply dropping them in the Resources folder of the Groups and Files pane of Xcode. All files in this folder are automatically copied in the target bundles resources folder. Such resources can be pulled out with the standard Core Foundation functions. Objective-C example:
[ [ NSBundle bundleWithIdentifier: ( NSString* )kPluginSignature ] pathForResource: @"sample" ofType: @"tiff" ] ] ]
C example:
CFURLRef url = CFBundleCopyResourceURL( CFSTR(sample, CFSTR(tiff), CFSTR(sub_directory_or_null) );
You can check the version of the running Mac OS X by calling the Gestalt function, the same way as you would with the 4D language. Examples of detecting Rosetta mode, the architecture (processor) and OS X version are provided in the mac info folder. All the files used in this tutorial are supplied in the string parameters folder.
The minimum requirement for compiling C/C++ source code in order to create a 4D plug-in for the Windows platform is Microsoft Visual C++ Express Edition. Open the "4D Plugin.vcproj" produced by the Plug-in Wizard by dropping it on the desktop icon of Visual C++. You will most probably be told that the project needs to be upgraded since the Plug-in Wizard (2004) exports the project in an old format.
24
Follow the instructions and upgrade the project file. Make sure you have created a test structure with 4D (2004 and/or v11 SQL). Find the 4D Plugin.bundle folder inside the build directory of the project folder and copy it inside the test structures Plugins folder. You may rename the plug-in if you wish.
25
26
Apply: remember to apply the changes or else the macro will not be updated.
Now navigate to the Linker theme and locate General. Find the property Output File. Click on the right end of the value and choose Edit:
Click Macros. Select the preset value up to the point where the bundle name is defined and replace it with the OutDir macro, which inherits the path defined in the previous procedure.
27
Click OK then Apply to confirm the modification. This will allow the executable file to be output directly to the Plugins folder of the test structure.
28
29
Click on the menu of configurations and choose Edit. Create a new configuration derived from the Debug configuration: Debug (v11) for example.
Select "Properties" from the Projects menu and locate "General" under the theme "Configuration Properties" again. Make sure the active configuration is the newly created one. Change the output directory this time to a test structure created with 4D v11 SQL.
30
suffixes specified. In such cases the compiler will read the "Character Set" flag defined and jump to the appropriate implementation. Note that a plug-in project produced by the 2004 wizard has the flag set to "Use Multi-Byte Character Set" and a project created by the version 11 wizard has it set to "Use Unicode Character Set". To override the default behavior, call a function with the W or A suffix specified.
Note that the file also contains routines that specifically handle multi-byte (ANSI) or wide (UNICODE) characters. All versions can be used in 4D 2004 or v11 SQL.
#include <Windows.h> #include <Tchar.h> #include <String.h> long PA_GetCharParameter( PA_PluginParameters params, unsigned index, _TCHAR** tchr ); void PA_SetCharParameter( PA_PluginParameters params, unsigned index, _TCHAR* tchr, long len ); void PA_ReturnChar( PA_PluginParameters params, _TCHAR* tchr, long len ); void PA_DuplicateChar( _TCHAR* src, _TCHAR** dst, long len ); long PA_GetCharParameterW( PA_PluginParameters params, unsigned index, LPWSTR* ustr ); void PA_SetCharParameterW( PA_PluginParameters params, unsigned index, LPWSTR ustr, long len ); void PA_ReturnCharW( PA_PluginParameters params, LPWSTR ustr, long len ); void PA_DuplicateCharW( LPWSTR src, LPWSTR* dst, long len ); long PA_GetCharParameterA( PA_PluginParameters params, unsigned index, LPSTR* mstr ); void PA_SetCharParameterA( PA_PluginParameters params, unsigned index, LPSTR mstr, long len ); void PA_ReturnCharA( PA_PluginParameters params, LPSTR mstr, long len ); void PA_DuplicateCharA( LPSTR src, LPSTR* dst, long len );
31
Using the standard functions, we can complete the code so that the passed value is echoed back as the return value and the second parameter.
void echo_string( PA_PluginParameters params ) { long Param1_len; char Param1[32000]; Param1_len = PA_GetTextParameter( params, 1, Param1 ); // --- write the code of echo_string here... PA_SetTextParameter( params, 2, Param1, Param1_len ); PA_ReturnText( params, Param1, Param1_len ); }
32
Using the routines in PA_strings.c, the same code will look like this:
void echo_string( PA_PluginParameters params ) { #if VERSIONWIN long len; _TCHAR* str; _TCHAR* ret; len = PA_GetCharParameter( params, 1, &str ); PA_DuplicateChar( str, &ret, len ); PA_SetCharParameter( params, 2, str, len ); PA_ReturnChar( params, ret, len ); #else CFStringRef cfstr, ret; cfstr = PA_CFStringCreateFromParameter( params, 1 ); ret = CFStringCreateCopy( kCFAllocatorDefault, cfstr ); PA_CFStringSetAndReleaseParameter( params, 2, cfstr ); PA_CFStringReturnAndRelease( params, ret ); #endif }
Notice we receive the text parameter as _TCHAR. This way the source code can be ported directly to the 4D v11 SQL framework with no modification and is Unicode compatible by nature. You can even receive the text as Unicode in 2004 by adding the W suffix and process with a Unicode demanding function, or likewise receive the text as ANSI in version 11 and pass it to a legacy string function by adding the A suffix.
33
Click OK. 4D should launch as usual so open the test structure. Create a method and write a few lines of code that calls the newly created plug-in.
34
simple migration of a 2004 version. One is that it can handle Unicode text directly, even those that exceed 32K in size. To give an example, 4D Internet Commands is not a genuine plug-in and you know that because it cannot handle text larger than 32K in size. Another reason for creating a version 11 plug-in with the 4D v11 SQL API is that it can handle 4D pictures in its native format, including PDF on Mac. With the 2004 API, pictures were passed in the PICT format that required the QuickTime library to process in any meaningful way. Finally, the version 11 API introduces some new entry points such as the ones listed below: PA_ExecuteCommandByID PA_GetCommandID PA_LocaliseStringByID PA_LocaliseString . PA_CreateElementsFromXMLDefinition PA_RunInMainProcess
36
Otherwise the command numbers for each function might not match those defined in the rsrc and cause havoc. Note that the project contains 2 source files, 4DPlugin.c and its header file. Select and delete them.
Now go back to the 2004 project, locate the 4 source files and drag and drop them to the 4D v11 SQL projects Solution Explorer.
37
The project will build just fine, because the source codes are written for Unicode if the related API is that of version 11. You can see that the source has compiled for 4D v11 SQL by checking the PA_strings.h file. The flag __FOURDPACKEX__ is set to 1 because a Unicode specific flag has been set in the included plug-in API.
38
The same __FOURDPACKEX__ flag would be set to 0 in the 2004 project. Since the Character Set is different, the _TCHAR data type will be mapped to LPSTR in 2004 and LPWSTR in version 11.
39
Unlike the Xcode project, which managed the copying of files and creation of a plug-in bundle, the Visual Studio project simply creates a executable file named 4D Plugin.4DX at the designated location. The .RSR file, which contains the plug-in name, ID, themes, commands and parameters list, is not created for each build. It needs to be duplicated manually. Visual C++ requires C source files to be logically formatted. More specifically the declaration of variables should be organized in the leading section, not scattered around or inserted within the execution block (which is acceptable in Xcode.) To load a particular library, write a comment as shown in the example below:
#pragma comment(lib, "imm32.lib")
To load a particular DLL, use the LoadLibrary function passing the DLL name in Unicode (11) or ANSI (2004) as shown in the example below. Note that the GetProcAddress function should always be given the procedure name in ANSI. Dont forget to unload the library after use with the FreeLibrary function.
static HANDLE mlangdll;
40
typedef HRESULT ( APIENTRY *LPCONVERTINETUNICODETOMULTIBYTE )( LPDWORD, DWORD, LPCWSTR, LPINT, LPSTR, LPINT ); LPCONVERTINETUNICODETOMULTIBYTE ConvertINetUnicodeToMultiByte = NULL; mlangdll = LoadLibrary(L"mlang.dll"); if(mlangdll){ ConvertINetUnicodeToMultiByte = (LPCONVERTINETUNICODETOMULTIBYTE)GetProcAddress( mlang, "ConvertINetUnicodeToMultiByte" ); }
It is good practice to monitor the memory usage of a plug-in with the Windows Task Manager. Write a loop method in 4D that calls the plug-in command repeatedly and look out for any traces of a memory leak (unreleased memory allocation). All the files used in this tutorial are supplied in the string parameters folder.
Conclusion
------------------------------------------------------------------------------------------------------------------------------------------------------------------
The developer can create plug-ins using the Plug-in Wizard, Xcode and Visual Studio. By exporting the product to the Plugins folder inside a sample structure, one can even trace and debug the C source code. The 2004 and 11 APIs call different entry points, notably for text and picture handling. It is possible however to manage code that is compatible with both versions of the API with some wrapper functions.
41