4
votes

I'm starting to use Inno Setup, and I have some problems with my INI file encoding.
I want to save user input in the INI file, and this input can contain accents.

I use Inno Setup Unicode, my setupScript.iss is UTF-8 encoded, and here is my code (a part) :

[INI]
Filename: "{app}\www\conf\config.ini"; Section: "Settings"; Key: "ca.plafondAnnuel"; String: "{code:GetUser|Plafond}"
Filename: "{app}\www\conf\config.ini"; Section: "Settings"; Key: "app.siren"; String: "{code:GetUser|Siren}"
Filename: "{app}\www\conf\config.ini"; Section: "Settings"; Key: "app.adresse"; String: "{code:GetUser|Adresse}"


[Code]
var
  UserPage: TInputQueryWizardPage;
  ExamplePage : TInputOptionWizardPage;
  ImmatriculationPage : TInputOptionWizardPage;
  FakeElemIndex: Integer;
  FakeElem: TCustomEdit;
  AdresseTextarea: TNewMemo;

procedure InitializeWizard;
begin
  UserPage := CreateInputQueryPage(wpWelcome,
    'Configuration de l''application', '',
    'Configurez ici votre application. Une fois installée, vous pourrez modifier ces valeurs.');

  UserPage.Add('Siren :', False);
  UserPage.Add('Plafond annuel (utilisé par les auto-entreprises, mettre 0 si vous ne souhaitez pas plafonner votre chiffre d''affaire.):', False);

  FakeElemIndex := UserPage.Add('Votre adresse complète (telle qu''elle s''affichera sur les devis et factures, avec nom complet):', False);
  FakeElem  := UserPage.Edits[FakeElemIndex];

  AdresseTextarea := TNewMemo.Create(WizardForm);
  AdresseTextarea.Parent := FakeElem.Parent;
  AdresseTextarea.SetBounds(FakeElem.Left, FakeElem.Top, FakeElem.Width, ScaleY(50));

  // Hide the original single-line edit
  FakeElem.Visible := False;
end; 

function GetUser(Param: String): String;
begin
  if Param = 'Adresse' then
    Result := AdresseTextarea.Text
  else if Param = 'Siren' then
    Result := UserPage.Values[0]
  else if Param = 'Plafond' then
    Result := UserPage.Values[1];
end;

The value returned by getUser|Adresse in the [INI] part is not UTF-8 encoded: I open the INI file with Notepad++ and I see the file is UTF-8 encoded. But the value adresse is ANSI encoded (If I change the encoding of the file to ANSI, this value is readable)

Someone can help me understand how can I save this user input in UTF-8 ?

Thanks a lot !

1
Note that you should not use UTF-8 in the code. Quoting Unicode Inno Setup article: ... supports Unicode, but not for its input source. This means it does use Unicode string types as said, but any literal Unicode characters in the script will be converted to ANSI. This doesn't mean you can't display Unicode strings: you can for example instead use encoded Unicode characters to build Unicode strings (like S := #$0100 + #$0101 + 'Aa';), or load the string from a file using LoadStringsFromFile, or use a {cm:...} constant.Martin Prikryl
Thanks for you replies Martin. I have to use UTF8 because I made a web app in utf8 without thinking I could install it as a desktop app later. So, the ini conf file must be in utf8 too, in order to be read correctly by my web app. So, I must be able to write in this ini file in UTF8, not ANSI.wyllyjon

1 Answers

4
votes

The INI functions of Inno Setup ([INI] section and SetIni* functions) use internally the Windows API function WritePrivateProfileString.

This function does not support UTF-8 at all. All it supports is the ANSI encoding and UTF-16.
See How to read/write Chinese/Japanese characters from/to INI files?

So it's even questionable whether the target application will be able to read UTF-8-encoded INI file, if it relies on the Windows API function to read it.


Anyway, if you need the UTF-8, you would have to format the entries to INI format yourself and use SaveStringsToUTF8File function to write it.


The last option is to hack it by using the system call WritePrivateProfileString to write seemingly ANSI-encoded string, which will be in fact UTF-8-encoded.

For that you need to convert the string to UTF-8 in your code. You can use WideCharToMultiByte for that.

function WideCharToMultiByte(CodePage: UINT; dwFlags: DWORD;
  lpWideCharStr: string; cchWideChar: Integer; lpMultiByteStr: AnsiString;
  cchMultiByte: Integer; lpDefaultCharFake: Integer;
  lpUsedDefaultCharFake: Integer): Integer;
  external '[email protected] stdcall';

const
  CP_UTF8 = 65001;

function GetStringAsUtf8(S: string): AnsiString;
var
  Len: Integer;
begin
  Len := WideCharToMultiByte(CP_UTF8, 0, S, Length(S), Result, 0, 0, 0);
  SetLength(Result, Len);
  WideCharToMultiByte(CP_UTF8, 0, S, Length(S), Result, Len, 0, 0);
end;

function WritePrivateProfileString(
  lpAppName, lpKeyName, lpString, lpFileName: AnsiString): Integer;
  external '[email protected] stdcall';

procedure CurStepChanged(CurStep: TSetupStep);
var
  IniFileName: string;
begin
  if CurStep = ssInstall then
  begin
    Log('Writting INI file');
    if not ForceDirectories(ExpandConstant('{app}\www\conf')) then
    begin
      MsgBox('Error creating directory for INI file', mbError, MB_OK);
    end
      else
    begin
      IniFileName := ExpandConstant('{app}\www\conf\config.ini');
      if (WritePrivateProfileString(
            'Settings', 'ca.plafondAnnuel', GetStringAsUtf8(GetUser('Plafond')),
            IniFileName) = 0) or
         (WritePrivateProfileString(
            'Settings', 'app.siren', GetStringAsUtf8(GetUser('Siren')),
            IniFileName) = 0) or
         (WritePrivateProfileString(
            'Settings', 'app.adresse', GetStringAsUtf8(GetUser('Adresse')),
            IniFileName) = 0) then
      begin
        MsgBox('Error writting the INI file', mbError, MB_OK);
      end;
    end;
  end;
end;