Tuesday, September 16, 2008

Tips When Porting a Delphi Application to Delphi 2009

Over the past few months I have been keeping notes as I port some components and applications to Delphi 2009, specifically for the purpose of gaining unicode support. While my project is nowhere near complete, I wanted to post some tips. If you are considering porting to Delphi 2009 the CodeGear Developer Network has lots of great documentation describing the new unicode VCL and some unicode basics. This post is simply meant to supliment that information with issues I have encountered and is sprinkled with a few Advantage-specific notes as well.

Database Functionality and Changes

If the AsString method is used on field types that are not ansi-specific you will get unexpected results. For example, if you have a field of type ftBytes and you used to use MyByteField.AsString to set and get these values, you will now be getting unicode characters as opposed to single byte characters. The TBinaryField class has a new AsAnsiString method you will need to use in order to get the results you are used to.

If you read any TBlobStream data into character buffers, those buffers will need to be changed to AnsiChar buffers or TByte buffers, otherwise length/offsets/etc will not be what your existing code expects.

The Delphi database core has been modified and will now parse an SQL statement like the following:

CREATE TABLE c:\mytable ( id INTEGER );

and interpret it as having one parameter called \mytable. It will then automatically add an item to the query component’s Params property. In Advantage execution of the statement will then fail with the error “Error 5110: The parameter number specified was invalid for the statement.”, as really there is no parameter in this statement.

To work around this problem in the Delphi VCL, you must quote the table name. For example:

CREATE TABLE “c:\mytable” ( id INTEGER );

Reading ansi string data from a stream into a string buffer will not work. You need to explicitly read into a temporary ansi buffer first. See below:

The reason the temporary ansi buffer is necessary is because ReadBuffer automatically determines the type of the destination buffer and acts accordingly. If you had stored ansi data in the file or stream, you need to read it into an ansi buffer, not a unicode buffer. If you pass a unicode buffer to ReadBuffer it is going to think you stored unicode data and will read it out as such.

Under the covers CodeGear has changed the TBookmark type from a pointer to TBytes. This will not affect most applications that simply use the GetBookmark and FreeBookmark TDataSet methods. If, however, you are doing anything "goofy" with the pointer you get from GetBookmark, beware. Many of our automated tests needed to be modified to consume the bookmarks in a more generic/standard fashion.

Advantage-Specific Issues

If using the TAdsDictionary component, many of the parameters for functions like TAdsDictionary.AdsDDSetTableProperty are sent via Pointers. If in the past you were casting string variables to PChar, these casts will need to change to send an ansi character buffer now. For example:

AdsDictionary1.SetTableProperty( 'table1', ADS_DD_TABLE_DEFAULT_INDEX, pchar(strDefaultIndex), 8, 0, '' );

Would need to change to:

AdsDictionary1.SetTableProperty( 'table1', ADS_DD_TABLE_DEFAULT_INDEX, PAceChar( AnsiString( strDefaultIndex ) ), 8, 0, '' );

If you use any API’s that return data into char buffers (in Advantage this means ACE API’s), those buffers need to be redeclared as arrays of bytes or an array of AnsiChar, not an array of char (as char is now a unicode char). If using the ACE API a new type called AceChar has been defined and can be used.

Windows API’s

If you are calling Windows API’s and sending in buffers, you may have been using the sizeof function when telling the API how long your buffer is. Those calls need to be changed to use the Length function, as the Windows widechar API’s require the number of characters, not the number of bytes. For example:

must be changed to:

If you were not explicitly calling the “A” functions (for example, SearchPathA), then when compiled in Delphi 2009 your application will now be using the equivalent “W” function (SearchPathW). In many cases this works well, as the Delphi buffers you were passing are now unicode buffers. There are still situations where manual inspection is necessary, however. For example, any API’s that accept a structure will need to be inspected to verify you are populating any string fields in the structure with the expected string type.

No comments:

Post a Comment