Anda di halaman 1dari 41

4D Plug-in Development Guide Part 1

By Keisuke Miyako, 4D Japan Technical Note 09-02

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.

Creating a Plug-in Project


------------------------------------------------------------------------------------------------------------------------------------------------------------------

About the Plug-in Wizard


The Plug-in Wizard is an interpreted 4D application that generates the C source code and project files for Xcode on Mac OS X and Visual Studio on Windows. Although its use is not mandatory, it helps give a good start for plug-in development. Since its source code is accessible, some developers have customized the original to better suite their personal and professional needs.

Launching the Plug-in Wizard (2004)


Download or install the latest version of Plug-in Wizard 2004 and open it with the 4D Runtime Interpreted provided with the source code.

A list of projects will appear; click New:

Naming the Plug-in


Change the project name from new Project. The plug-in will be given the same name. The name will appear in the Explorer of 4D and be used in conjunction with the plug-in ID to distinguish it from others. You might want to modify the ID as well, although you can have multiple plug-ins with the same ID but different names. You should pick a number between 15000 and 32767. Note however that plug-ins have to rely solely on plug-in numbers to indentify and call other plug-ins.

Project Name: the name will appear in the Explorer of 4D.

Project Number: the name and number combined will specify a plugin.

Adding a Command Theme


Click Add Theme and define a command theme. Themes will appear in the method editor as well as the Explorer of 4D to help find a particular command.

Command Theme: you group plugin commands by a theme.

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.

Generating the project files


All five boxes at the bottom right of the main panel can remain checked for now. Click Generate. You will be prompted to designate a destination.

Close: You must close the dialog to save the changes to a project.

Generate: You can generate Mac/Win project files together.

Saving the project


Close the main panel. Remember to save modifications or the project will be lost. Now you should have a record in the project list. You are set to quit the Wizard.

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.

About the generated files


The wizard will generate the basic files necessary for a plug-in to be built. The .xcode package and .vcproj file are project files for Xcode and Visual Studio respectively. The rsrc file is a Resources manager file in which the plug-ins name, number and a list of all its themes, commands, parameters are kept. The info.plist file and .lproj folder are used to create the plug-in as a bundle.

Creating test structures


You would probably want to test run your newly created plug-in during development, possibly after each build. If you have an empty structure file ready, you can designate the product to be created directly inside its Plugins folder and even trace the C source code. 4D v11 SQL plug-ins obviously need to be used with a version 11 structure. 2004 plug-ins on the other hand, should be tested for both 2004 and 11, PPC and Intel for Mac OS X. That means you create 2 versions of test structures for 2004.

About using multiple data files with the Wizard


Like any 4D application, the Plug-in Wizard remembers the last used data file and prefers it (if it still exists) over the one placed next to the structure. Therefore it is important to be aware of which data file you are currently working with, by pressing the option/Alt key when starting the application or by checking the About dialog.

Compiling the source codes with Xcode


------------------------------------------------------------------------------------------------------------------------------------------------------------------

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).

Setting the Base SDK


Click "Info" on the main panel or select "Configure SCM for this project" from the SCM. On the "General" pane you should see the Base SDK, which is usually set to "Current Mac OS". The repository of commands and their behavior differ (quite substantially) depending on which version of SDK you choose to develop with. Normally you will not need to change this setting, even if you intend to develop for Mac OS X older than that of the SDK itself. Only think about changing the SDK if a project that built fine before seems not to work anymore. Note that you will only have older SDKs if you have installed them in the past.

Base SDK: You would normally use the latest SDK.

Setting the Target Architecture


Click "Info" on the main panel or select "Configure SCM for this project" from the SCM menu. Switch over to the "Build" pane. The "Architectures" property should probably be set to "Native Architecture of Build Machine", meaning that if you are developing on an Intel machine, the plug-in will be built for Intel only. This is not the desired setting for a 2004 plug-in, since 4D 2004 is a PPC/Rosetta application. Double click and select "Other" to edit the item. Overwrite "$(NATIVE_ARCH)" with "ppc i386" to make the plug-in become Universal Binary. Alternatively you can select "Standard (32-bit Universal)".

