5

I got the source of an older project and have to change little things but I got in big trouble because of having only delphi 2010 to do that.

There is an record defined :

bbil = record
  path : string;
  pos: byte;
  nr: Word;
end;

later this definition is used to read from file :

b_bil: file of bbil;
pbbil: ^bbil;
l_bil : tlist;

while not(eof(b_bil)) do
  begin
    new(pbbil);
    read(b_bil, pbbil^);
    l_bil.add(pbbil);
  end

The primary problem is, the compiler does not accept the type "string" in the record because he wants a "finalization". So I tried to change "string" to "string[255]" or "shortstring". Doing this the app is reading the file but with wrong content.

My question is how to convert the old "string" type with which the files were written to the "new" types in Delphi 2010.

I already tried a lot e.g. "{$H-}". Adding only one char more in the record shows, the file is correct, because file is read nearly correct but truncated one char more each dataset - the length of lengthbyte+255chars seems to be correct fpr the definition but shortstring is not matching.

TLama
  • 75,147
  • 17
  • 214
  • 392
rseffner
  • 53
  • 4
  • Using AnsiString in the record definition results also in an compiler errror : "finalization of bbil needed". Shorting these AnsiString in this way "pfad : AnsiString[255];" does also not work : "; expected but [ found". What I probably need isa ShortAnsiString? – rseffner Jan 16 '12 at 17:24
  • 2
    IMO your code should not compile in Delphi 3 too; try ` path : array[0..N] of AnsiChar;`, where N is some constant, maybe 255. – kludg Jan 16 '12 at 17:37
  • 2
    Do as David says, declare the string as `ShortString`. For alignment of the record, try the packed record directive if that helps. For this code to have been working in D3, the {$H-} directive must have been used. – LU RD Jan 16 '12 at 17:43
  • @user539484 If you use `AnsiString`, how could the `read` work? On the other hand the only explanation that really makes sense, `ShortString` apparently fails too. – David Heffernan Jan 16 '12 at 17:44
  • Hi Serg, I compared the mentioned code to a backup of 2007 - the record definition and the process of reading the file is exactly the same as mentionen here. – rseffner Jan 16 '12 at 17:49
  • Hah, yes, sorry, i was talking shite. Ofc, generic AnsiStrings will fail too. Start migrating from Delphi 1 :-) Turbo Pascal strings and probably word(?) aligned fields – OnTheFly Jan 16 '12 at 18:44

3 Answers3

5

Eek! It looks like your code either pre-dates or does not use long strings. If you want to get the same behaviour as in your old Delphi then you need to replace string with ShortString.

I see that you've tried that already and report that it fails. It's really the only explanation that makes any sense to me because all other string types are essentially pointers and so the only way the read could ever have worked is with a ShortString. The migration you are attempting is immense and you probably have huge numbers of confounding problems.

@LU RD makes a good point in the comments that the record layout may differ between Delphi versions since you are not using a packed array. You can investigate the record layout using the two Delphi versions that you have at hand. You will need to arrange that the size of the records match between versions, and that the offsets to the fields also match.

Based on the comments below, adding a padding byte between pos and nr will resolve your problems.

bbil = record
  path : string;
  pos: byte;
  _pad: byte;
  nr: Word;
end;

You could also achieve the same effect by setting the $ALIGN compiler option to {$ALIGN ON} which would be how I think I would go about things.

