Omnis Technical Note TNXM0003 June 2009

Creating Old-Style Unicode External Components

For Omnis Studio Unicode version
By Gary Ashford

Introduction
This Technote assumes familiarity with the Omnis External Component SDK and example projects- which can be downloaded from this website, and Microsoft Developer Studio/ Visual Studio which is the Windows application used to compile and link Omnis external components

The following discusses the issues involved when building old-style components compatible with the Unicode version of Omnis Studio. For a discussion relating to new-style components, please refer to Technote TNXM0002

Old-Style components are self-contained dynamic libraries which reside in the Omnis\external folder. New-Style components are built against the Omnis component library and reside in the Omnis\xcomp folder.

Unicode or Non-Unicode?
If you are designing a component to be used with both the Unicode and Non-Unicode versions of Studio (Omnis Studio 4.3.1 for example), you should create separate Unicode targets, i.e. Unicode Debug and Unicode Release to complement your existing Debug and Release targets.
In the Unicode targets, the following additional Preprocessor Definitions are required:

isunicode Requirement of the Omnis external component interface
UNICODE Requirement of Visual Studio to enable wide character support
_UNICODE Required by the __T() macro (tchar.h) when creating Unicode literal strings

In this way, you can make use of conditional compilation statements (e.g. #ifdef isunicode) to handle Unicode-specific code whilst retaining the same source files for use by both the Unicode and non-Unicode targets.
If you will not require the non-Unicode targets (for instance if designing components for Studio 5 and later) the Debug and Release targets can subsequently be discarded. For a full list of preprocessor definitions used by Omnis components, please refer to the sample project properties.
It should be noted that since Studio 5 supports Unicode only, Unicode components are required even if the component itself will ultimately only be handling non-Unicode data.

When building Unicode targets you should ensure that the compiler correctly recognises the wchar_t data type. With Visual Studio 2008, the Project Settings->C++->Language>"Treat wchar_t as Built-in Type" should be set to: No (/Zc:wchar_t-).
With Xcode, the following flag should be added to the "Other C++ Flags": -fshort-wchar

Handling Unicode Character data
Omnis Studio uses the Utf32 encoding for all character data exchanged with external components although there are several helper functions and data types which provide compatibility and conversion between other encodings, notably; Utf8 and Utf16. Items common to both old and new-style components are shown highlighted:

qchar datatype
(Defined in omstring.h)
When isunicode is defined, qchar is defined as unsigned long (4 bytes/Utf32). For non-Unicode targets, qchar defaults to unsigned char (1 byte). Most ECO.. and GDI.. component interface methods take qchar arguments as parameters.
qoschar datatype This datatype corresponds to the encoding used by the operating system. qoschar is defined as unsigned short (2 bytes/Utf16) under Windows and MacOSX, but as unsigned char (1 byte/Utf8) under Linux. For non-Unicode targets, qoschar defaults to char. Some external component library classes (notably strxxx) take qoschar arguments in their constructors. See QTEXT()
qbyte datatype qbyte is always defined as unsigned char and is used specifically when handling binary and one-byte-per-character data (i.e. can be used to store Utf8 data if required).
QTEXT() macro This is useful for creating and supplying literal string values inside components. When _UNICODE is defined, QTEXT() appends the L ## escape sequence onto the supplied text. This instructs the compiler to treat the resulting text as a string of qoschars. QTEXT() can be used anywhere where a qoschar* argument is required, for example:
qoschar *myText = (qoschar *)QTEXT("Version 2.0.0.0 ");
QBYTELEN() & QOSBYTELEN() macros These provide a simple conversion from a supplied character length to the corresponding Utf32 or Utf16/Utf8 byte length respectively. It should be noted that they do not operate on strings or arrays of characters directly. They simply multiply the supplied parameter by 4 in the case of QCHARLEN() or 2 (or 1) in the case of QOSCHARLEN().
QCHARLEN() & QOSCHARLEN() macros These provide a simple conversion from a supplied byte length to the corresponding qchar or qoschar character length respectively. It should be noted that they do not operate on strings or arrays of characters directly. They simply divide the supplied parameter by 4 in the case of QCHARLEN() or 2 (or 1) in the case of QOSCHARLEN().
CHRcopyAsciiStringToQchar
(Defined in callback.h)
This function converts a null-terminated string of 7-bit ASCII characters to qchars. The destination buffer must be pre-defined and large enough to contain the Utf32 data.
CHRcopyQcharStringToAscii This function converts a null-terminated string of qchars to 7-bit ASCII.
Assumes that the source text contains non-Unicode characters.
CHRcopyAsciiDataToQchar This function copies and converts the supplied string of ASCII characters to qchars. The source length must be supplied.
CHRcopyAsciiStringToQoschar This function converts a null-terminated string of 7-bit ASCII characters to qoschars.
CHRcopyQoscharStringToAscii This function converts a null-terminated string of qoschars to 7-bit ASCII.
Assumes that the source text contains non-Unicode characters.
CHRcopyAsciiDataToQoschar This function copies and converts the supplied string of ASCII data to qoschars. The source length must be supplied.
OMstr... functions
(Defined in omstring.h)
There are a number of Omnis string functions to mirror the standard C string functions. These operate on strings of qchars and are prefixed to distinguish them from their ASCII counterparts. For example: OMstrcpy, OMstrlen, OMstrncat & OMstrtok.
There are also functions to convert between character strings and integers: OMlongToString and OMstrtoul.

Loading Resources
When loading resource strings (for example when responding to the ext_designinfo dispatcher message), you should either use LoadStringW for Unicode targets or LoadStringA and convert to qchars using CHRcopyAsciiStringToQchar().
For example:

LoadStringA(hInstLib, strid, buffer, 256); //Load resource string into buffer
startPtr = strchr( buffer, '(' );
endPtr = strrchr( buffer, ')' );
length = (short) ( endptr - startptr + 1 );    //Extract text between the parentheses
if ((length > 0) && (startPtr) && (endPtr))
{
    #ifdef isunicode       //For the Unicode target- convert from ASCII to Utf32
        *++endPtr = '\0';   //add null terminator
        CHRcopyAsciiStringToQchar(startPtr, destPtr, qtrue);
    #else             //Otherwise just copy the ASCII text to the destination buffer
        OMstrncpy((qchar *)destPtr, (qchar *)startPtr, length);
    #endif
}

Reading Omnis FldVals
Old-style components typically exchange data with Omnis via GetFldVal and SetFldVal callbacks.
There are a number of format specifiers to allow Unicode parameters to be passed:

fmt_pstring A pascal-style string of qchars- with the length value at element zero
fmt_cstring A null-terminated string of qchars
fmt_hostring A string of qoschars stored in handle-memory
fmt_costring A null terminated string of qoschars
fmt_ostringlen An integer value corresponding ot the character length the parameter
fmt_hutf8 Character data which is encoded as Utf8 and stored in handle memory

For example:

GetFldVal(ref_parm1, fmt_cstring, sizeof(buffer), &buffer[0], callback);   //Get parameter as a string of qchars

Writing Omnis FldVals
You can also write data back to Omnis FldVals using either the Utf8, Utf16 or Utf32 encoding using the format specifiers shown above:

SetFldVal (ref_parm1 ,fmt_cstring, &buffer[0] ,callback); //set buffer as a string of qchars
or
qoschar *myText = QTEXT("Default Value");
SetFldVal (ref_parm1 ,fmt_costring, myText ,callback); //set buffer as a string of qoschars

About Omnis Fonts
In order to display Unicode data in an Omnis field or in the method editor, an appropriate font must be chosen which supports the required subset of Unicode. Unicode Fonts typically support a range of Unicode characters, displaying characters outside this range as square blocks. Please refer to Technote TNXM0002 for an illustration.

Sample Project and Omnis Library
The following sample project contains a simple character property which can be set and retrieved using the Test_SetText and Test_GetText methods. There is also a Test_Unicode method which illustrates setting of an Omnis FldVal from a literal string of qoschars. The sample is supplied with a Visual Studio 2008 project file.
An Omnis Studio 4.3.1 Unicode library is also provided which loads and calls these methods.

Sample Project
(tnxm0003.zip)
Sample Library (utest.lbs) Converting External Components to Unicode (pdf. See chapter 4)

References
External Component SDK and Documentation: Component SDK