View Issue Details

IDProjectCategoryView StatusLast Update
0004635JEDI VCL00 JVCL Componentspublic2009-07-08 14:10
ReporterwessonAssigned Toobones 
PrioritynormalSeverityminorReproducibilitysometimes
Status resolvedResolutionfixed 
Product VersionDaily / GIT 
Target VersionFixed in Version3.38 
Summary0004635: Under non unicode versions of Delphi AppStorage classes may fail to properly save / reload unicode strings
DescriptionThis apply to D2007 and below, and under a Japanese version of Windows.

Some stored wide (unicode) strings might be garbaged by the JvRegistryAppStorage component.

Internally, unicode string are converted into UTF-8 strings. however, when these strings are stored to and loaded from the registry, they are considered as ANSI strings - so windows may apply some sort of conversion with the current user default code page.

Problem is that, for japanese users (and maybe other like chinese ...), the codepage-based conversion may misinterpret UTF-8 formatted string, such as when the string is reloaded from the registry, it might have been changed by windows.

This may corrupt stored pathes containing unicode characters. For instance we store a path to user's documents, and user name has some japanese characters.
Additional InformationSolution might be to implement virtual methods

DoReadWideString
and
DoWriteWideString

in base class (on the model of DoReadString and DoWriteString),

and override them in JvRegistryAppStorage class with direct access to widestring registry storage (to avoid the use of UTF-8 formatted strings in registry)

TagsNo tags attached.

Relationships

related to 0004586 resolvedobones JvAppStorage: No longer load/save char property 

Activities

wesson

2008-12-22 08:44

reporter   ~0015171

Issue added as requested by Obones in notes of issue 4586

"
As to the ReadWideString/WriteWriteString functions, please create a new issue with more details for them

"

obones

2008-12-23 00:42

administrator   ~0015177

Could you provide diff for both the base class and the registry storage class?

obones

2009-04-29 11:19

administrator   ~0015440

Hello, any news?

wesson

2009-04-29 15:36

reporter   ~0015482

sorry for the delay, I forgot this. I'll add this asap

jfudickar

2009-06-28 16:53

developer   ~0015728

Any News?

2009-06-29 10:35

 