In the long run you really ought to get away from short strings, ANSI encoding, direct mapping between your internal records and your data files and so on. In the short run you may be better off getting hold of the same version of Delphi as was used to build this code and using that. I'd expect this issue to be just the tip of the iceberg.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • David, I think the `ShortString` advice is correct, only the size of the record needs to match. An alignment directive can solve this or perhaps the packed record declaration. – LU RD Jan 16 '12 at 17:51
  • Using ShortString as replacement of "String" in the recordset was one of the first things I did. But it works not as expected, all read datarecords were trash ;-( I have now to read about "packed record directive" - because I dont know it. – rseffner Jan 16 '12 at 17:56
  • 1
    Just declare the type as `packed record` instead of `record`. This will remove alignments made by the compiler. To see the size of the record, try using `SizeOf(bbil)`. – LU RD Jan 16 '12 at 18:01
  • You should try to find out how many records are in the file. Together with the file size this shall give you the record size. Check this with Sizeof(bbil). You won't succeed while these don't match. – Uwe Raabe Jan 16 '12 at 18:01
  • For my Eyes the best readable results I get when I add "trash : char;" to the end of the record set. So I am able to read the strings for a while (because they where truncated every by one char more), using ShortString makes only the first dataset readable. – rseffner Jan 16 '12 at 18:02
  • Your last comment makes it very likely that you do have a layout mismatch. Do you have a Delphi 3 to hand? – David Heffernan Jan 16 '12 at 18:04
  • 1
    Remember that Char is two bytes in since D2009. – LU RD Jan 16 '12 at 18:05
  • I declared bbil as packed record and got the record size of 259. I also looked at the file and it shows a size of 265720 bytes (266240 at disk, but I think this does not matter). So the record size needs to be 260 because this is the 1022th part of the whole file. – rseffner Jan 16 '12 at 18:09
  • 1
    Then add a trash byte after pos, and you should have this working. – LU RD Jan 16 '12 at 18:13
  • The char size hint helps a lot. I changed "trash : char;" to "trash : byte;" and the strings seems correct as ShortString. Also the values für "pos" makes sense, but values in "nr" were wrong. I am sur to expect values between 1 and 38xx but I get values over 20.000. – rseffner Jan 16 '12 at 18:14
  • @LU_RD Why after "pos" and not after "nr". Your hint works, but why? – rseffner Jan 16 '12 at 18:16
  • 1
    Because the compiler can align byte values on an even address, leaving a space to the next parameter. It is even possible to have the compiler to align at quad addresses. Therefore when handling records during IO, keep them declared as packed. – LU RD Jan 16 '12 at 18:21
  • Or at least post hex dump for several records, @rseffner – OnTheFly Jan 16 '12 at 18:46
2

Just remember:

"string" <> "string[255]" <> "shortstring" <> AnsiString

Back in old DOS/Turbo Pascal days, "strings" were indeed limited to 255 characters. In large part because the 1st byte contained the string length, and a byte can only have a value between 0 and 255.

That is no longer an issue in contemporary versions of Delphi.

"ShortString" is the type for the old DOS/Pascal string type.

"LongString" has been the default string type for a long time (including the Borland Delphi 2006 I currently use for most production work). From Delphi 3 .. Delphi 2009, LongStrings held 8-bit characters, and were limited only by available memory. From Delphi 3 .. Delphi 2009, "LongStrings" were synonymous with "AnsiStrings".

Recent versions of Delphi (Delphi 2009 and higher, including the new Delphi XE2) all now default to multi-byte Unicode "WideString" strings. WideStrings, like AnsiStrings, are also effectively "unlimited" in maximum length.

This article explains in more detail:

http://delphi.about.com/od/beginners/l/aa071800a.htm

PS: Consider using "sizeof(bbil)" and "Packed" for binary records.

paulsm4
  • 114,292
  • 17
  • 138
  • 190
  • 1
    In Delphi terminology long strings refers to both AnsiString and UnicodeString, the latter being the new string type introduced on D2009. And WideString is different again, it is a wrapper around the COM BSTR. – David Heffernan Jan 16 '12 at 19:21
  • 2
    @rseffner - The terminology can be confusing. Mostly Microsoft's fault. Please look at the links I cited (particularly the "about.com" article) to understand strings. And please experiment with "sizeof()" to troubleshoot packing and alignment issues with your binary records. – paulsm4 Jan 16 '12 at 19:40
  • 1
    Terminology is perfectly clear and nothing to do with MS. The language in question is produced by Embarcadero. My previous comment was trying to steer you towards fixing the various errors in your answer. As it stands it is rather inaccurate. – David Heffernan Jan 16 '12 at 19:53
  • On some versions of delphi, String=AnsiString, for instance, so paul, David is pointing out that you are making incorrect statements in your answer. In fact, on all versions of Delphi, `String` is not a type but an alias for another type, be it `ShortString` in Delphi 1, `AnsiString' in delphi 2 through 2007, and 'UnicodeString' in 2009 until now. Sothat would be more clear than your series of inequalities, which are not strictly correct. I don't believe LongString is a type name or alias or documentation term. – Warren P Jan 18 '12 at 04:44
0

Maybe I'm overlooking something, but, the way I see it, your delphi 3 code is broken too. Try to determine the size of your record:

bbil = record
  path : string;
  pos: byte;
  nr: Word;
end;

path (anything between 1 and 256 - one byte for length, rest for data), pos (1 byte), nr (2 bytes), making your record data size vary from 1+1+2=4 bytes to 256+1+2=259 bytes. Under that circumstance, you would get garbage from file in any case, since your program cant now how many bytes to read, before actually reading the data. I suggest you fix your record so that the string is of a fixed size, like:

path : ShortString[255];

Then, you would be able to write and read fine in both delphi 3 and 2010.

Tuncay Göncüoğlu
  • 1,699
  • 17
  • 21