Architecture: You can type ppc i386 for both processors.

Alternatively you can select Standard (32-bit Universal Binary) for the same effect.

Setting the Deployment Target


In the "Build" pane of the project's property palette, find "Deployment Target" under the theme "Deployment". It would be "Compiler Default" unless set otherwise. You may change this to "Mac OS X 10.3", "Mac OS X 10.4" or whatever version you want to define as the minimum requirement for the plug-in to be valid.

Deployment Target: the plugin will not be loaded for earlier OS editions.

About the Development and Deployment build configurations


The Plug-in Wizard has published 3 different build preferences; "Default", "Development" and "Deployment". If you choose "Deployment", the plug-in file will be most compact, but you will not have the ability to debug it with Xcode. "Development", on the other hand, creates a larger plug-in file but you will have the convenience of stepping in to the source code similar to how you would debug in 4D.

Setting the output destination - 1


Switch the configuration from "All Configurations" to "Development". Subsequent modifications will only apply to the "Development" build configuration. Find the property "Per-configuration Build Products Path" under the category "Build Locations" (you can use the search window to narrow down the list of properties). The setting reads "build/Development" on the property list but double clicking on it will reveal that the actual value is: $(BUILD_DIR)/$(CONFIGURATION)/$(EFFECTIVE_PLATFORM_NAME). Changing this to "$(SRCROOT)/<your_test_structure_name_here>/Plugins" will mean that the plug-in will be exported directly into the test structure's Plugins folder ready to be tested with 4D 2004.

10

Build Product Path: $(SRCROOT) represents the project root folder.

Setting the output destination - 2


Take a look at the "Configuration" pane of the project information palette. Each configuration represents a slight variation of the project's build setting. When a new setting is required, you can either modify an existing configuration or duplicate one and work on the copy. For example, if you want to test the 4D 2004 plug-in with 4D v11 SQL as well as with 4D 2004, create a copy of the "Development" build phase and change the Per-configuration Build Products Path to the version 11 test structure's Plugins folder.

Build Configuration: duplicate and modify one for each minor variant.

11

Adding a bundle signature


On an Intel machine, you might be distracted by the fact that the created plug-in appears in Finder as a folder with the extension ".bundle" (note however that the plug-in will be recognized and loaded properly despite this appearance). You can resolve this by adding the bundle signature.

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

PkgInfo: required to make the package appear as a folder.

Copy Files: you can add custom build phases for non-standard procedure.

Setting the Bundle Identifier (UTI)


In the main list of files, find Info.plist. This file contains vital system information about your plug-in. One property you should always edit is the Bundle identifier, which is com.4D.4DPlugin by default. Change it to a UTI (Universal Type Identifier) that is a reverse DNS of your own domain (if you have one) and period delimited classification of the plug-in followed by a more specific description, so that the final string can be regarded as a unique identifier for the plug-in. This way you can use this identifier to specify or locate it under various circumstances.

13

Bundle Identifier: Specify the plugins identity in a class hierarchical manner.

Renaming the plug-in


Your newly created plug-in will always be given the name "4D Plugin " unless defined otherwise. To change the name of the product, open the target's information palette, navigate to the "build" pane and look for the property "Product Name" under the "Packaging" theme (this modification is only valid if done in the target's information pane, not the project's). Change the value "4D Plugin" to another name. This effectively modifies the folder name of the ".bundle" folder (package) as well as the executable file name. Therefore it is essential that we also edit the "Info.plist" where the executable file name is declared.

14

Product Name: This property is only present when you open the information palette with the product 4D Plugin selected.

Executable file: The name should be the same as the product.

Localizing the plug-in


The file "InfoPlist.strings" contains basic information of the plug-in such as version, name and copyright as displayed in the "Get Info" pane of Finder. The information should be given in English. To localize the content for international deployment, find the "English.lproj" folder included in the project, copy and rename it as

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.

Adding source files to the project


