-2

In the past I used INI-Files to store unicode text, but now I need to store unicode text in the executable. How can I achieve this?

I want to store these letters:

āčēūīšķļņž
Little Helper
  • 2,419
  • 9
  • 37
  • 67
  • You do know that my approach below is to be written in a *.PAS (or *.DPR) file, not in a *.DFM file? – Andreas Rejbrand Aug 15 '11 at 12:44
  • 2
    Working with Unicode characters in Delphi 7 is extremely boring. It would be a really, really good idea to upgrade to Delphi 2009+. Alternatively, you can switch to Free Pascal/Lazarus, which is free. – Andreas Rejbrand Aug 15 '11 at 12:49
  • 3
    I can't get Delphi 2009+ because Im 14 yrs old and dont have any money, C'mon people. – Little Helper Aug 15 '11 at 12:53
  • Lazarus is the IDE (with a GUI) for the Free Pascal compiler. – Andreas Rejbrand Aug 15 '11 at 12:55
  • FreePascal/Lazarus don't cost any money. Probably still better than Delphi 7. – Rudy Velthuis Aug 15 '11 at 12:56
  • @Robrok: The ' before ) at the end of the red line shouldn't be there. – Andreas Rejbrand Aug 15 '11 at 13:15
  • If you have switched to FP/Lazarus and are having issues there, I think you should ask a new Q. – Andreas Rejbrand Aug 15 '11 at 13:20
  • 2
    @Robrok: The latter problem can probably be solved by telling Lazarus to save your source files as Unicode or UTF-8 files. Try the options dialog, or the editor right-click menu. – Andreas Rejbrand Aug 15 '11 at 13:27
  • 2
    @Robrok - ok, so you are back in Delphi now. Once again, the answer what Andreas provided is right. You are probably trying to display your Unicode constant in a certain non Unicode component or function. Note that Delphi 7 has no built-in component supporting Unicode and most of the functions are also ANSI (non Unicode). And that the source files are saved also as ANSI so you can't save the chars like `āčēūīšķļņž` directly in your source file, you need to use their ordinal values. –  Aug 15 '11 at 13:49
  • I get it a long time ago, thanks people for respond! – Little Helper Aug 15 '11 at 14:05
  • 2
    Your question *right now* seems like a simple one. What difficulty are you having storing Unicode text in your program? – Rob Kennedy Aug 15 '11 at 15:27
  • The code you've added doesn't really store Unicode text. Maybe that's your problem. You're storing Ansi text because that's what TIniFile does; it doesn't store Unicode text in INI files. For that, you'd need a Unicode version of Delphi. – Rob Kennedy Aug 19 '11 at 18:49
  • @LittleHelper "*I can't get Delphi 2009+ because Im 14 yrs old and dont have any money*" - the [Community Edition](https://www.embarcadero.com/products/delphi/starter) of Delphi is free (at least until your revenue from your software reaches $5000). – Remy Lebeau Sep 24 '19 at 18:17
  • @RobKennedy "*You're storing Ansi text because that's what TIniFile does; it doesn't store Unicode text in INI files. For that, you'd need a Unicode version of Delphi*" - or, just skip using `TIniFile` and use the Unicode version of the `(Get|Write)PrivateProfile...()` Wi32 API functions directly. – Remy Lebeau Sep 24 '19 at 18:19

5 Answers5

3

If you want to save the Unicode INI files then you might try the following code. The files are saved in UTF8 encoding.

Also you might take a look at this Unicode library where you can find a lot of helper functions.

uses IniFiles;

function WideStringToUTF8(const Value: WideString): AnsiString;
var
  BufferLen: Integer;
begin
  Result := '';

  if Value <> '' then
  begin
    BufferLen := WideCharToMultiByte(CP_UTF8, 0, PWideChar(Value), -1, nil, 0, nil, nil);
    SetLength(Result, BufferLen - 1);
    if BufferLen > 1 then
      WideCharToMultiByte(CP_UTF8, 0, PWideChar(Value), -1, PAnsiChar(Result), BufferLen - 1, nil, nil);
  end;
end;

function UTF8ToWideString(const Value: AnsiString): WideString;
var
  BufferLen: integer;
begin
  Result := '';

  if Value <> '' then
  begin
    BufferLen := MultiByteToWideChar(CP_UTF8, 0, PAnsiChar(Value), -1, nil, 0);
    SetLength(Result, BufferLen - 1);
    if BufferLen > 1 then
      MultiByteToWideChar(CP_UTF8, 0, PAnsiChar(Value), -1, PWideChar(Result), BufferLen - 1);
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  IniFile: TIniFile;
const
  UnicodeValue = WideString(#$0101#$010D#$0113#$016B#$012B#$0161);
begin
  IniFile := TIniFile.Create('C:\test.ini');

  try
    IniFile.WriteString('Section', 'Key', WideStringToUTF8(UnicodeValue));
    IniFile.UpdateFile;
  finally
    IniFile.Free;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  IniFile: TIniFile;
  UnicodeValue: WideString;
begin
  IniFile := TIniFile.Create('C:\test.ini');

  try
    UnicodeValue := UTF8ToWideString(IniFile.ReadString('Section', 'Key', 'Default'));
    MessageBoxW(Handle, PWideChar(UnicodeValue), 'Caption', 0);
  finally
    IniFile.Free;
  end;
end;

enter image description here
with Delphi 2007 on 64-bit Windows 7 Enterprise SP 1

2

If you definitely need to use Delphi 7 there are some variants:

  1. Store strings in resources linked to executable file.

  2. Store strings in big memo or same thing, located on global data module or any other visual or non-visual component and access it by index. It's possible because strings in Delphi resources stored in XML-encoded form. E.g. your symbols example āčēūīšķļņž will be stored as &#257;&#269;&#275;&#363;&#299;&#353;&#311;&#316;&#326;&#382;

  3. Store XML-encoded or Base64-encoded strings in string constants inside your code.

For string conversion you can use EncdDecd.pas , xdom.pas or some functions of System.pas like UTF8Encode/UTF8Decode.

To display and edit Unicode strings in Delphi forms you can use special set of Unicode controls like TNT Unicode Controls or subclass original Delphi controls and do some other workarounds by yourself, like described in this excerpt from comments in TntControls.pas (part of TNT Unicode Controls):

Windows NT provides support for native Unicode windows. To add Unicode support to a TWinControl descendant, override CreateWindowHandle() and call CreateUnicodeHandle().

One major reason this works is because the VCL only uses the ANSI version of SendMessage() -- SendMessageA(). If you call SendMessageA() on a UNICODE window, Windows deals with the ANSI/UNICODE conversion automatically. So for example, if the VCL sends WM_SETTEXT to a window using SendMessageA, Windows actually expects a PAnsiChar even if the target window is a UNICODE window. So caling SendMessageA with PChars causes no problems.

A problem in the VCL has to do with the TControl.Perform() method. Perform() calls the window procedure directly and assumes an ANSI window. This is a problem if, for example, the VCL calls Perform(WM_SETTEXT, ...) passing in a PAnsiChar which eventually gets passed downto DefWindowProcW() which expects a PWideChar.

This is the reason for SubClassUnicodeControl(). This procedure will subclass the Windows WndProc, and the TWinControl.WindowProc pointer. It will determine if the message came from Windows or if the WindowProc was called directly. It will then call SendMessageA() for Windows to perform proper conversion on certain text messages.

Another problem has to do with TWinControl.DoKeyPress(). It is called from the WM_CHAR message. It casts the WideChar to an AnsiChar, and sends the resulting character to DefWindowProc. In order to avoid this, the DefWindowProc is subclassed as well. WindowProc will make a WM_CHAR message safe for ANSI handling code by converting the char code to #FF before passing it on. It stores the original WideChar in the .Unused field of TWMChar. The code #FF is converted back to the WideChar before passing onto DefWindowProc.

ThinkJet
  • 6,725
  • 24
  • 33
  • Yes i am using TNT controls to display unicode text instead of MessageBoxW(Handle, PWideChar(MyString), 'Caption', 0); – Little Helper Aug 16 '11 at 06:56
  • @Robrok - about how to store Unicode INI files. My answer was wrong. At least in D2007 I haven't found the overriden constructor with TEncoding. I'll try to update it. –  Aug 16 '11 at 07:36
  • `TEncoding` was added in D2009 – Remy Lebeau Sep 24 '19 at 18:20
0

Simple, the idea is to find a non-visual component, which can store text and store your text there. Prefer that such component can also provide you an editor to edit the text in design time.

There is a component call FormResource which can do this. I use TUniScript. I believe there are other similar components. However, I did not find a usable component from the standard library.

Trenton McKinney
  • 56,955
  • 33
  • 144
  • 158
Gerald Me
  • 107
  • 1
  • 2
  • 8
0

The approach Widestring(#$65E5#$672C) does not work, because Delphi 7 just doesn't expect more than one byte for the #, so the outcome is by far not what you expect when going above 255 or $FF.

Another approach WideChar($65E5)+ WideChar($672C) can be used to store single Unicode codepoints in your source code when knowing that you need to have a Widestring at the start of the assignment (which can also be an empty literal) so the compiler understands which datatype you want:

const
  // Compiler error "Imcompatible types"
  WONT_COMPILE: WideChar($65E5)+ WideChar($672C);

  // 日本
  NIPPON: Widestring('')+ WideChar($65E5)+ WideChar($672C);

Looks cumbersome, but surely has your UTF-16 texts in Delphi 7.

Alternatively, store your constants in UTF-8, which is ASCII safe - that way you can use # easily. One advantage is, that it's a lot less cumbersome to write in your source code. One disadvantage is, that you can never use the constant directly, but have to convert it to UTF-16 first:

const
  // UTF-8 of the two graphemes 日 and 本, needing 3 bytes each
  NIPPON: #$E6#$97#$A5#$E6#$9C#$AC;
var
  sUtf16: Widestring;
begin
  // Internally these are 2 WORDs: $65E5 and $672C
  sUtf16:= UTF8ToWideString( NIPPON );
AmigoJack
  • 5,234
  • 1
  • 15
  • 31
0

Do

const MyString = WideString('Teksts latvie'#$0161'u valod'#$0101);
Andreas Rejbrand
  • 105,602
  • 8
  • 282
  • 384
  • @Robrok: Since I cannot ready your mind, I don't know what is wrong with it. (It *does* work in the sense that the string *is* stored as a wide string in memory...) Maybe you are trying to store Unicode characters in an ANSI source file? Try `WideString('Teksts latvie'#$0161'u valod'#$0101')`. (You can obtain the character codes in many ways -- using `charmap`, a decent text editor (like [Rejbrand Text Editor](http://english.rejbrand.se/rteditor/)), or -- easiest -- using [Wolfram|Alpha](http://www.wolframalpha.com/input/?i=%C5%A1).) – Andreas Rejbrand Aug 15 '11 at 12:40
  • I am using Delphi-7 and if i paste this: const MyString : WideString='āčēūīšķļņž' to Delphi unit it transforms to this: const MyString : WideString=('a-c(e-u-i-s(k,l,n,z('); – Little Helper Aug 15 '11 at 12:45
  • 3
    @Robrok: Andreas is trying to help. It doesn't make that easy if you act like that. I don't remember if Delphi 7 supports it, but try to set the source file to Unicode or UTF8, if possible. – Rudy Velthuis Aug 15 '11 at 12:59
  • How you are showing that string ? Have you tried e.g. `MessageBoxW(Handle, PWideChar(MyString), 'Caption', 0);` ? Don't forget you have no Unicode components in Delphi 7. And most of the functions like ShowMessage won't be Unicode too. You need to use only Unicode compatible things –  Aug 15 '11 at 13:12
  • Yes i use this boring thing ^^^ – Little Helper Aug 15 '11 at 13:14
  • @Robrok - anyway that boring `W` at the end of the `MessageBoxW` means `Widestring`. There is also the `MessageBoxA` what is the `ANSI` version of the same function and the `MessageBox` which in your Delphi 7 refers to the `MessageBoxA` because your Delphi are ANSI, not Unicode. If you would have D2009+, then the `MessageBox` would refer to the `MessageBoxW`, because D2009+ are Unicode. –  Aug 15 '11 at 15:13