Jump to content
Jean_D

Since February 2025, the creation of an 'ini' file with Delphi is much slower than in the past.

Recommended Posts

Using 'Delphi 12 Update 1,' I created a simple project. The purpose of the project was to create an 'ini' file (via the TIniFile component) made up of ten sections. Each section holding forty-five keys. Each key having a random integer (between 0 and 999) as value.

 

When the executable is run under an older 32-bit Windows OS, the creation of the 'ini' takes less than two hundred milliseconds. However, when the same executable is run under a newer 64-bit Windows OS (such as 'Windows 10'), the creation of the 'ini' is considerably slower. Indeed, it took almost three seconds for the file to be created.

 

As anybody else experienced this 'new' behavior? I am saying 'new' behavior because prior to February 2025, the execution times were almost identical between the older OS and the newer one.

Edited by Jean_D

Share this post


Link to post

Must be something specific on your PC.  Using Delphi 12.2 Patch 2 on Windows 10 64-bit, when I create an INI file with the specifications you have described, it takes only 150ms with TIniFile.  Switch to TMemIniFile and that drops to under 5ms.

Edited by Remy Lebeau

Share this post


Link to post

May I ask which anti-virus software are you using? I am using 'Microsoft Defender Antivirus'. I have noticed that, if I turn off 'Microsoft Defender Antivirus,' my speed issue disappear.

Share this post


Link to post
4 hours ago, Jean_D said:

May I ask which anti-virus software are you using?

I use AVG.

4 hours ago, Jean_D said:

I have noticed that, if I turn off 'Microsoft Defender Antivirus,' my speed issue disappear.

You might consider adding an exception to it for your data folder.

Share this post


Link to post

You are correct. Adding an exception does indeed remove the slowness. It is strange though that the same program written in C++ via Visual Studio does not experience the slowness (without adding an exception).

Share this post


Link to post

I bet the C++ code does not do the same as TIniFile in Delphi which calls WritePrivateProfileString for every entry.

Share this post


Link to post

The C++ code might not be 100% identical to the Delphi one. But its essence is the same.

void CreateIniFile(const char* iniFileName) {
    // Get the directory of the executable
    std::wstring exeDir = GetExecutableDirectory();
    if (exeDir.empty()) {
        return; // Exit if we couldn't get the executable directory
    }

    // Combine the executable directory with the INI file name
    std::wstring iniFilePath = exeDir + L"\\" + std::wstring(iniFileName, iniFileName + strlen(iniFileName));

    // Define the sections
    const char* sections[] = { "Section1", "Section2", "Section3", "Section4", "Section5", "Section6" };

    // Convert iniFile to wide-character string (LPCWSTR)
    std::wstring wIniFile = iniFilePath;

    // Loop through the sections
    for (int i = 0; i < 6; ++i) {
        std::string section = sections[i];
        std::wstring wsection;
        ConvertToWideChar(section, wsection);

        // Generate a random number of keys between 40 and 50 for this section
        int numKeys = RandomNumber(40, 50);

        // Loop through the keys in each section
        for (int j = 1; j <= numKeys; ++j) {
            // Create a key name (e.g., Key1, Key2, ... KeyN)
            std::string key = "Key" + std::to_string(j);
            std::wstring wkey;
            ConvertToWideChar(key, wkey);

            // Create an integer value for the key (e.g., 123, 456, ...)
            int value = RandomNumber(1, 1000);  // You can change the range as needed
            std::string valueStr = std::to_string(value);
            std::wstring wvalue;
            ConvertToWideChar(valueStr, wvalue);

            // Write the key-value pair to the INI file
            BOOL result = WritePrivateProfileString(wsection.c_str(), wkey.c_str(), wvalue.c_str(), wIniFile.c_str());

            if (!result) {
                std::cout << "Error writing key " << key << " to section " << section << std::endl;
            }
        }
    }

    std::wcout << L"INI file created successfully at " << iniFilePath << std::endl;
}

 

Share this post


Link to post
1 hour ago, Jean_D said:

The C++ code might not be 100% identical to the Delphi one. But its essence is the same.

Hard to say when you didn't show the Delphi code to compare.

 

On a side note...

1 hour ago, Jean_D said:

// Combine the executable directory with the INI file name
std::wstring iniFilePath = exeDir + L"\\" + std::wstring(iniFileName, iniFileName + strlen(iniFileName));

 

That is not the correct way to convert a char* string to a std::wstring.  That just makes a copy of each char as-is to wchar_t, which does not actually translate the characters from one encoding to another.  You need MultiByteForWideChar() or equivalent for that task to avoid data loss/corruption.

 

Since you have a ConvertToWideChar() function, why not use it? Hopefully it does a proper conversion:

std::wstring iniFilePath;
ConvertToWideChar(exeDir + "\\" + std::string(iniFileName), iniFilePath);

Though, why does ConvertToWideChar() take a std::wstring variable as an output parameter, instead of returning the std::wstring?  If it did that instead, you could eliminate your wIniFile, wsection, and wkey variables.

1 hour ago, Jean_D said:

// Define the sections
const char* sections[] = { "Section1", "Section2", "Section3", "Section4", "Section5", "Section6" };

 

Why char* here?  You are converting from char* to std::string to std::wstring, which is a bit overkill.  You could have the array store wchar_t* instead, and get rid of the std::string and std::wstring variables, since WritePrivateProfileStringW() takes wchar_t* strings.

1 hour ago, Jean_D said:

// Create a key name (e.g., Key1, Key2, ... KeyN)
std::string key = "Key" + std::to_string(j);
std::wstring wkey;
ConvertToWideChar(key, wkey);

// Create an integer value for the key (e.g., 123, 456, ...)
int value = RandomNumber(1, 1000);  // You can change the range as needed
std::string valueStr = std::to_string(value);
std::wstring wvalue;
ConvertToWideChar(valueStr, wvalue);

 

If you use std::to_wstring() instead of std::to_string(), you can get rid of these std::string variables and ConvertToWideChar() calls.

Edited by Remy Lebeau

Share this post


Link to post
33 minutes ago, Remy Lebeau said:

Hard to say when you didn't show the Delphi code to compare.

Fair point, here is the Delphi code:

procedure TForm1.Button1Click(Sender: TObject);
var
  iCnt: Byte;
  iCpt: Byte;
  iSection: String;
  iIniFile: TIniFile;
  iIdentifier: String;
begin
  iIniFile := TIniFile.Create(ChangeFileExt(Application.ExeName, '.ini'));
  for iCnt := 1 to 6 do
  begin
    iSection := 'Tab #' + IntToStr(iCnt);
    for iCpt := 1 to 45 do
    begin
      iIdentifier := 'Key #' + IntToStr(iCpt);
      iIniFile.WriteInteger(iSection, iIdentifier, Random(999));
    end;
  end;
  iIniFile.Free;
end;
34 minutes ago, Remy Lebeau said:

On a side note...

Thanks for all the pointers. In terms of Visual Studio C++ (or C++ in general), I have very little knowledge. I used AI to help me write the code. The code that I posted is the code that I got with its help. I will use your inputs to improve it and learn from it.

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×