The file "4DPlugin.c" created by the Plug-in Wizard contains code that receives and returns values to and from 4D. The idea is that you complete the C/C++ code by inserting lines between them that work on those values. Although in theory you can write the entire code in this single file, it is more practical to outsource codes that are generic or object oriented. To add a new source code to the plug-in project, select "New File..." from the File menu. To add an existing file to the project, drag and drop it to the "Groups and Files" pane of Xcode.

About CFStringRef/NSString* objects


The Plug-in API of 4D 2004 maps 4D string/alpha/text data to the C data type char* (a pointer to a buffer of bytes). However, string data is more conveniently handled if it is passed as CFStringRef (an abstract object that represents Unicode characters) in Core Foundation, the development framework of Mac OS X. NSString* is the Cocoa definition of the same object. The utility code "PA_strings.c" provided with this tutorial is designed to map 4D text with CFStringRef in both 4D

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"

About the Get Rule and Create Rule


The Get and Create Rules are naming conventions implemented throughout the Mac OS X development framework (Cocoa and Carbon). Basically it means that when a function with the verb "Get" is called, you only receive a handle to the object and do not own it. To work on the target entity you need to first "Retain" the object and then "Release" it when done. On the other hand, when you call a function with the verb "Create" or "Copy", you own the object, and are thus responsible for its disposal by a "Release" call (note that the act of releasing differs depending on context). As such, the functions defined in the "PA_string.c" file called "PA_CFStringCreateFromParameter" requires you to "CFRelease" the object or otherwise conclude the procedure by passing the object to "PA_CFStringReturnAndRelease" or "PA_CFStringSetAndReleaseParameter".

About Unicode and the 4D character set


As mentioned above, CFStringRef is an abstract object that represents Unicode text. On the contrary, 4D 2004 is designed to store text in "legacy" encodings that are not Unicode. The exact character set used is dynamically determined at the point of launch depending on the system language, which uses one of the internal conversion tables to and from Unicode in the "4D Extensions/Language Support" folder. Conversion to and from Unicode is necessary, since the operating system, including the graphical interface, basically requires Unicode. Plug-in developers can take advantage of this mechanism by calling the "EX_CHANGE_STRING" entry point. This approach is favorable over standard conversion methods since it dynamically adapts to the current character set used by the 4D application.

Editing the source code


