0

I've migrated my project from XE5 to 10 Seattle. I'm still using ANSII codes to communicate with devices. With my new build, Seattle IDE is sending † character instead of space char (which is #32 in Ansii code) in Char array. I need to send space character data to text file but I can't.

I tried #32 (like before I used), #032 and #127 but it doesn't work. Any idea?

Here is how I use:

fillChar(X,50,#32);

Method signature (var X; count:Integer; Value:Ordinal)

Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94

2 Answers2

5

Despite its name, FillChar() fills bytes, not characters.

Char is an alias for WideChar (2 bytes) in Delphi 2009+, in prior versions it is an alias for AnsiChar (1 byte) instead.

So, if you have a 50-element array of WideChar elements, the array is 100 bytes in size. When you call fillChar(X,50,#32), it fills in the first 50 bytes with a value of $20 each. Thus the first 25 WideChar elements will have a value of $2020 (aka Unicode codepoint U+2020 DAGGER, ) and the second 25 elements will not have any meaningful value.

This issue is explained in the FillChar() documentation:

Fills contiguous bytes with a specified value.

In Delphi, FillChar fills Count contiguous bytes (referenced by X) with the value specified by Value (Value can be of type Byte or AnsiChar)

Note that if X is a UnicodeString, this may not work as expected, because FillChar expects a byte count, which is not the same as the character count.

In addition, the filling character is a single-byte character. Therefore, when Buf is a UnicodeString, the code FillChar(Buf, Length(Buf), #9); fills Buf with the code point $0909, not $09. In such cases, you should use the StringOfChar routine.

This is also explained in Embarcadero's Unicode Migration Resources white papers, for instance on page 28 of Delphi Unicode Migration for Mere Mortals: Stories and Advice from the Front Lines by Cary Jensen:

Actually, the complexity of this type of code is not related to pointers and buffers per se. The problem is due to Chars being used as pointers. So, now that the size of Strings and Chars in bytes has changed, one of the fundamental assumptions that much of this code embraces is no longer valid: That individual Chars are one byte in length.

Since this type of code is so problematic for Unicode conversion (and maintenance in general), and will require detailed examination, a good argument can be made for refactoring this code where possible. In short, remove the Char types from these operations, and switch to another, more appropriate data type. For example, Olaf Monien wrote, "I wouldn't recommend using byte oriented operations on Char (or String) types. If you need a byte-buffer, then use ‘Byte’ as [the] data type: buffer: array[0..255] of Byte;."

For example, in the past you might have done something like this:

var
  Buffer: array[0..255] of AnsiChar;
begin
  FillChar(Buffer, Length(Buffer), 0);

If you merely want to convert to Unicode, you might make the following changes:

var
  Buffer: array[0..255] of Char;
begin
  FillChar(Buffer, Length(buffer) * SizeOf(Char), 0);

On the other hand, a good argument could be made for dropping the use of an array of Char as your buffer, and switch to an array of Byte, as Olaf suggests. This may look like this (which is similar to the first segment, but not identical to the second, due to the size of the buffer):

var
  Buffer: array[0..255] of Byte;
begin
  FillChar(Buffer, Length(buffer), 0);

Better yet, use this second argument to FillChar which works regardless of the data type of the array:

var
  Buffer: array[0..255] of Byte;
begin
  FillChar(Buffer, Length(buffer) * SizeOf(Buffer[0]), 0);

The advantage of these last two examples is that you have what you really wanted in the first place, a buffer that can hold byte-sized values. (And Delphi will not try to apply any form of implicit string conversion since it's working with bytes and not code units.) And, if you want to do pointer math, you can use PByte. PByte is a pointer to a Byte.

The one place where changes like may not be possible is when you are interfacing with an external library that expects a pointer to a character or character array. In those cases, they really are asking for a buffer of characters, and these are normally AnsiChar types.

So, to address your issue, since you are interacting with an external device that expects Ansi data, you need to declare your array as using AnsiChar or Byte elements instead of (Wide)Char elements. Then your original FillChar() call will work correctly again.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
4

If you want to use ANSI for communication with devices, you would define the array as

x: array[1..50] of AnsiChar;

In this case to fill it with space characters you use

FillChar(x, 50, #32);

Using an array of AnsiChar as communication buffer may become troublesome in a Unicode environment, so therefore I would suggest to use a byte array as communication buffer

x: array[1..50] of byte;

and intialize it with

FillChar(x, 50, 32);
Rudy Velthuis
  • 28,387
  • 5
  • 46
  • 94
Tom Brunberg
  • 20,312
  • 8
  • 37
  • 54