jvRegistryStorage_safe_widestring.patch (4,726 bytes)
Index: run/JvAppRegistryStorage.pas
===================================================================
--- run/JvAppRegistryStorage.pas	(revision 12353)
+++ run/JvAppRegistryStorage.pas	(working copy)
@@ -1,4 +1,4 @@
-{-----------------------------------------------------------------------------
+{-----------------------------------------------------------------------------
 The contents of this file are subject to the Mozilla Public License
 Version 1.1 (the "License"); you may not use this file except in compliance
 with the License. You may obtain a copy of the License at
@@ -120,6 +120,8 @@
     procedure DoWriteString(const Path: string; const Value: string); override;
     function DoReadBinary(const Path: string; Buf: TJvBytes; BufSize: Integer): Integer; override;
     procedure DoWriteBinary(const Path: string; const Buf: TJvBytes; BufSize: Integer); override;
+    function DoReadWideString(const Path: string; const Default: Widestring): Widestring; override;
+    procedure DoWriteWideString(const Path: string; const Value: Widestring); override;
     class function GetStorageOptionsClass: TJvAppStorageOptionsClass; override;
   public
     constructor Create(AOwner: TComponent); override;
@@ -514,6 +516,33 @@
   RegWriteString(FRegHKEY, SubKey, ValueName, Value);
 end;
 
+function TJvAppRegistryStorage.DoReadWideString(const Path: string; const Default: Widestring): Widestring;
+var
+  SubKey: string;
+  ValueName: string;
+begin
+  SplitKeyPath(Path, SubKey, ValueName);
+  try
+    Result := RegReadWideStringDef(FRegHKEY, SubKey, ValueName, Default);
+  except
+    on E: EJclRegistryError do
+      if StorageOptions.DefaultIfReadConvertError then
+        Result := Default
+      else
+        raise;
+  end;
+end;
+
+procedure TJvAppRegistryStorage.DoWriteWideString(const Path: string; const Value: Widestring);
+var
+  SubKey: string;
+  ValueName: string;
+begin
+  SplitKeyPath(Path, SubKey, ValueName);
+  CreateKey(SubKey);
+  RegWriteWideString(FRegHKEY, SubKey, ValueName, Value);
+end;
+
 function TJvAppRegistryStorage.DoReadBinary(const Path: string; Buf: TJvBytes; BufSize: Integer): Integer;
 var
   SubKey: string;
Index: run/JvAppStorage.pas
===================================================================
--- run/JvAppStorage.pas	(revision 12353)
+++ run/JvAppStorage.pas	(working copy)
@@ -349,6 +349,14 @@
     function DoReadString(const Path: string; const Default: string): string; virtual; abstract;
     { Stores an string value. }
     procedure DoWriteString(const Path: string; const Value: string); virtual; abstract;
+
+    { Retrieves the specified string value. If the value is not found, the Default will be
+      returned. If the value is not a string (or can't be converted to a string an EConvertError
+      exception will be raised. }
+    function DoReadWideString(const Path: string; const Default: Widestring): Widestring; virtual;
+    { Stores an string value. }
+    procedure DoWriteWideString(const Path: string; const Value: Widestring); virtual;
+
     { Retrieves the specified value into a buffer. The result holds the number of bytes actually
       retrieved. }
     function DoReadBinary(const Path: string; Buf: TJvBytes; BufSize: Integer): Integer; virtual; abstract;
@@ -1745,11 +1753,27 @@
   Result := DoReadFloat(Path, Default);
 end;
 
+function TJvCustomAppStorage.DoReadWideString(const Path: string;
+  const Default: Widestring): Widestring;
+begin
+  {$IFDEF COMPILER12_UP}
+  Result := UTF8ToWideString(RawByteString(ReadString(Path, string(UTF8Encode(Default)))));
+  {$ELSE}
+  Result := UTF8Decode(ReadString(Path, UTF8Encode(Default)));
+  {$ENDIF COMPILER12_UP}
+end;
+
 procedure TJvCustomAppStorage.DoWriteDateTime(const Path: string; Value: TDateTime);
 begin
   DoWriteFloat(Path, Value);
 end;
 
+procedure TJvCustomAppStorage.DoWriteWideString(const Path: string;
+  const Value: Widestring);
+begin
+  DoWriteString(Path,string(UTF8Encode(Value)));
+end;
+
 procedure TJvCustomAppStorage.DoError(const msg: string);
 begin
   if Assigned(OnError) then
@@ -3220,17 +3244,13 @@
 function TJvCustomAppStorage.ReadWideString(const Path: string;
   const Default: WideString = ''): WideString;
 begin
-  {$IFDEF COMPILER12_UP}
-  Result := UTF8ToWideString(RawByteString(ReadString(Path, string(UTF8Encode(Default)))));
-  {$ELSE}
-  Result := UTF8Decode(ReadString(Path, UTF8Encode(Default)));
-  {$ENDIF COMPILER12_UP}
+  Result := DoReadWideString(Path,Default);
 end;
 
 procedure TJvCustomAppStorage.WriteWideString(const Path: string;
   const Value: WideString);
 begin
-  WriteString(Path, string(UTF8Encode(Value)));
+  DoWriteWideString(Path,Value);
 end;
 {$ENDIF COMPILER6_UP}
 

wesson

2009-06-29 10:41

reporter   ~0015732

Finally it is here, attached patch build on revision 12353

The base AppStorage class receive 2 additionnal virtual methods to handle widestrings,

The registry AppStorage class override these methods to explicitly call RegReadWideString/RegWriteWideString for WideString storage, instead of converting WideString to UTF8 string and load/store them using RegReadString/RegWriteString

Please note that starting from Delphi 2009 , this is no longer required

obones

2009-07-08 14:10

administrator   ~0015780

This is now in SVN.

Issue History

Date Modified Username Field Change
2008-12-22 08:40 wesson New Issue
2008-12-22 08:44 wesson Note Added: 0015171
2008-12-23 00:41 obones Relationship added related to 0004586
2008-12-23 00:42 obones Note Added: 0015177
2008-12-23 00:42 obones Status new => feedback
2009-04-29 11:19 obones Note Added: 0015440
2009-04-29 15:36 wesson Note Added: 0015482
2009-06-28 16:53 jfudickar Note Added: 0015728
2009-06-29 10:35 wesson File Added: jvRegistryStorage_safe_widestring.patch
2009-06-29 10:41 wesson Note Added: 0015732
2009-07-03 17:30 obones Status feedback => acknowledged
2009-07-08 14:10 obones Note Added: 0015780
2009-07-08 14:10 obones Status acknowledged => resolved
2009-07-08 14:10 obones Fixed in Version => Daily / SVN
2009-07-08 14:10 obones Resolution open => fixed
2009-07-08 14:10 obones Assigned To => obones