Shown below is the code generated by the Plug-in Wizard (2004).
void echo_string( PA_Plug-inParameters params ) { long Param1_len; char Param1[32000]; long Param2_len; char Param2[32000]; long returnValue_len; char returnValue[32000]; Param1_len = PA_GetTextParameter( params, 1, Param1 ); // --- write the code of echo_string here... PA_SetTextParameter( params, 2, Param2, Param2_len ); PA_ReturnText( params, returnValue, returnValue_len ); }

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.

Building the plug-in


Clicking the build button of Xcode will invoke compilation, the copying of resources and the creation of the plug-in binary. Find "4D Plugin.bundle" (or its new name) on the main list of files and see if its colour has changed from red (meaning that the file is missing) to black (meaning that the file was created). If the file has been created, select "Reveal in Finder" from the context menu (invoked by a right-click or control-click). The product should be inside the Plugins folder of your sample database. It is good practice that you check the file information ("Get Info" or command+I) at this point and confirm that the plug-in is a Universal Binary. If you

18

intend to test the same plug-in with both 4D 2004 and v11 SQL, change the "Active Build Configuration" and build again.

Adding break points to the C source code


You can add break points similar to how you would in 4D by clicking the left column of the source code. Doing so will halt the execution when the specified line of code has been approached during the debugging phase.

Break point: place one on a line that is not blank or just comments.

Testing the plug-in


If you are developing your 4D 2004 plug-in (a PPC/Rosetta application) on a PPC machine, you can trace your C source with Xcode. If you are working on an Intel machine, you could do the same by testing the 4D 2004 plug-in (assuming it is a Universal Binary) with 4D v11 SQL. To do so, find "Executables" in the "Groups and Files" pane and select "Add/New Custom Executable" from the context menu. Type a meaningful name like "4D 2004.7 build 3" or "4D v11 SQL Release 3" instead of "Executable". Click "Choose" and locate the appropriate 4D application. To run and debug, add some break points in the left margin of your C source code (unlike with 4D, the break point must be placed at a line with some executable code), highlight a custom executable and select "Start 4D xxx with Break Points" from the context menu. 4D will launch and run as usual so you will need to manually select the sample structure placed inside the project folder. Write some code to see if all the parameters are passed correctly when the plug-in commands are called.

19

Stepping through: you can trace the C code line by line.

Watching: you can monitor the content of structures and pointers.

20

Finalizing the plug-in


Once satisfied with the debugging phase, you would want to create a distribution version of the plug-in. Switch the build configuration to Deployment. The destination folder is set to Build/Deployment by default. It is always good to be aware of the destination for each phase and avoid confusion. Building the plug-in with this configuration results in a file considerably smaller. For this tutorial plug-in, the Development version was 308KB in size while the Deployment version was only 104KB. Of course, you might as well test the Deployment version just in case.

Using the 4D v11 SQL API


Download or install the latest version of Plug-in Wizard version 11 and open it with 4D. Create and generate files with the same project name, theme, command and parameters. Place the files generated by the wizard in a distinct folder in order not to confuse them with the 2004 files and open the project with Xcode. (Again, you will probably be prompted to upgrade the project to a newer format.)

Managing generic source code for both versions of the API


The file 4DPlugin.c generated by the wizard begins with a switch to each function based on the plug-in command number. This number is recorded in the 4DPlugin.rsrc file. The number for each command can change each time you generate a project, so it is best to keep the rsrc and c files that were generated on the same occasion together. In this tutorial we are going to reuse the same source code for 2004 and 11 projects. You can do this by a) removing the c and rsrc files from a new project and replacing them with a link to the files in an older project, or b) keeping the c and rsrc files but deleting the latter part of the c file after where the switching is performed. Write the functional code in a different file, which both projects can share. For example, to take the second approach, open the project folder with Finder and delete the files 4DPlugin.c and 4DPlugin.rsrc. Both files will appear red in Xcode because the link has been broken.

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.

About Unicode and the character 0


If you test both versions of the tutorial plug-in, you will find out that text returned either as return value or output parameter gets terminated at character 0 with the version 11 plug-in, unlike with the 2004 plug-in which gives back the entire content. This is because the entry points in the 2004 API take both the length and content of a char* buffer, while the 11 API takes PA_Unichar*, a pointer to a nullterminated Unicode buffer. (Incidentally, text passed to the plug-in comes in its entirety with length and content.) One must take this factor into account if a character 0 is intentionally included in an input text parameter and is expected to be passed back from a version 11 plug-in.

Notes for development in Xcode


------------------------------------------------------------------------------------------------------------------------------------------------------------------

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.

Compiling the source code with Visual Studio


------------------------------------------------------------------------------------------------------------------------------------------------------------------

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.

About the Debug and Release build configurations


The Plug-in Wizard has published 2 different build preferences; "Debug" and "Release". If you choose "Release", the created plug-in will be compact, but you will not have the ability to debug it with Visual C++. "Debug", on the other hand, creates a larger plug-in but with the convenience of stepping in to the source code similar to how you would debug 4D code.

Setting the output directory


Select "Properties" from the Projects menu and locate "General" under the theme "Configuration Properties". Find the property "Output Directory" which should be ".\Objs/Debug" by default. Click on right end of the value, choose Browse, locate the sample structures Plugins folder, select the bundle and click Apply. This means the created binary will be placed inside the test structures Plugins folder, ready for testing.

25

Output Directory: can later be referenced as $(OutDir) in macros.

The plugin bundle: is a good location point to register as a macro.

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

Build: the default setting is to export to a folder named build.

OutDir: it is more convenient for debugging to output into a structure.

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

Adding alternative build configurations


