Migrating to DataFlex 2021 Part 2
Lesson 9: Unicode and external APIs
This lesson will show how to apply language changes for making external APIs support Unicode. Since all strings have become UTF-8, a lot already works and has become Unicode, but not all. The external APIs and Windows interfacing may specially need extra work. External functions that call the ANSI versions of a function, should be changed into the Wide version. This has been done for the packages, but there may be others in the application that need attention. The goal is to switch Windows API calls from the ANSI version to the UTF-16 version. In other words, from the ‘A’ version to the ‘W’ version.
If ANSI functions are still being used, it will still work in many cases, but non-ASCII characters will not display correctly; they may show up as question marks. Since strings in DataFlex are UTF-8, conversions are needed between UTF-8 and UTF-16 for communicating with Windows functionality. For DataFlex packages and tools, the changes have been made.
For example, the Windows function ‘DrawText.’ As shown here, ‘DrawTextA’ was previously called. Now ‘DrawTextW’ is called, and the external function is renamed to ‘DrawTextW.’ It is important to note that the second parameter is a pointer to a string, to a wide string now.
If there is to be a call to this function in the source, an ‘ToAnsi’ call is probably used with this parameter. It could look like this…
The ‘sLabel’ variable is converted from OEM to ANSI before its pointer is passed to the ‘DrawText’ function. The wrappers for many Windows functions have been created to limit the number of changes that will need to be made. There is a wrapper function for ‘DrawText,’ which means that the call to ‘DrawText’ is unchanged. It would still work, and support Unicode, but at the very least, the ToAnsi statement must be removed because the ANSI version is not being called. The Unicode version is.
Demonstration - The Wrapper Function
- Here is example code that calls the ‘DrawText’ function.
- The ‘toAnsi’ call needs to be removed. Now, in DataFlex 2021, invoking ‘DrawText’ means that the wrapper function of ‘DrawText’ is called.
- Looking at the definition, t has the name of the original DrawText external_function, but it is now a function. This wrapper function converts the string, which is passed by its pointer, to a wide string, a UTF-16 string. Then it calls the external_function DrawTextW with the same parameters as those passed to the wrapper function, but with the change in the second parameter. The contents of the wrapper function do not need to attention, it just works.
- The actual external_function DrawTextW calls the Unicode version of the Windows function.
- Now, it works well, but instead of calling the wrapper function, the code could be changed to call the DrawTextW external function directly. If done, it must be called with a pointer to a wide string.
- The second line converts a DataFlex string, which is UTF-8, to a UTF-16 string, by a simple move statement. The last line calls the DrawTextW external function directly, with a pointer to the WString variable.
- Windows APIs must be converted to UTF-16. If other external APIs that are used might be UTF-8. In that case, a wrapper function is not needed, and it can be called directly using the DataFlex string.
- The example shown illustrated the kind of changes that need to be made for calling the Unicode version of Windows functions. This is also true for custom components that are UTF-16.
- Attention will have to be paid to other items such as ‘SendMessage,’ ‘SetWindowLong,’ callback functions, etcetera.
- ‘SendMessage’ is implemented in the runtime and will call the wide version of Windows. If a string is passed, it should be a ‘WString.’ A regular DataFlex string won’t work well. The same is true for functions like ‘SetWindowLong,’ ‘GetClassLong’ and others.
- When correcting communication with external APIs to support Unicode the following should be looked for:
- ToAnsi and ToOem: Check the code to see if changes are needed.
- External functions
- Check if the function expects or returns strings.
- Check whether UTF-8 or UTF-16 is expected.
- If UTF-16, check if a wrapper is already defined (and used)
- Else consider calling the wide version directly
- SendMessage
- See if strings are involved. If so, change to WString.
- Change to using wide strings when calling UTF-16 external functions directly.