View Issue Details

IDProjectCategoryView StatusLast Update
0034876FPCFCLpublic2019-03-24 16:38
ReporterCCRDudeAssigned ToMichael Van Canneyt 
PrioritynormalSeverityminorReproducibilityalways
Status resolvedResolutionduplicate 
PlatformIntel PCOSWindowsOS Version10 (1709)
Product Version3.3.1Product Build 
Target VersionFixed in Version 
Summary0034876: TRegistry's rdMultiString treats Unicode strings as AnsiStrings
DescriptionTRegistry from unit Registry.pas in package fcl-registry supports rdMultiString through methods ReadStringList and WriteStringList - but treats the Unicode data from the WinAPI as ansistrings.
Additional InformationReadStringList/WriteStringList
* use the underlying GetData/PutData,
* which in turn use SysGetData/SysPutData,
* which use RegQueryValueExW and RegSetValueExW.

These get assigned to a "String". Just like the correct implementation in ReadString/WriteString, this needs to use a UnicodeString and proper conversion using UTF8Encode/UTF8Decode before assigning from/to the TStrings parameter.

The required changes are drafted in the attached patch (untested yet).
TagsNo tags attached.
Fixed in Revision
FPCOldBugId
FPCTarget
Attached Files
  • reg-rdmultistring.patch (1,130 bytes)
    Index: packages/fcl-registry/src/registry.pp
    ===================================================================
    --- packages/fcl-registry/src/registry.pp	(revision 40536)
    +++ packages/fcl-registry/src/registry.pp	(working copy)
    @@ -418,7 +431,7 @@
     Var
       Info : TRegDataInfo;
       ReadDataSize: Integer;
    -  Data: string;
    +  Data: UnicodeString;
     
     begin
       AList.Clear;
    @@ -442,7 +455,7 @@
            end;
            SetLength(Data, ReadDataSize);
            Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
    -       AList.Text := Data;
    +       AList.Text := UTF8Encode(Data);
          end
        end
     end;
    @@ -498,11 +511,11 @@
     procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
     
     Var
    -  Data: string;
    +  Data: UnicodeString;
     
     begin
    -  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
    -  PutData(Name, PChar(Data), Length(Data),rdMultiString);
    +  Data := UTF8Decode(StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0);
    +  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
     end;
     
     procedure TRegistry.WriteFloat(const Name: string; Value: Double);
    
    reg-rdmultistring.patch (1,130 bytes)
  • registry.stringlist.diff (1,935 bytes)
    Index: packages/fcl-registry/src/registry.pp
    ===================================================================
    --- packages/fcl-registry/src/registry.pp	(revision 41290)
    +++ packages/fcl-registry/src/registry.pp	(working copy)
    @@ -431,7 +431,7 @@
     Var
       Info : TRegDataInfo;
       ReadDataSize: Integer;
    -  Data: string;
    +  Data: UnicodeString;
     
     begin
       AList.Clear;
    @@ -441,7 +441,7 @@
          If Not (Info.RegData in [rdMultiString]) then
            Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
          SetLength(Data,Info.DataSize);
    -     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
    +     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData);
          if ReadDataSize > 0 then
          begin
            // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
    @@ -453,9 +453,9 @@
              if Data[ReadDataSize] = #0 then
                Dec(ReadDataSize);
            end;
    -       SetLength(Data, ReadDataSize);
    -       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
    -       AList.Text := Data;
    +       SetLength(Data, ReadDataSize div SizeOf(WideChar));
    +       Data := UnicodeStringReplace(Data, #0, LineEnding, [rfReplaceAll]);
    +       AList.Text := UTF8Encode(Data);
          end
        end
     end;
    @@ -511,11 +511,15 @@
     procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
     
     Var
    -  Data: string;
    -
    +  Data: UnicodeString;
    +  Skip: Boolean;
     begin
    -  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
    -  PutData(Name, PChar(Data), Length(Data),rdMultiString);
    +  Skip := List.SkipLastLineBreak;
    +  List.SkipLastLineBreak := True;
    +  Data := UnicodeString(StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]));
    +  Data := Data + #0#0;
    +  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
    +  List.SkipLastLineBreak := Skip;
     end;
     
     procedure TRegistry.WriteFloat(const Name: string; Value: Double);
    
    registry.stringlist.diff (1,935 bytes)
  • registry.stringlist.2.diff (2,023 bytes)
    Index: packages/fcl-registry/src/registry.pp
    ===================================================================
    --- packages/fcl-registry/src/registry.pp	(revision 41290)
    +++ packages/fcl-registry/src/registry.pp	(working copy)
    @@ -431,7 +431,7 @@
     Var
       Info : TRegDataInfo;
       ReadDataSize: Integer;
    -  Data: string;
    +  Data: UnicodeString;
     
     begin
       AList.Clear;
    @@ -441,7 +441,7 @@
          If Not (Info.RegData in [rdMultiString]) then
            Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
          SetLength(Data,Info.DataSize);
    -     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
    +     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData) div SizeOf(WideChar);
          if ReadDataSize > 0 then
          begin
            // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
    @@ -454,8 +454,8 @@
                Dec(ReadDataSize);
            end;
            SetLength(Data, ReadDataSize);
    -       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
    -       AList.Text := Data;
    +       Data := UnicodeStringReplace(Data, #0, LineEnding, [rfReplaceAll]);
    +       AList.Text := String(Data);
          end
        end
     end;
    @@ -511,11 +511,15 @@
     procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
     
     Var
    -  Data: string;
    -
    +  Data: UnicodeString;
    +  Skip: Boolean;
     begin
    -  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
    -  PutData(Name, PChar(Data), Length(Data),rdMultiString);
    +  Skip := List.SkipLastLineBreak;
    +  List.SkipLastLineBreak := True;
    +  Data := UnicodeString(StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]));
    +  Data := Data + #0#0;
    +  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
    +  List.SkipLastLineBreak := Skip;
     end;
     
     procedure TRegistry.WriteFloat(const Name: string; Value: Double);
    @@ -583,7 +587,7 @@
       Value: TStream): Integer;
     begin
       result:=-1; // unimplemented
    - // 
    + //
     end;
     
     function TRegistryIniFile.ReadDate(const Section, Name: string;
    
  • registry.stringlist.3.diff (2,119 bytes)
    Index: packages/fcl-registry/src/registry.pp
    ===================================================================
    --- packages/fcl-registry/src/registry.pp	(revision 41415)
    +++ packages/fcl-registry/src/registry.pp	(working copy)
    @@ -431,7 +431,7 @@
     Var
       Info : TRegDataInfo;
       ReadDataSize: Integer;
    -  Data: string;
    +  Data: UnicodeString;
     
     begin
       AList.Clear;
    @@ -441,7 +441,7 @@
          If Not (Info.RegData in [rdMultiString]) then
            Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
          SetLength(Data,Info.DataSize);
    -     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
    +     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData) div SizeOf(WideChar);
          if ReadDataSize > 0 then
          begin
            // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
    @@ -454,8 +454,8 @@
                Dec(ReadDataSize);
            end;
            SetLength(Data, ReadDataSize);
    -       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
    -       AList.Text := Data;
    +       Data := UnicodeStringReplace(Data, #0, LineEnding, [rfReplaceAll]);
    +       AList.Text := String(Data);
          end
        end
     end;
    @@ -511,11 +511,15 @@
     procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
     
     Var
    -  Data: string;
    -
    +  Data: UnicodeString;
    +  S: String;
     begin
    -  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
    -  PutData(Name, PChar(Data), Length(Data),rdMultiString);
    +  S := List.Text;
    +  if (not List.SkipLastLineBreak) and (Copy(S, Length(S)-Length(LineEnding)+1, Length(LineEnding)) = LineEnding) then
    +    System.Delete(S, Length(S)-Length(LineEnding)+1, Length(LineEnding));
    +  Data := UnicodeString(StringReplace(S, LineEnding, #0, [rfReplaceAll]));
    +  Data := Data + #0#0;
    +  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
     end;
     
     procedure TRegistry.WriteFloat(const Name: string; Value: Double);
    @@ -583,7 +587,7 @@
       Value: TStream): Integer;
     begin
       result:=-1; // unimplemented
    - // 
    + //
     end;
     
     function TRegistryIniFile.ReadDate(const Section, Name: string;
    
  • registry.stringlist.4.pp (2,243 bytes)
    procedure TRegistry.ReadStringList(const Name: string; AList: TStrings);
    // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
    // the size includes any terminating null character or characters
    // unless the data was stored without them! (RegQueryValueEx @ MSDN)
    const
      CSpaceFor2NullChars = 2 * SizeOf(WideChar);
    var
      Info: TRegDataInfo;
      DataSize: Integer;
      PStart, PEnd, Buffer: PWideChar;
      ReadLength: Integer;
      U: UnicodeString;
    begin
      if not Assigned(AList) then
        Exit;
      AList.Clear;
      if not GetDataInfo(Name, Info) or (Info.DataSize <= 0) then
        Exit;
      if Info.RegData <> rdMultiString then
        raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
      DataSize := Info.DataSize + CSpaceFor2NullChars;
      GetMem(Buffer, DataSize);
      try
        ReadLength := (GetData(Name, Buffer, DataSize, Info.RegData) +
          CSpaceFor2NullChars) div SizeOf(WideChar);
        // Always add last 2 terminal #0, we allocate space for it
        Buffer[ReadLength - 1] := #0;
        Buffer[ReadLength - 2] := #0;
        PStart := Buffer;
        while PStart^ <> #0 do
        begin
          PEnd := StrEnd(PStart);
          SetString(U, PStart, PEnd - PStart);
          AList.Append(UTF8Encode(U));
          PStart := PEnd + 1;
        end;
      finally
        FreeMem(Buffer);
      end;
    end;
    
    procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
    var
      Buffer: PWideChar;
      U: UnicodeString;
      i, BufSize, Len: Integer;
      P: PWideChar;
    begin
      if not Assigned(List) or (List.Count = 0) then
        Exit;
      BufSize := 0;
      for i := 0 to List.Count - 1 do
      begin
        U := UnicodeString(List[i]);
        if U <> '' then // Ignore empty lines
          Inc(BufSize, ByteLength(U) + SizeOf(WideChar)); // +1 = +#0
      end;
      Inc(BufSize, SizeOf(WideChar)); // Last #0
      GetMem(Buffer, BufSize);
      try
        P := Buffer;
        for i := 0 to List.Count - 1 do
        begin
          U := UnicodeString(List[i]);
          if U = '' then
            Continue;
          Len := Length(U) + 1;  // +1 = +#0 and U have last #0
          Move(PWideChar(U)^, P^, Len * SizeOf(WideChar));
          Inc(P, Len);
        end;
        P^ := #0;
        PutData(Name, Buffer, BufSize, rdMultiString);
      finally
        FreeMem(Buffer);
      end;
    end;
    
    registry.stringlist.4.pp (2,243 bytes)
  • registry.stringlist.4.diff (3,821 bytes)
    Index: packages/fcl-registry/src/registry.pp
    ===================================================================
    --- packages/fcl-registry/src/registry.pp	(revision 41425)
    +++ packages/fcl-registry/src/registry.pp	(working copy)
    @@ -427,37 +427,44 @@
     end;
     
     procedure TRegistry.ReadStringList(const Name: string; AList: TStrings);
    -
    -Var
    -  Info : TRegDataInfo;
    -  ReadDataSize: Integer;
    -  Data: string;
    -
    +// If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
    +// the size includes any terminating null character or characters
    +// unless the data was stored without them! (RegQueryValueEx @ MSDN)
    +const
    +  CSpaceFor2NullChars = 2 * SizeOf(WideChar);
    +var
    +  Info: TRegDataInfo;
    +  DataSize: Integer;
    +  PStart, PEnd, Buffer: PWideChar;
    +  ReadLength: Integer;
    +  U: UnicodeString;
     begin
    +  if not Assigned(AList) then
    +    Exit;
       AList.Clear;
    -  GetDataInfo(Name,Info);
    -  if info.datasize>0 then
    +  if not GetDataInfo(Name, Info) or (Info.DataSize <= 0) then
    +    Exit;
    +  if Info.RegData <> rdMultiString then
    +    raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
    +  DataSize := Info.DataSize + CSpaceFor2NullChars;
    +  GetMem(Buffer, DataSize);
    +  try
    +    ReadLength := (GetData(Name, Buffer, DataSize, Info.RegData) +
    +      CSpaceFor2NullChars) div SizeOf(WideChar);
    +    // Always add last 2 terminal #0, we allocate space for it
    +    Buffer[ReadLength - 1] := #0;
    +    Buffer[ReadLength - 2] := #0;
    +    PStart := Buffer;
    +    while PStart^ <> #0 do
         begin
    -     If Not (Info.RegData in [rdMultiString]) then
    -       Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
    -     SetLength(Data,Info.DataSize);
    -     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
    -     if ReadDataSize > 0 then
    -     begin
    -       // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
    -       // the size includes any terminating null character or characters
    -       // unless the data was stored without them! (RegQueryValueEx @ MSDN)
    -       if StringSizeIncludesNull then begin
    -         if Data[ReadDataSize] = #0 then
    -           Dec(ReadDataSize);
    -         if Data[ReadDataSize] = #0 then
    -           Dec(ReadDataSize);
    -       end;
    -       SetLength(Data, ReadDataSize);
    -       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
    -       AList.Text := Data;
    -     end
    -   end
    +      PEnd := StrEnd(PStart);
    +      SetString(U, PStart, PEnd - PStart);
    +      AList.Append(UTF8Encode(U));
    +      PStart := PEnd + 1;
    +    end;
    +  finally
    +    FreeMem(Buffer);
    +  end;
     end;
     
     function TRegistry.ReadTime(const Name: string): TDateTime;
    @@ -509,13 +516,39 @@
     end;
     
     procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
    -
    -Var
    -  Data: string;
    -
    +var
    +  Buffer: PWideChar;
    +  U: UnicodeString;
    +  i, BufSize, Len: Integer;
    +  P: PWideChar;
     begin
    -  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
    -  PutData(Name, PChar(Data), Length(Data),rdMultiString);
    +  if not Assigned(List) or (List.Count = 0) then
    +    Exit;
    +  BufSize := 0;
    +  for i := 0 to List.Count - 1 do
    +  begin
    +    U := UnicodeString(List[i]);
    +    if U <> '' then // Ignore empty lines
    +      Inc(BufSize, ByteLength(U) + SizeOf(WideChar)); // +1 = +#0
    +  end;
    +  Inc(BufSize, SizeOf(WideChar)); // Last #0
    +  GetMem(Buffer, BufSize);
    +  try
    +    P := Buffer;
    +    for i := 0 to List.Count - 1 do
    +    begin
    +      U := UnicodeString(List[i]);
    +      if U = '' then
    +        Continue;
    +      Len := Length(U) + 1;  // +1 = +#0 and U have last #0
    +      Move(PWideChar(U)^, P^, Len * SizeOf(WideChar));
    +      Inc(P, Len);
    +    end;
    +    P^ := #0;
    +    PutData(Name, Buffer, BufSize, rdMultiString);
    +  finally
    +    FreeMem(Buffer);
    +  end;
     end;
     
     procedure TRegistry.WriteFloat(const Name: string; Value: Double);
    
  • registry.stringlist.5.diff (2,200 bytes)
    Index: packages/fcl-registry/src/registry.pp
    ===================================================================
    --- packages/fcl-registry/src/registry.pp	(revision 41415)
    +++ packages/fcl-registry/src/registry.pp	(working copy)
    @@ -431,7 +431,7 @@
     Var
       Info : TRegDataInfo;
       ReadDataSize: Integer;
    -  Data: string;
    +  Data: UnicodeString;
     
     begin
       AList.Clear;
    @@ -441,7 +441,7 @@
          If Not (Info.RegData in [rdMultiString]) then
            Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
          SetLength(Data,Info.DataSize);
    -     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
    +     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData) div SizeOf(WideChar);
          if ReadDataSize > 0 then
          begin
            // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
    @@ -454,8 +454,8 @@
                Dec(ReadDataSize);
            end;
            SetLength(Data, ReadDataSize);
    -       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
    -       AList.Text := Data;
    +       Data := UnicodeStringReplace(Data, #0, List.LineBreak, [rfReplaceAll]);
    +       AList.Text := String(Data);
          end
        end
     end;
    @@ -511,11 +511,15 @@
     procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
     
     Var
    -  Data: string;
    -
    +  Data: UnicodeString;
    +  S: String;
     begin
    -  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
    -  PutData(Name, PChar(Data), Length(Data),rdMultiString);
    +  S := List.Text;
    +  if (not List.SkipLastLineBreak) and (Copy(S, Length(S)-Length(List.LineBreak)+1, Length(List.LineBreak)) = List.LineBreak) then
    +    System.Delete(S, Length(S)-Length(List.LineBreak)+1, Length(List.LineBreak));
    +  Data := UnicodeString(StringReplace(S, List.LineBreak, #0, [rfReplaceAll]));
    +  Data := Data + #0#0;
    +  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
     end;
     
     procedure TRegistry.WriteFloat(const Name: string; Value: Double);
    @@ -583,7 +587,7 @@
       Value: TStream): Integer;
     begin
       result:=-1; // unimplemented
    - // 
    + //
     end;
     
     function TRegistryIniFile.ReadDate(const Section, Name: string;
    
  • RegistryTest.zip (4,582 bytes)
  • registry.stringlist.6.diff (2,201 bytes)
    Index: packages/fcl-registry/src/registry.pp
    ===================================================================
    --- packages/fcl-registry/src/registry.pp	(revision 41415)
    +++ packages/fcl-registry/src/registry.pp	(working copy)
    @@ -431,7 +431,7 @@
     Var
       Info : TRegDataInfo;
       ReadDataSize: Integer;
    -  Data: string;
    +  Data: UnicodeString;
     
     begin
       AList.Clear;
    @@ -441,7 +441,7 @@
          If Not (Info.RegData in [rdMultiString]) then
            Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
          SetLength(Data,Info.DataSize);
    -     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
    +     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData) div SizeOf(WideChar);
          if ReadDataSize > 0 then
          begin
            // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
    @@ -454,8 +454,8 @@
                Dec(ReadDataSize);
            end;
            SetLength(Data, ReadDataSize);
    -       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
    -       AList.Text := Data;
    +       Data := UnicodeStringReplace(Data, #0, AList.LineBreak, [rfReplaceAll]);
    +       AList.Text := String(Data);
          end
        end
     end;
    @@ -511,11 +511,15 @@
     procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
     
     Var
    -  Data: string;
    -
    +  Data: UnicodeString;
    +  S: String;
     begin
    -  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
    -  PutData(Name, PChar(Data), Length(Data),rdMultiString);
    +  S := List.Text;
    +  if (not List.SkipLastLineBreak) and (Copy(S, Length(S)-Length(List.LineBreak)+1, Length(List.LineBreak)) = List.LineBreak) then
    +    System.Delete(S, Length(S)-Length(List.LineBreak)+1, Length(List.LineBreak));
    +  Data := UnicodeString(StringReplace(S, List.LineBreak, #0, [rfReplaceAll]));
    +  Data := Data + #0#0;
    +  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
     end;
     
     procedure TRegistry.WriteFloat(const Name: string; Value: Double);
    @@ -583,7 +587,7 @@
       Value: TStream): Integer;
     begin
       result:=-1; // unimplemented
    - // 
    + //
     end;
     
     function TRegistryIniFile.ReadDate(const Section, Name: string;
    
  • registry.stringlist.7.diff (2,149 bytes)
    Index: packages/fcl-registry/src/registry.pp
    ===================================================================
    --- packages/fcl-registry/src/registry.pp	(revision 41415)
    +++ packages/fcl-registry/src/registry.pp	(working copy)
    @@ -430,8 +430,8 @@
     
     Var
       Info : TRegDataInfo;
    -  ReadDataSize: Integer;
    -  Data: string;
    +  ReadDataSize, i: Integer;
    +  Data: UnicodeString;
     
     begin
       AList.Clear;
    @@ -441,7 +441,7 @@
          If Not (Info.RegData in [rdMultiString]) then
            Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
          SetLength(Data,Info.DataSize);
    -     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
    +     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData) div SizeOf(WideChar);
          if ReadDataSize > 0 then
          begin
            // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
    @@ -454,7 +454,7 @@
                Dec(ReadDataSize);
            end;
            SetLength(Data, ReadDataSize);
    -       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
    +       Data := UnicodeStringReplace(Data, #0, AList.LineBreak, [rfReplaceAll]);
            AList.Text := Data;
          end
        end
    @@ -511,11 +511,17 @@
     procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
     
     Var
    -  Data: string;
    -
    +  Data: UnicodeString;
    +  S, LLB: String;
     begin
    -  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
    -  PutData(Name, PChar(Data), Length(Data),rdMultiString);
    +  S := List.Text;
    +  LLB := List.LineBreak;
    +  if (not List.SkipLastLineBreak) and (Copy(S, Length(S)-Length(LLB)+1, Length(LLB)) = LLB) then
    +    System.Delete(S, Length(S)-Length(LLB)+1, Length(LLB));
    +  Data := UnicodeString(StringReplace(S, List.LineBreak, #0, [rfReplaceAll]));
    +  if StringSizeIncludesNull then
    +    Data := Data + #0#0;
    +  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
     end;
     
     procedure TRegistry.WriteFloat(const Name: string; Value: Double);
    @@ -583,7 +589,7 @@
       Value: TStream): Integer;
     begin
       result:=-1; // unimplemented
    - // 
    + //
     end;
     
     function TRegistryIniFile.ReadDate(const Section, Name: string;
    
  • registry.stringlist.7.utf8encode.diff (2,195 bytes)
    Index: packages/fcl-registry/src/registry.pp
    ===================================================================
    --- packages/fcl-registry/src/registry.pp	(revision 41415)
    +++ packages/fcl-registry/src/registry.pp	(working copy)
    @@ -430,8 +430,8 @@
     
     Var
       Info : TRegDataInfo;
    -  ReadDataSize: Integer;
    -  Data: string;
    +  ReadDataSize, i: Integer;
    +  Data: UnicodeString;
     
     begin
       AList.Clear;
    @@ -441,7 +441,7 @@
          If Not (Info.RegData in [rdMultiString]) then
            Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
          SetLength(Data,Info.DataSize);
    -     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
    +     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData) div SizeOf(WideChar);
          if ReadDataSize > 0 then
          begin
            // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
    @@ -454,8 +454,8 @@
                Dec(ReadDataSize);
            end;
            SetLength(Data, ReadDataSize);
    -       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
    -       AList.Text := Data;
    +       Data := UnicodeStringReplace(Data, #0, AList.LineBreak, [rfReplaceAll]);
    +       AList.Text := Utf8Encode(Data);
          end
        end
     end;
    @@ -511,11 +511,17 @@
     procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
     
     Var
    -  Data: string;
    -
    +  Data: UnicodeString;
    +  S, LLB: String;
     begin
    -  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
    -  PutData(Name, PChar(Data), Length(Data),rdMultiString);
    +  S := List.Text;
    +  LLB := List.LineBreak;
    +  if (not List.SkipLastLineBreak) and (Copy(S, Length(S)-Length(LLB)+1, Length(LLB)) = LLB) then
    +    System.Delete(S, Length(S)-Length(LLB)+1, Length(LLB));
    +  Data := UnicodeString(StringReplace(S, List.LineBreak, #0, [rfReplaceAll]));
    +  if StringSizeIncludesNull then
    +    Data := Data + #0#0;
    +  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
     end;
     
     procedure TRegistry.WriteFloat(const Name: string; Value: Double);
    @@ -583,7 +589,7 @@
       Value: TStream): Integer;
     begin
       result:=-1; // unimplemented
    - // 
    + //
     end;
     
     function TRegistryIniFile.ReadDate(const Section, Name: string;
    

Relationships

duplicate of 0035213 closedMichael Van Canneyt Make TRegistry fully Unicode capable 

Activities

CCRDude

2019-01-15 12:32

reporter  

reg-rdmultistring.patch (1,130 bytes)
Index: packages/fcl-registry/src/registry.pp
===================================================================
--- packages/fcl-registry/src/registry.pp	(revision 40536)
+++ packages/fcl-registry/src/registry.pp	(working copy)
@@ -418,7 +431,7 @@
 Var
   Info : TRegDataInfo;
   ReadDataSize: Integer;
-  Data: string;
+  Data: UnicodeString;
 
 begin
   AList.Clear;
@@ -442,7 +455,7 @@
        end;
        SetLength(Data, ReadDataSize);
        Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
-       AList.Text := Data;
+       AList.Text := UTF8Encode(Data);
      end
    end
 end;
@@ -498,11 +511,11 @@
 procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
 
 Var
-  Data: string;
+  Data: UnicodeString;
 
 begin
-  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
-  PutData(Name, PChar(Data), Length(Data),rdMultiString);
+  Data := UTF8Decode(StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0);
+  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
 end;
 
 procedure TRegistry.WriteFloat(const Name: string; Value: Double);
reg-rdmultistring.patch (1,130 bytes)

Thaddy de Koning

2019-01-15 13:39

reporter   ~0113427

Last edited: 2019-01-15 13:41

View 2 revisions

In {$mode delphiunicode} everything works fine. I am not sure the patch is correct.
It seems rather Lazarus centered and fcl-registry is a FPC package: FPC itself doesn't know a string alias for UTF8 strings. "String" can be shortstring, AnsiString or UTF16. NOT UTF8.
If it needs a fix it should be on the Lazarus side, not in FPC core.

Thaddy de Koning

2019-01-15 14:28

reporter   ~0113428

Furthermore Windows does not use UTF8 but UTF16.

CCRDude

2019-01-15 21:37

reporter   ~0113430

Regarding side of fix:

If ReadMultiString/WriteMultiString would not be wrong, then ReadString/WriteString would be wrong, because they do use UTF8Encode/UTF8Decode, while the ones questioned here do not use it. A consistent use of one or the other within the same unit would be a good thing!

Regarding {$mode delphiunicode}:

Not sure what you mean by that. Do you refer to changing the whole RTL to this mode? Because if just changing it for Registry.pas, the two functions I addressed use TStrings, which would still contain ansistring, so the assignment within them would still be without explicit conversion.

Regarding UTF8:

I suggested UTF8 because that's what is in use for rdString. If UTF8 is not to be used in the RTL, that would be another fix required to remove it from existing code.

Thaddy de Koning

2019-01-16 08:42

reporter   ~0113435

Last edited: 2019-01-16 08:43

View 2 revisions

You are correct in assuming that there is still not a complete unicode rtl, but the rtl should not be utf8. Hence I think the patch is wrong.
Tstrings should work, though, with unicode16 strings.

Bart Broersma

2019-02-11 13:38

reporter  

registry.stringlist.diff (1,935 bytes)
Index: packages/fcl-registry/src/registry.pp
===================================================================
--- packages/fcl-registry/src/registry.pp	(revision 41290)
+++ packages/fcl-registry/src/registry.pp	(working copy)
@@ -431,7 +431,7 @@
 Var
   Info : TRegDataInfo;
   ReadDataSize: Integer;
-  Data: string;
+  Data: UnicodeString;
 
 begin
   AList.Clear;
@@ -441,7 +441,7 @@
      If Not (Info.RegData in [rdMultiString]) then
        Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
      SetLength(Data,Info.DataSize);
-     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
+     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData);
      if ReadDataSize > 0 then
      begin
        // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
@@ -453,9 +453,9 @@
          if Data[ReadDataSize] = #0 then
            Dec(ReadDataSize);
        end;
-       SetLength(Data, ReadDataSize);
-       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
-       AList.Text := Data;
+       SetLength(Data, ReadDataSize div SizeOf(WideChar));
+       Data := UnicodeStringReplace(Data, #0, LineEnding, [rfReplaceAll]);
+       AList.Text := UTF8Encode(Data);
      end
    end
 end;
@@ -511,11 +511,15 @@
 procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
 
 Var
-  Data: string;
-
+  Data: UnicodeString;
+  Skip: Boolean;
 begin
-  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
-  PutData(Name, PChar(Data), Length(Data),rdMultiString);
+  Skip := List.SkipLastLineBreak;
+  List.SkipLastLineBreak := True;
+  Data := UnicodeString(StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]));
+  Data := Data + #0#0;
+  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
+  List.SkipLastLineBreak := Skip;
 end;
 
 procedure TRegistry.WriteFloat(const Name: string; Value: Double);
registry.stringlist.diff (1,935 bytes)

Cyrax

2019-02-11 13:53

reporter   ~0114033

Can't we use TBytes and TMBCSEncoding class to do conversion from UTF-16 to RTL codepage?

Bart Broersma

2019-02-11 14:34

reporter  

registry.stringlist.2.diff (2,023 bytes)
Index: packages/fcl-registry/src/registry.pp
===================================================================
--- packages/fcl-registry/src/registry.pp	(revision 41290)
+++ packages/fcl-registry/src/registry.pp	(working copy)
@@ -431,7 +431,7 @@
 Var
   Info : TRegDataInfo;
   ReadDataSize: Integer;
-  Data: string;
+  Data: UnicodeString;
 
 begin
   AList.Clear;
@@ -441,7 +441,7 @@
      If Not (Info.RegData in [rdMultiString]) then
        Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
      SetLength(Data,Info.DataSize);
-     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
+     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData) div SizeOf(WideChar);
      if ReadDataSize > 0 then
      begin
        // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
@@ -454,8 +454,8 @@
            Dec(ReadDataSize);
        end;
        SetLength(Data, ReadDataSize);
-       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
-       AList.Text := Data;
+       Data := UnicodeStringReplace(Data, #0, LineEnding, [rfReplaceAll]);
+       AList.Text := String(Data);
      end
    end
 end;
@@ -511,11 +511,15 @@
 procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
 
 Var
-  Data: string;
-
+  Data: UnicodeString;
+  Skip: Boolean;
 begin
-  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
-  PutData(Name, PChar(Data), Length(Data),rdMultiString);
+  Skip := List.SkipLastLineBreak;
+  List.SkipLastLineBreak := True;
+  Data := UnicodeString(StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]));
+  Data := Data + #0#0;
+  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
+  List.SkipLastLineBreak := Skip;
 end;
 
 procedure TRegistry.WriteFloat(const Name: string; Value: Double);
@@ -583,7 +587,7 @@
   Value: TStream): Integer;
 begin
   result:=-1; // unimplemented
- // 
+ //
 end;
 
 function TRegistryIniFile.ReadDate(const Section, Name: string;

Marco van de Voort

2019-02-12 11:53

manager   ~0114049

Hmm, I think a trimrightset(s,[0000013,0000010]) is nicer than the skiplastlinebreak stuff.

Mutating a by value class (even if you restore it), is not nice behaviour.

Bart Broersma

2019-02-23 17:17

reporter  

registry.stringlist.3.diff (2,119 bytes)
Index: packages/fcl-registry/src/registry.pp
===================================================================
--- packages/fcl-registry/src/registry.pp	(revision 41415)
+++ packages/fcl-registry/src/registry.pp	(working copy)
@@ -431,7 +431,7 @@
 Var
   Info : TRegDataInfo;
   ReadDataSize: Integer;
-  Data: string;
+  Data: UnicodeString;
 
 begin
   AList.Clear;
@@ -441,7 +441,7 @@
      If Not (Info.RegData in [rdMultiString]) then
        Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
      SetLength(Data,Info.DataSize);
-     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
+     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData) div SizeOf(WideChar);
      if ReadDataSize > 0 then
      begin
        // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
@@ -454,8 +454,8 @@
            Dec(ReadDataSize);
        end;
        SetLength(Data, ReadDataSize);
-       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
-       AList.Text := Data;
+       Data := UnicodeStringReplace(Data, #0, LineEnding, [rfReplaceAll]);
+       AList.Text := String(Data);
      end
    end
 end;
@@ -511,11 +511,15 @@
 procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
 
 Var
-  Data: string;
-
+  Data: UnicodeString;
+  S: String;
 begin
-  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
-  PutData(Name, PChar(Data), Length(Data),rdMultiString);
+  S := List.Text;
+  if (not List.SkipLastLineBreak) and (Copy(S, Length(S)-Length(LineEnding)+1, Length(LineEnding)) = LineEnding) then
+    System.Delete(S, Length(S)-Length(LineEnding)+1, Length(LineEnding));
+  Data := UnicodeString(StringReplace(S, LineEnding, #0, [rfReplaceAll]));
+  Data := Data + #0#0;
+  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
 end;
 
 procedure TRegistry.WriteFloat(const Name: string; Value: Double);
@@ -583,7 +587,7 @@
   Value: TStream): Integer;
 begin
   result:=-1; // unimplemented
- // 
+ //
 end;
 
 function TRegistryIniFile.ReadDate(const Section, Name: string;

Serge Anvarov

2019-02-24 02:21

reporter   ~0114379

Why is "LineEnding" used everywhere instead of "List.LineBreak"?

Serge Anvarov

2019-02-24 04:36

reporter   ~0114380

I think using the Text in List property always breaks the code page in strings. I. e. it is necessary to parse the list line by line and add the same way.
Patch and raw pp file attached.

Serge Anvarov

2019-02-24 04:36

reporter  

registry.stringlist.4.pp (2,243 bytes)
procedure TRegistry.ReadStringList(const Name: string; AList: TStrings);
// If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
// the size includes any terminating null character or characters
// unless the data was stored without them! (RegQueryValueEx @ MSDN)
const
  CSpaceFor2NullChars = 2 * SizeOf(WideChar);
var
  Info: TRegDataInfo;
  DataSize: Integer;
  PStart, PEnd, Buffer: PWideChar;
  ReadLength: Integer;
  U: UnicodeString;
begin
  if not Assigned(AList) then
    Exit;
  AList.Clear;
  if not GetDataInfo(Name, Info) or (Info.DataSize <= 0) then
    Exit;
  if Info.RegData <> rdMultiString then
    raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
  DataSize := Info.DataSize + CSpaceFor2NullChars;
  GetMem(Buffer, DataSize);
  try
    ReadLength := (GetData(Name, Buffer, DataSize, Info.RegData) +
      CSpaceFor2NullChars) div SizeOf(WideChar);
    // Always add last 2 terminal #0, we allocate space for it
    Buffer[ReadLength - 1] := #0;
    Buffer[ReadLength - 2] := #0;
    PStart := Buffer;
    while PStart^ <> #0 do
    begin
      PEnd := StrEnd(PStart);
      SetString(U, PStart, PEnd - PStart);
      AList.Append(UTF8Encode(U));
      PStart := PEnd + 1;
    end;
  finally
    FreeMem(Buffer);
  end;
end;

procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
var
  Buffer: PWideChar;
  U: UnicodeString;
  i, BufSize, Len: Integer;
  P: PWideChar;
begin
  if not Assigned(List) or (List.Count = 0) then
    Exit;
  BufSize := 0;
  for i := 0 to List.Count - 1 do
  begin
    U := UnicodeString(List[i]);
    if U <> '' then // Ignore empty lines
      Inc(BufSize, ByteLength(U) + SizeOf(WideChar)); // +1 = +#0
  end;
  Inc(BufSize, SizeOf(WideChar)); // Last #0
  GetMem(Buffer, BufSize);
  try
    P := Buffer;
    for i := 0 to List.Count - 1 do
    begin
      U := UnicodeString(List[i]);
      if U = '' then
        Continue;
      Len := Length(U) + 1;  // +1 = +#0 and U have last #0
      Move(PWideChar(U)^, P^, Len * SizeOf(WideChar));
      Inc(P, Len);
    end;
    P^ := #0;
    PutData(Name, Buffer, BufSize, rdMultiString);
  finally
    FreeMem(Buffer);
  end;
end;
registry.stringlist.4.pp (2,243 bytes)

Serge Anvarov

2019-02-24 04:37

reporter  

registry.stringlist.4.diff (3,821 bytes)
Index: packages/fcl-registry/src/registry.pp
===================================================================
--- packages/fcl-registry/src/registry.pp	(revision 41425)
+++ packages/fcl-registry/src/registry.pp	(working copy)
@@ -427,37 +427,44 @@
 end;
 
 procedure TRegistry.ReadStringList(const Name: string; AList: TStrings);
-
-Var
-  Info : TRegDataInfo;
-  ReadDataSize: Integer;
-  Data: string;
-
+// If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
+// the size includes any terminating null character or characters
+// unless the data was stored without them! (RegQueryValueEx @ MSDN)
+const
+  CSpaceFor2NullChars = 2 * SizeOf(WideChar);
+var
+  Info: TRegDataInfo;
+  DataSize: Integer;
+  PStart, PEnd, Buffer: PWideChar;
+  ReadLength: Integer;
+  U: UnicodeString;
 begin
+  if not Assigned(AList) then
+    Exit;
   AList.Clear;
-  GetDataInfo(Name,Info);
-  if info.datasize>0 then
+  if not GetDataInfo(Name, Info) or (Info.DataSize <= 0) then
+    Exit;
+  if Info.RegData <> rdMultiString then
+    raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
+  DataSize := Info.DataSize + CSpaceFor2NullChars;
+  GetMem(Buffer, DataSize);
+  try
+    ReadLength := (GetData(Name, Buffer, DataSize, Info.RegData) +
+      CSpaceFor2NullChars) div SizeOf(WideChar);
+    // Always add last 2 terminal #0, we allocate space for it
+    Buffer[ReadLength - 1] := #0;
+    Buffer[ReadLength - 2] := #0;
+    PStart := Buffer;
+    while PStart^ <> #0 do
     begin
-     If Not (Info.RegData in [rdMultiString]) then
-       Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
-     SetLength(Data,Info.DataSize);
-     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
-     if ReadDataSize > 0 then
-     begin
-       // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
-       // the size includes any terminating null character or characters
-       // unless the data was stored without them! (RegQueryValueEx @ MSDN)
-       if StringSizeIncludesNull then begin
-         if Data[ReadDataSize] = #0 then
-           Dec(ReadDataSize);
-         if Data[ReadDataSize] = #0 then
-           Dec(ReadDataSize);
-       end;
-       SetLength(Data, ReadDataSize);
-       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
-       AList.Text := Data;
-     end
-   end
+      PEnd := StrEnd(PStart);
+      SetString(U, PStart, PEnd - PStart);
+      AList.Append(UTF8Encode(U));
+      PStart := PEnd + 1;
+    end;
+  finally
+    FreeMem(Buffer);
+  end;
 end;
 
 function TRegistry.ReadTime(const Name: string): TDateTime;
@@ -509,13 +516,39 @@
 end;
 
 procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
-
-Var
-  Data: string;
-
+var
+  Buffer: PWideChar;
+  U: UnicodeString;
+  i, BufSize, Len: Integer;
+  P: PWideChar;
 begin
-  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
-  PutData(Name, PChar(Data), Length(Data),rdMultiString);
+  if not Assigned(List) or (List.Count = 0) then
+    Exit;
+  BufSize := 0;
+  for i := 0 to List.Count - 1 do
+  begin
+    U := UnicodeString(List[i]);
+    if U <> '' then // Ignore empty lines
+      Inc(BufSize, ByteLength(U) + SizeOf(WideChar)); // +1 = +#0
+  end;
+  Inc(BufSize, SizeOf(WideChar)); // Last #0
+  GetMem(Buffer, BufSize);
+  try
+    P := Buffer;
+    for i := 0 to List.Count - 1 do
+    begin
+      U := UnicodeString(List[i]);
+      if U = '' then
+        Continue;
+      Len := Length(U) + 1;  // +1 = +#0 and U have last #0
+      Move(PWideChar(U)^, P^, Len * SizeOf(WideChar));
+      Inc(P, Len);
+    end;
+    P^ := #0;
+    PutData(Name, Buffer, BufSize, rdMultiString);
+  finally
+    FreeMem(Buffer);
+  end;
 end;
 
 procedure TRegistry.WriteFloat(const Name: string; Value: Double);

Bart Broersma

2019-02-24 14:29

reporter  

registry.stringlist.5.diff (2,200 bytes)
Index: packages/fcl-registry/src/registry.pp
===================================================================
--- packages/fcl-registry/src/registry.pp	(revision 41415)
+++ packages/fcl-registry/src/registry.pp	(working copy)
@@ -431,7 +431,7 @@
 Var
   Info : TRegDataInfo;
   ReadDataSize: Integer;
-  Data: string;
+  Data: UnicodeString;
 
 begin
   AList.Clear;
@@ -441,7 +441,7 @@
      If Not (Info.RegData in [rdMultiString]) then
        Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
      SetLength(Data,Info.DataSize);
-     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
+     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData) div SizeOf(WideChar);
      if ReadDataSize > 0 then
      begin
        // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
@@ -454,8 +454,8 @@
            Dec(ReadDataSize);
        end;
        SetLength(Data, ReadDataSize);
-       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
-       AList.Text := Data;
+       Data := UnicodeStringReplace(Data, #0, List.LineBreak, [rfReplaceAll]);
+       AList.Text := String(Data);
      end
    end
 end;
@@ -511,11 +511,15 @@
 procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
 
 Var
-  Data: string;
-
+  Data: UnicodeString;
+  S: String;
 begin
-  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
-  PutData(Name, PChar(Data), Length(Data),rdMultiString);
+  S := List.Text;
+  if (not List.SkipLastLineBreak) and (Copy(S, Length(S)-Length(List.LineBreak)+1, Length(List.LineBreak)) = List.LineBreak) then
+    System.Delete(S, Length(S)-Length(List.LineBreak)+1, Length(List.LineBreak));
+  Data := UnicodeString(StringReplace(S, List.LineBreak, #0, [rfReplaceAll]));
+  Data := Data + #0#0;
+  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
 end;
 
 procedure TRegistry.WriteFloat(const Name: string; Value: Double);
@@ -583,7 +587,7 @@
   Value: TStream): Integer;
 begin
   result:=-1; // unimplemented
- // 
+ //
 end;
 
 function TRegistryIniFile.ReadDate(const Section, Name: string;

Serge Anvarov

2019-02-24 21:43

reporter   ~0114386

In the List.SetText the default user code page is used for new lines. The registry value may contain a value not in the default code page. And the result will be wrong. I attach reg file and demo to test.

And one more thing: in ReadStringList used AList, not List (compiler error)

Serge Anvarov

2019-02-24 21:43

reporter  

RegistryTest.zip (4,582 bytes)

Bart Broersma

2019-02-24 22:26

reporter  

registry.stringlist.6.diff (2,201 bytes)
Index: packages/fcl-registry/src/registry.pp
===================================================================
--- packages/fcl-registry/src/registry.pp	(revision 41415)
+++ packages/fcl-registry/src/registry.pp	(working copy)
@@ -431,7 +431,7 @@
 Var
   Info : TRegDataInfo;
   ReadDataSize: Integer;
-  Data: string;
+  Data: UnicodeString;
 
 begin
   AList.Clear;
@@ -441,7 +441,7 @@
      If Not (Info.RegData in [rdMultiString]) then
        Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
      SetLength(Data,Info.DataSize);
-     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
+     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData) div SizeOf(WideChar);
      if ReadDataSize > 0 then
      begin
        // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
@@ -454,8 +454,8 @@
            Dec(ReadDataSize);
        end;
        SetLength(Data, ReadDataSize);
-       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
-       AList.Text := Data;
+       Data := UnicodeStringReplace(Data, #0, AList.LineBreak, [rfReplaceAll]);
+       AList.Text := String(Data);
      end
    end
 end;
@@ -511,11 +511,15 @@
 procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
 
 Var
-  Data: string;
-
+  Data: UnicodeString;
+  S: String;
 begin
-  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
-  PutData(Name, PChar(Data), Length(Data),rdMultiString);
+  S := List.Text;
+  if (not List.SkipLastLineBreak) and (Copy(S, Length(S)-Length(List.LineBreak)+1, Length(List.LineBreak)) = List.LineBreak) then
+    System.Delete(S, Length(S)-Length(List.LineBreak)+1, Length(List.LineBreak));
+  Data := UnicodeString(StringReplace(S, List.LineBreak, #0, [rfReplaceAll]));
+  Data := Data + #0#0;
+  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
 end;
 
 procedure TRegistry.WriteFloat(const Name: string; Value: Double);
@@ -583,7 +587,7 @@
   Value: TStream): Integer;
 begin
   result:=-1; // unimplemented
- // 
+ //
 end;
 
 function TRegistryIniFile.ReadDate(const Section, Name: string;

Serge Anvarov

2019-02-25 17:12

reporter   ~0114414

[quote]it simply cannot be "translated" to[/quote], sorry but my patch do it.

Bart Broersma

2019-02-27 12:28

reporter  

registry.stringlist.7.diff (2,149 bytes)
Index: packages/fcl-registry/src/registry.pp
===================================================================
--- packages/fcl-registry/src/registry.pp	(revision 41415)
+++ packages/fcl-registry/src/registry.pp	(working copy)
@@ -430,8 +430,8 @@
 
 Var
   Info : TRegDataInfo;
-  ReadDataSize: Integer;
-  Data: string;
+  ReadDataSize, i: Integer;
+  Data: UnicodeString;
 
 begin
   AList.Clear;
@@ -441,7 +441,7 @@
      If Not (Info.RegData in [rdMultiString]) then
        Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
      SetLength(Data,Info.DataSize);
-     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
+     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData) div SizeOf(WideChar);
      if ReadDataSize > 0 then
      begin
        // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
@@ -454,7 +454,7 @@
            Dec(ReadDataSize);
        end;
        SetLength(Data, ReadDataSize);
-       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
+       Data := UnicodeStringReplace(Data, #0, AList.LineBreak, [rfReplaceAll]);
        AList.Text := Data;
      end
    end
@@ -511,11 +511,17 @@
 procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
 
 Var
-  Data: string;
-
+  Data: UnicodeString;
+  S, LLB: String;
 begin
-  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
-  PutData(Name, PChar(Data), Length(Data),rdMultiString);
+  S := List.Text;
+  LLB := List.LineBreak;
+  if (not List.SkipLastLineBreak) and (Copy(S, Length(S)-Length(LLB)+1, Length(LLB)) = LLB) then
+    System.Delete(S, Length(S)-Length(LLB)+1, Length(LLB));
+  Data := UnicodeString(StringReplace(S, List.LineBreak, #0, [rfReplaceAll]));
+  if StringSizeIncludesNull then
+    Data := Data + #0#0;
+  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
 end;
 
 procedure TRegistry.WriteFloat(const Name: string; Value: Double);
@@ -583,7 +589,7 @@
   Value: TStream): Integer;
 begin
   result:=-1; // unimplemented
- // 
+ //
 end;
 
 function TRegistryIniFile.ReadDate(const Section, Name: string;

Bart Broersma

2019-02-27 12:29

reporter  

registry.stringlist.7.utf8encode.diff (2,195 bytes)
Index: packages/fcl-registry/src/registry.pp
===================================================================
--- packages/fcl-registry/src/registry.pp	(revision 41415)
+++ packages/fcl-registry/src/registry.pp	(working copy)
@@ -430,8 +430,8 @@
 
 Var
   Info : TRegDataInfo;
-  ReadDataSize: Integer;
-  Data: string;
+  ReadDataSize, i: Integer;
+  Data: UnicodeString;
 
 begin
   AList.Clear;
@@ -441,7 +441,7 @@
      If Not (Info.RegData in [rdMultiString]) then
        Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
      SetLength(Data,Info.DataSize);
-     ReadDataSize := GetData(Name,PChar(Data),Info.DataSize,Info.RegData);
+     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData) div SizeOf(WideChar);
      if ReadDataSize > 0 then
      begin
        // If the data has the REG_SZ, REG_MULTI_SZ or REG_EXPAND_SZ type,
@@ -454,8 +454,8 @@
            Dec(ReadDataSize);
        end;
        SetLength(Data, ReadDataSize);
-       Data := StringReplace(Data, #0, LineEnding, [rfReplaceAll]);
-       AList.Text := Data;
+       Data := UnicodeStringReplace(Data, #0, AList.LineBreak, [rfReplaceAll]);
+       AList.Text := Utf8Encode(Data);
      end
    end
 end;
@@ -511,11 +511,17 @@
 procedure TRegistry.WriteStringList(const Name: string; List: TStrings);
 
 Var
-  Data: string;
-
+  Data: UnicodeString;
+  S, LLB: String;
 begin
-  Data := StringReplace(List.Text, LineEnding, #0, [rfReplaceAll]) + #0#0;
-  PutData(Name, PChar(Data), Length(Data),rdMultiString);
+  S := List.Text;
+  LLB := List.LineBreak;
+  if (not List.SkipLastLineBreak) and (Copy(S, Length(S)-Length(LLB)+1, Length(LLB)) = LLB) then
+    System.Delete(S, Length(S)-Length(LLB)+1, Length(LLB));
+  Data := UnicodeString(StringReplace(S, List.LineBreak, #0, [rfReplaceAll]));
+  if StringSizeIncludesNull then
+    Data := Data + #0#0;
+  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
 end;
 
 procedure TRegistry.WriteFloat(const Name: string; Value: Double);
@@ -583,7 +589,7 @@
   Value: TStream): Integer;
 begin
   result:=-1; // unimplemented
- // 
+ //
 end;
 
 function TRegistryIniFile.ReadDate(const Section, Name: string;

Serge Anvarov

2019-02-27 16:03

reporter   ~0114488

Bart, there is a simple point of view: I sent a working example of a short program, and a piece of registry. With it your patch works with an error! My without! The conclusion is obvious.

Now the details of why List.Text cannot be used.
1. GetText create Result with default codepage and append to it characters from items which may contain other code page. Without convertion!
2. SetText uses GetNextLine, which produces the same error as GetText.

Of course, we can assume that while there is no Unicode version of TStrings yet, or the GetText and SetText problem has not been fixed, that this is not the task of this patch. But I offer you a solution that is already working now! Just DON'T use the wrong functions.
Please note that it will automatically solve problems LineEnding and SkipLastLineBreak.

The remark about StringSizeIncludesNull is accepted, the fix is obvious (line P^:= #0 need with if).

Bart Broersma

2019-02-27 17:34

reporter   ~0114492

Last edited: 2019-02-27 17:50

View 4 revisions

I give up.
@some devel: please remove the patches I atached, they're rubbish.

Bart Broersma

2019-03-11 18:49

reporter   ~0114789

@serge: you want the returning stringlist to be utf8 encoded by default, I think it is wrong.
We are in the process of making the entire TRegistry Unicode proof.
Most likely by overloading all public and protected methods with UnicodeString parameters.

Since this is not possible for TStringList, and since Delphi does not have ReadStringList/WriteStringList, a boolean parameter can be added to the method, indicating wether the returned stringlist should be enoced as UTF8 or default system codepage (CP_ACP).
That would keep both Lazarus users and plain fpc users happy: they can uses Unicode (UTF8 in this case) if they need to, or simple CP_ACP if they don't.

In hindside ReadStringArray/WriteStringArray would have been better: it could have been overloaded with a UnicodeString version much more easily.

Bart Broersma

2019-03-11 18:52

reporter   ~0114790

Also note that it has been requested to solve all Unicode issues with TRegistry in one patch.

Bart Broersma

2019-03-13 14:06

reporter   ~0114814

Please continue in 0035213.

Michael Van Canneyt

2019-03-13 14:24

administrator   ~0114815

Please monitor the fixes in 0035213

Issue History

Date Modified Username Field Change
2019-01-15 12:32 CCRDude New Issue
2019-01-15 12:32 CCRDude File Added: reg-rdmultistring.patch
2019-01-15 13:39 Thaddy de Koning Note Added: 0113427
2019-01-15 13:41 Thaddy de Koning Note Edited: 0113427 View Revisions
2019-01-15 14:28 Thaddy de Koning Note Added: 0113428
2019-01-15 21:37 CCRDude Note Added: 0113430
2019-01-16 08:42 Thaddy de Koning Note Added: 0113435
2019-01-16 08:43 Thaddy de Koning Note Edited: 0113435 View Revisions
2019-02-11 13:38 Bart Broersma File Added: registry.stringlist.diff
2019-02-11 13:53 Cyrax Note Added: 0114033
2019-02-11 14:34 Bart Broersma File Added: registry.stringlist.2.diff
2019-02-12 11:53 Marco van de Voort Note Added: 0114049
2019-02-23 17:17 Bart Broersma File Added: registry.stringlist.3.diff
2019-02-24 02:21 Serge Anvarov Note Added: 0114379
2019-02-24 04:36 Serge Anvarov Note Added: 0114380
2019-02-24 04:36 Serge Anvarov File Added: registry.stringlist.4.pp
2019-02-24 04:37 Serge Anvarov File Added: registry.stringlist.4.diff
2019-02-24 14:29 Bart Broersma File Added: registry.stringlist.5.diff
2019-02-24 21:43 Serge Anvarov Note Added: 0114386
2019-02-24 21:43 Serge Anvarov File Added: RegistryTest.zip
2019-02-24 22:26 Bart Broersma File Added: registry.stringlist.6.diff
2019-02-25 17:12 Serge Anvarov Note Added: 0114414
2019-02-27 12:28 Bart Broersma File Added: registry.stringlist.7.diff
2019-02-27 12:29 Bart Broersma File Added: registry.stringlist.7.utf8encode.diff
2019-02-27 16:03 Serge Anvarov Note Added: 0114488
2019-02-27 17:34 Bart Broersma Note Added: 0114492
2019-02-27 17:41 Bart Broersma Note Edited: 0114492 View Revisions
2019-02-27 17:41 Bart Broersma Note Edited: 0114492 View Revisions
2019-02-27 17:50 Bart Broersma Note Edited: 0114492 View Revisions
2019-03-11 18:49 Bart Broersma Note Added: 0114789
2019-03-11 18:52 Bart Broersma Note Added: 0114790
2019-03-13 14:06 Bart Broersma Note Added: 0114814
2019-03-13 14:23 Michael Van Canneyt Relationship added related to 0035213
2019-03-13 14:24 Michael Van Canneyt Relationship replaced duplicate of 0035213
2019-03-13 14:24 Michael Van Canneyt Status new => resolved
2019-03-13 14:24 Michael Van Canneyt Resolution open => duplicate
2019-03-13 14:24 Michael Van Canneyt Assigned To => Michael Van Canneyt
2019-03-13 14:24 Michael Van Canneyt Note Added: 0114815