You can add build configurations if, for example, you intend to test your 2004 plugin with both 4D 2004 and v11 SQL. Select Configuration Manager from the Build menu.

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.

Adding source files to the project


The file "4DPlugin.c" created by the Plug-in Wizard contains code that receives and returns values to and from 4D. The idea is that you complete the C/C++ code by inserting lines between them that work on those values. Although in theory you can write the entire code in this single file, it is more practical to outsource code that is generic or object oriented. To add new source code to the plug-in project, select "New/File..." from the File menu. To add an existing file to the project, drag and drop it to the "Solution Explorer" pane of Visual C++.

About wide characters and ANSI (multi-byte) characters


When text values are handled as char* (a pointer to a buffer of bytes), the interpretation of its content depends on the locale or regional setting of the running platform. The term ANSI used in Microsoft's documentation implies that a text value has been passed in such context. Wide characters, on the other hand, means that the content is Unicode, more specifically a sequence of 2-byte (hence the notion "wide") code points. When the text is ANSI, the character encoding is one of the code pages defined by Microsoft; 932 for Japanese, 1252 for West European etc. While wide characters all have a fixed length (2 bytes per code point), the length per character of an ANSI text may vary since non-Unicode encodings rely on multicharacter sequences, the art of escaping with extended characters (bytes above the value 0x80) to express non-ASCII characters. For such reason, characters in ANSI encodings (code pages) are often referred to as multi-byte characters.

About Windows string functions


Practically every string function in the Windows environment has its wide and multibyte character implementation. The former is suffixed by a capital W, the latter with a capital A. Typically the developer would simply call a function with no such

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.

About LPSTR, LPWSTR and TCHAR


For code integrity it is better to handle text as LPSTR (multi-byte) or LPWSTR (wide) explicitly instead of just an abstract char*. You can also use _TCHAR, which acts as a placeholder for either a multi-byte or wide character. The exact nature of _TCHAR will be defined at the point of compilation by the preprocessor.

About the PA_strings.c routines - 1


The Plug-in API of 4D 2004 maps 4D string/alpha/text data to the C data type char* (a pointer to a buffer of bytes). However, as argued above, string data is more conveniently handled if it is passed as _TCHAR. The utility code "PA_strings.c" provided with this tutorial is designed to map 4D text with CFStringRef in both 4D 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 _TCHAR as plug-in parameters.
#include "PA_strings.h"

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

About the PA_strings.c routines - 2


The routines call the EX_CHANGE_STRING entry point discussed earlier to convert to and from Unicode regardless of the running version and localization of 4D. However the way text is passed between a plug-in and 4D is fundamentally different with 4D 2004 and v11 SQL and one must pay attention to that fact. In 4D 2004, text is always copied to a buffer created by the plug-in thus making it its responsibility to free that buffer. Likewise passing a buffer of text back to 4D meant that the buffer was duplicated and therefore could be freed after copy. In 4D v11 SQL, Unicode text is managed intelligently by 4D by whats known as reference counts. The plug-in will receive a pointer to the Unicode text, not the actual content. Creation and modification of Unicode must be reported to 4D through the API. The plug-in should not alter that Unicode without 4Ds knowledge. For the sake of simplicity, the routines in PA_strings.c create and pass a pointer to a buffer of text in its 2004 implementation. After calling PA_GetCharParameter in 2004, one must conclude the procedure with a call to PA_ReturnChar or PA_SetCharParameter which internally frees the pointer or otherwise frees the pointer explicitly. The version 11 implementation does not allocate or free memory for the reason described above.

Editing the source code


