4

Here's a curious phenomenon. I have a Delphi 10.3 application that has started writing duplicate entries to a TIniFile.

Here's the code:

with TIniFile.Create(UserDatFileName) do
try
  WriteInteger(SFormName, 'Top', AForm.Top);
  WriteInteger(SFormName, 'Left', AForm.Left);
  WriteInteger(SFormName, 'Height', AForm.Height);
  WriteInteger(SFormName, 'Width', AForm.Width);
  WriteString(SFormName, 'WindowState', SWindowState);
finally
  Free;
end;

This has worked fine for years. Now, all of a sudden, I'm getting output like this:

[fMainForm]
Top=0
Left=0
Height=556
Width=671
WindowState=wsMaximized
pnlNavigation.Width=165
TreeListcxTreeListModule.Width=161
Top=0
Left=0
Height=556
Width=671
WindowState=wsMaximized
pnlNavigation.Width=165
TreeListcxTreeListModule.Width=161
Top=0
Left=0
Height=556
Width=671
WindowState=wsMaximized
pnlNavigation.Width=165
TreeListcxTreeListModule.Width=161
... etcetera etcetera

Any suggestions about why this might be happening? I thought TIniFile value pairs were supposed to be unique within each section?

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
sdaberle
  • 69
  • 1
  • 2
  • 2
    Can you provide a [mcve]? Because it's likely environmental. – David Heffernan Dec 28 '20 at 22:55
  • I think this could theoretically happen when multiple applications are writing at the same time. I've never seen this exact behavior, but I have seen errors that indicate that TIniFile (using the `WritePrivateProfileString` API), locks only a part of a file for writing. So maybe, if a value doesn't exist, multiple applications could be able to add it at the same time. – GolezTrol Dec 29 '20 at 01:08
  • 3
    Btw, I would use `TMemIniFile` nowadays. That is the default on any non-Windows platform, and works well on Windows as well. Quite a bit faster, even. You can flush changes all at once, instead of calling this relatively slow API on each write. – GolezTrol Dec 29 '20 at 01:11
  • It's not illegal to have an inifile like this, and [with the right API](https://stackoverflow.com/questions/4214689/ini-file-with-duplicate-keys) you can actually read those values. – GolezTrol Dec 29 '20 at 01:13
  • Curiouser and curiouser. Turns out it also wasn't reading the IniFile entries on start up -- it was executing all the Read... statements but not getting the values. Oddly, adding a ReadSection statement (I was trying to verify the IniFile contents) got the other reads to work but the duplicate writes continued on closing. Switching to TMemIniFile seems to have resolved both problems but I'd love to know what was causing it in the first place. Probably as David Heffernen suggests it was environmental, and my guess is the two issues are related: writing duplicate keys because not reading right. – sdaberle Dec 29 '20 at 08:12
  • 5
    My guess would be a file format issue. Look at the file with a hex editor, there might be characters in it that break the PrivateProfileString functions. – dummzeuch Dec 29 '20 at 08:49
  • 1
    Did you try to delete the file and then looked if the problem is still there... – R. Hoek Dec 29 '20 at 14:05
  • @r-hoek: I didn't try that, no. No doubt it would have worked. But then I wouldn't necessarily have found the issue. – sdaberle Dec 29 '20 at 18:53

1 Answers1

1

@dummzeuch for the win: there were three extraneous characters (Hex EF BB BF) at the beginning of the file. Removing those removed the problem. I also found the same three characters at the start of another similarly problematic INI file.

sdaberle
  • 69
  • 1
  • 2
  • 1
    That is the byte order mark (BOM). It defines that the text that follows is encoded as [UTF-8](https://en.wikipedia.org/wiki/Byte_order_mark#UTF-8). – LU RD Dec 29 '20 at 21:57
  • This means that the ini file is stored as UTF-8 by your program or some other program. If you use `TMemIniFile`, you can read or write the file as UTF-8. See [How do I read a UTF8 encoded INI file?](https://stackoverflow.com/q/16364869/576719). But first, figure out why it was written as UTF-8 in the first place. – LU RD Dec 29 '20 at 22:05