Shown below is the code generated by the Plug-in Wizard (2004).
void echo_string( PA_PluginParameters params ) { long Param1_len; char Param1[32000]; long Param2_len; char Param2[32000]; long returnValue_len; char returnValue[32000]; Param1_len = PA_GetTextParameter( params, 1, Param1 ); // --- write the code of echo_string here... PA_SetTextParameter( params, 2, Param2, Param2_len ); PA_ReturnText( params, returnValue, returnValue_len ); }

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.

Building the plug-in


Select Build 4D Plugin from the Build menu. If you have directly copied the project file from a Mac machine, you would likely get a flood of errors due to file incompatibility. The fault is with the 4D Plugin API folder. Delete and replace it with one from the Windows version of the Plug-in Wizard and you should be fine.

Adding break points to the C source code


You can add break points similar to how you would in 4D by clicking the left column of the source code. Doing so will halt the execution when the specified line of code has been approached during the debugging phase.

33

Testing the plug-in


Since we have directly exported the binary into our test structure, with the build configuration Debug, we can benefit from the debugging environment of Visual C++. Select Start Debugging form the Debug menu. You will be prompted to locate an executable so choose 4D or its shortcut (4D 2004 or v11 SQL depending on the test structures version at the selected build configurations output directory).

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

Finalizing the plug-in


Once satisfied with the debugging phase, you would want to create a distribution version of the plug-in. Switch the build configuration to Release. The destination folder is set to Build/4D Plugin.bundle/Contents/Windows by default. It is always good to be aware of the destination for each phase and avoid confusion. Building the plug-in with this configuration results in a file considerable smaller. For this tutorial plug-in, the Debug version was 471KB in size while the Release version was only 58KB. Of course, you might want to test the Release version just in case.

About the two callback addresses


As demonstrated through the tutorial, a 2004 plug-in is compatible with the version 11 framework, regardless of the applications Unicode mode. To understand why, it may be worthwhile taking a brief look at the 4D Plug-in API files. By reading the file 4DPluginAPI.c we see that the global variable gCall4D stores the 4D callback address which is ( (PackInitBlock*)parameters )->fCall4Dex for a version 11 plug-in and ( (PackInitBlock*)parameters )->fCall4D for a 2004 plug-in. Therefore we know that there are two distinct lines of communication between a plug-in and 4D. When the old line is used, text is copied in the legacy (ANSI, you could say) encoding with the 32K limitation. When the new line is used, text is passed as a pointer to the Unicode buffer managed by 4D. Such buffers always exist, even if Unicode mode is off, because text is always internally stored in Unicode.

Reasons for using the 4D v11 SQL API


A plug-in produced by the version 11 Wizard is not compatible with 4D 2004, while a plug-in created by the 2004 Wizard is compatible with both versions of 4D. Still, there are advantages of creating a genuine version 11 plug-in as opposed to a 35

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

Issues with migration to the 4D v11 SQL API


Most of the 2004 API remains unchanged, at least in name. However, some functions such as the ones used for backup and log management have been removed (since 4D Backup is no longer provided as a plug-in), meaning that the code segment in question cannot be compiled with the version 11 framework. Another function that might cause disturbance is PA_CreateVariable, which no longer takes a second parameter (used to define string length). One solution would be to create a wrapper function that uses the appropriate syntax depending on the API version. Similar rules apply to PA_Alert, which now takes a second parameter.

Smart transition to the 4D v11 SQL API


The goal would be to create an environment where the same source code can successfully be compiled with both the 2004 and 11 APIs, thus making it possible to deploy for both targets without having to compromise functionality in the 11 version or managing two source codes that are almost but not quite identical. The PA_string.c code is an example of such approach. In essence it provides some wrapper functions that remove the API version difference so that the source code can be written in a generic manner.

Using the 4D v11 SQL API


Download or install the latest version of Plug-in Wizard version 11 and open it with 4D. Create and generate files with the same project name, theme, command and parameters. Place the files generated by the wizard in a distinct folder in order not to confuse them with the 2004 files and open the project with Visual C++. (Again, you will probably be prompted to upgrade the project to a newer format.)

Examples of generic source code for the 4D v11 SQL API


Unlike Xcode, which builds a new bundle from resources, Visual Studio will simply compile the source code and create an executable binary. You can remove the source files and replace them with links to files in another project folder, but for rsrc files, you need to duplicate them for each project at the appropriate location.

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

Notes for development in Visual Studio


------------------------------------------------------------------------------------------------------------------------------------------------------------------

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

Anda mungkin juga menyukai