Unit Registry;

{$mode objfpc}
{$H+}

interface

{$ifndef windows}
{$define XMLREG}
{$endif}

Uses
  {$ifndef XMLREG}
    Windows,
  {$endif XMLREG}
    Classes,
    SysUtils,
    inifiles;

{$I regdef.inc}

type
  ERegistryException = class(Exception);

  TRegKeyInfo = record
    NumSubKeys: Integer;
    MaxSubKeyLen: Integer;
    NumValues: Integer;
    MaxValueLen: Integer;
    MaxDataLen: Integer;
    FileTime: TDateTime;
  end;

  TRegDataType = (rdUnknown, rdString, rdExpandString, rdBinary, rdInteger, rdIntegerBigEndian,
                  rdLink, rdMultiString, rdResourceList, rdFullResourceDescriptor,  rdResourceRequirementList, rdInt64);

  TRegDataInfo = record
    RegData: TRegDataType;
    DataSize: Integer;
  end;

  TUnicodeStringArray = Array of UnicodeString;

{ ---------------------------------------------------------------------
    TRegistry
  ---------------------------------------------------------------------}

  { TRegistry }

  TRegistry = class(TObject)
  private
    FLastError: Longint;
    FStringSizeIncludesNull : Boolean;
    FSysData : Pointer;
    fAccess: LongWord;
    fCurrentKey: HKEY;
    fRootKey: HKEY;
    fLazyWrite: Boolean;
    fCurrentPath: UnicodeString;
    function FixPath(APath: UnicodeString): UnicodeString;
    function GetLastErrorMsg: string;
    function RegMultiSzDataToUnicodeStringArray(U: UnicodeString): TUnicodeStringArray;
    function ListToArray(List: TStrings; IsUtf8: Boolean): TUnicodeStringArray;
    procedure ArrayToList(const Arr: TUnicodeStringArray; List: TStrings; ForceUtf8: Boolean);
    procedure SetRootKey(Value: HKEY);
    Procedure SysRegCreate;
    Procedure SysRegFree;
    Function  SysGetData(const Name: UnicodeString; Buffer: Pointer; BufSize: Integer; Out RegData: TRegDataType): Integer;
    Function  SysPutData(const Name: UnicodeString; Buffer: Pointer; BufSize: Integer; RegData: TRegDataType) : Boolean;
    Function  SysCreateKey(Key: UnicodeString): Boolean;
  protected
    function GetBaseKey(Relative: Boolean): HKey;
    function GetData(const Name: UnicodeString; Buffer: Pointer;
                  BufSize: Integer; Out RegData: TRegDataType): Integer;
    function GetData(const Name: String; Buffer: Pointer;
                  BufSize: Integer; Out RegData: TRegDataType): Integer;
    function GetKey(Key: UnicodeString): HKEY;
    function GetKey(Key: String): HKEY;
    procedure ChangeKey(Value: HKey; const Path: UnicodeString);
    procedure ChangeKey(Value: HKey; const Path: String);
    procedure PutData(const Name: UnicodeString; Buffer: Pointer;
                  BufSize: Integer; RegData: TRegDataType);
    procedure PutData(const Name: String; Buffer: Pointer;
                  BufSize: Integer; RegData: TRegDataType);
    procedure SetCurrentKey(Value: HKEY);
  public
    constructor Create; overload;
    constructor Create(aaccess:longword); overload;
    destructor Destroy; override;

    function CreateKey(const Key: UnicodeString): Boolean;
    function CreateKey(const Key: String): Boolean;
    function DeleteKey(const Key: UnicodeString): Boolean;
    function DeleteKey(const Key: String): Boolean;
    function DeleteValue(const Name: UnicodeString): Boolean;
    function DeleteValue(const Name: String): Boolean;
    function GetDataInfo(const ValueName: UnicodeString; Out Value: TRegDataInfo): Boolean;
    function GetDataInfo(const ValueName: String; Out Value: TRegDataInfo): Boolean;
    function GetDataSize(const ValueName: UnicodeString): Integer;
    function GetDataSize(const ValueName: String): Integer;
    function GetDataType(const ValueName: UnicodeString): TRegDataType;
    function GetDataType(const ValueName: String): TRegDataType;
    function GetKeyInfo(Out Value: TRegKeyInfo): Boolean;
    function HasSubKeys: Boolean;
    function KeyExists(const Key: UnicodeString): Boolean;
    function KeyExists(const Key: String): Boolean;
    function LoadKey(const Key, FileName: UnicodeString): Boolean;  unimplemented;
    function LoadKey(const Key, FileName: String): Boolean;  unimplemented;
    function OpenKey(const Key: UnicodeString; CanCreate: Boolean): Boolean;
    function OpenKey(const Key: String; CanCreate: Boolean): Boolean;
    function OpenKeyReadOnly(const Key: UnicodeString): Boolean;
    function OpenKeyReadOnly(const Key: String): Boolean;
    function ReadCurrency(const Name: UnicodeString): Currency;
    function ReadCurrency(const Name: String): Currency;
    function ReadBinaryData(const Name: UnicodeString; var Buffer; BufSize: Integer): Integer;
    function ReadBinaryData(const Name: String; var Buffer; BufSize: Integer): Integer;
    function ReadBool(const Name: UnicodeString): Boolean;
    function ReadBool(const Name: String): Boolean;
    function ReadDate(const Name: UnicodeString): TDateTime;
    function ReadDate(const Name: String): TDateTime;
    function ReadDateTime(const Name: UnicodeString): TDateTime;
    function ReadDateTime(const Name: String): TDateTime;
    function ReadFloat(const Name: UnicodeString): Double;
    function ReadFloat(const Name: String): Double;
    function ReadInteger(const Name: UnicodeString): Integer;
    function ReadInteger(const Name: String): Integer;
    function ReadInt64(const Name: UnicodeString): Int64;
    function ReadInt64(const Name: String): Int64;
    function ReadString(const Name: UnicodeString): UnicodeString;
    function ReadString(const Name: String): string;
    procedure ReadStringList(const Name: UnicodeString; AList: TStrings; ForceUtf8: Boolean=False);
    procedure ReadStringList(const Name: String; AList: TStrings);
    function ReadStringArray(const Name: UnicodeString): TUnicodeStringArray;
    function ReadStringArray(const Name: String): TStringArray;
    function ReadTime(const Name: UnicodeString): TDateTime;
    function ReadTime(const Name: String): TDateTime;
    function RegistryConnect(const UNCName: UnicodeString): Boolean;
    function RegistryConnect(const UNCName: String): Boolean;
    function ReplaceKey(const Key, FileName, BackUpFileName: UnicodeString): Boolean; unimplemented;
    function ReplaceKey(const Key, FileName, BackUpFileName: String): Boolean;  unimplemented;
    function RestoreKey(const Key, FileName: UnicodeString): Boolean;  unimplemented;
    function RestoreKey(const Key, FileName: String): Boolean;  unimplemented;
    function SaveKey(const Key, FileName: UnicodeString): Boolean;
    function SaveKey(const Key, FileName: String): Boolean;
    function UnLoadKey(const Key: UnicodeString): Boolean;
    function UnLoadKey(const Key: String): Boolean;
    function ValueExists(const Name: UnicodeString): Boolean;
    function ValueExists(const Name: String): Boolean;

    procedure CloseKey;
    procedure CloseKey(key:HKEY);
    procedure GetKeyNames(Strings: TStrings);
    function GetKeyNames: TUnicodeStringArray;
    procedure GetValueNames(Strings: TStrings);
    //ToDo
    function GetValueNames: TUnicodeStringArray;
    procedure MoveKey(const OldName, NewName: UnicodeString; Delete: Boolean);  unimplemented;
    procedure MoveKey(const OldName, NewName: String; Delete: Boolean);  unimplemented;
    procedure RenameValue(const OldName, NewName: UnicodeString);
    procedure RenameValue(const OldName, NewName: String);
    procedure WriteCurrency(const Name: UnicodeString; Value: Currency);
    procedure WriteCurrency(const Name: String; Value: Currency);
    procedure WriteBinaryData(const Name: UnicodeString; const Buffer; BufSize: Integer);
    procedure WriteBinaryData(const Name: String; const Buffer; BufSize: Integer);
    procedure WriteBool(const Name: UnicodeString; Value: Boolean);
    procedure WriteBool(const Name: String; Value: Boolean);
    procedure WriteDate(const Name: UnicodeString; Value: TDateTime);
    procedure WriteDate(const Name: String; Value: TDateTime);
    procedure WriteDateTime(const Name: UnicodeString; Value: TDateTime);
    procedure WriteDateTime(const Name: String; Value: TDateTime);
    procedure WriteFloat(const Name: UnicodeString; Value: Double);
    procedure WriteFloat(const Name: String; Value: Double);
    procedure WriteInteger(const Name: UnicodeString; Value: Integer);
    procedure WriteInteger(const Name: String; Value: Integer);
    procedure WriteInt64(const Name: UnicodeString; Value: Int64);
    procedure WriteInt64(const Name: String; Value: Int64);
    procedure WriteString(const Name, Value: UnicodeString);
    procedure WriteString(const Name, Value: String);
    procedure WriteExpandString(const Name, Value: UnicodeString);
    procedure WriteExpandString(const Name, Value: String);
    procedure WriteStringList(const Name: UnicodeString; List: TStrings; IsUtf8: Boolean=False);
    procedure WriteStringArray(const Name: UnicodeString; const Arr: TUnicodeStringArray);
    procedure WriteStringArray(const Name: String; const Arr: TStringArray);
    procedure WriteTime(const Name: UnicodeString; Value: TDateTime);
    procedure WriteTime(const Name: String; Value: TDateTime);

    property Access: LongWord read fAccess write fAccess;
    property CurrentKey: HKEY read fCurrentKey;
    property CurrentPath: UnicodeString read fCurrentPath;
    property LazyWrite: Boolean read fLazyWrite write fLazyWrite;
    property RootKey: HKEY read fRootKey write SetRootKey;
    Property StringSizeIncludesNull : Boolean read FStringSizeIncludesNull;
    property LastError: Longint read FLastError; platform;
    property LastErrorMsg: string read GetLastErrorMsg; platform;
  end;

{ ---------------------------------------------------------------------
    TRegIniFile
  ---------------------------------------------------------------------}
  TRegIniFile = class(TRegistry)
  private
    fFileName          : String;
    fPath              : String;
    fPreferStringValues: Boolean;
    function OpenSection(const Section: string; CreateSection : Boolean = false): boolean;
    procedure CloseSection;
  public
    constructor Create(const FN: string); overload;
    constructor Create(const FN: string;aaccess:longword); overload;
    function ReadString(const Section, Ident, Default: string): string;
    function ReadInteger(const Section, Ident: string; Default: Longint): Longint;
    function ReadBool(const Section, Ident: string; Default: Boolean): Boolean;
    function ReadDate(const Section, Ident: string; Default: TDateTime):TDateTime;
    function ReadDateTime(const Section, Ident: string; Default: TDateTime):TDateTime;
    function ReadTime(const Section, Ident: string; Default: TDateTime):TDateTime;
    function ReadFloat(const Section, Ident: string; Default: Double): Double;

    procedure WriteString(const Section, Ident, Value: String);
    procedure WriteInteger(const Section, Ident: string; Value: Longint);
    procedure WriteBool(const Section, Ident: string; Value: Boolean);
    procedure WriteDate(const Section, Ident: string; Value: TDateTime);
    procedure WriteDateTime(const Section, Ident: string; Value: TDateTime);
    procedure WriteTime(const Section, Ident: string; Value: TDateTime);
    procedure WriteFloat(const Section, Ident: string; Value: Double);
    procedure ReadSection(const Section: string; Strings: TStrings);
    procedure ReadSections(Strings: TStrings);
    procedure ReadSectionValues(const Section: string; Strings: TStrings);
    procedure EraseSection(const Section: string);
    procedure DeleteKey(const Section, Ident: String);

    property FileName: String read fFileName;
    property PreferStringValues: Boolean read fPreferStringValues
                write fPreferStringValues;
  end{$ifdef XMLREG}deprecated 'Use TRegistry instead. Will be removed in 4.0'{$endif} platform; 

{ ---------------------------------------------------------------------
    TRegIniFile
  ---------------------------------------------------------------------}


  TRegistryIniFile = class(TCustomIniFile)
  private
    FRegIniFile: TRegIniFile;
  public
    constructor Create(const AFileName: string); overload;
    constructor Create(const AFileName: string; AAccess: LongWord); overload;
    destructor destroy; override;
    function ReadDate(const Section, Name: string; Default: TDateTime): TDateTime; override;
    function ReadDateTime(const Section, Name: string; Default: TDateTime): TDateTime; override;
    function ReadInteger(const Section, Name: string; Default: Longint): Longint; override;
    function ReadFloat(const Section, Name: string; Default: Double): Double; override;
    function ReadString(const Section, Name, Default: string): string; override;
    function ReadTime(const Section, Name: string; Default: TDateTime): TDateTime; override;
    function ReadBinaryStream(const Section, Name: string; Value: TStream): Integer; override; unimplemented;
    procedure WriteDate(const Section, Name: string; Value: TDateTime); override;
    procedure WriteDateTime(const Section, Name: string; Value: TDateTime); override;
    procedure WriteFloat(const Section, Name: string; Value: Double); override;
    procedure WriteInteger(const Section, Name: string; Value: Longint); override;
    procedure WriteString(const Section, Name, Value: String); override;
    procedure WriteTime(const Section, Name: string; Value: TDateTime); override;
    procedure WriteBinaryStream(const Section, Name: string; Value: TStream); override;
    procedure ReadSection(const Section: string; Strings: TStrings); override;
    procedure ReadSections(Strings: TStrings); override;
    procedure ReadSectionValues(const Section: string; Strings: TStrings); override;
    procedure EraseSection(const Section: string); override;
    procedure DeleteKey(const Section, Name: String); override;
    procedure UpdateFile; override;
    function ValueExists(const Section, Ident: string): Boolean; override;
    property RegIniFile: TRegIniFile read FRegIniFile;
  end{$ifdef XMLREG}deprecated 'Use TRegistry instead. Will be removed in 4.0'{$endif} platform; 

ResourceString
  SInvalidRegType   = 'Invalid registry data type: "%s"';
  SRegCreateFailed  = 'Failed to create key: "%s"';
  SRegSetDataFailed = 'Failed to set data for value "%s"';
  SRegGetDataFailed = 'Failed to get data for value "%s"';

var
  GlobalXMLFile : Boolean = False;

implementation

{ ---------------------------------------------------------------------
    Include implementation-dependent code
  ---------------------------------------------------------------------}


{$ifdef XMLREG}
{$i xregreg.inc}
{$else}
{$i winreg.inc}
{$endif}

{ ---------------------------------------------------------------------
    Generic, implementation-independent code.
  ---------------------------------------------------------------------}

{$ifdef DebugRegistry}
function DbgS(const S: UnicodeString): String;
var
  C: WideChar;
begin
  Result := '';
  for C in S do Result := Result + IntToHex(Word(C),4) + #32;
  Result := TrimRight(Result);
end;
{$endif}

constructor TRegistry.Create;

begin
  inherited Create;
  FAccess     := KEY_ALL_ACCESS;
  FRootKey    := HKEY_CURRENT_USER;
  FLazyWrite  := True;
  FCurrentKey := 0;
  SysRegCreate;
end;

constructor TRegistry.Create(aaccess: longword);

begin
  Create;
  FAccess     := aaccess;
end;

destructor TRegistry.Destroy;
begin
  CloseKey;
  SysRegFree;
  inherited Destroy;
end;

function TRegistry.CreateKey(const Key: UnicodeString): Boolean;

begin
  Result:=SysCreateKey(Key);
  If Not Result Then
    Raise ERegistryException.CreateFmt(SRegCreateFailed, [Key]);
end;

function TRegistry.CreateKey(const Key: String): Boolean;
begin
  Result:=CreateKey(UnicodeString(Key));
end;

function TRegistry.DeleteKey(const Key: String): Boolean;
begin
  Result:=DeleteKey(UnicodeString(Key));
end;

function TRegistry.DeleteValue(const Name: String): Boolean;
begin
  Result:=DeleteValue(UnicodeString(Name));
end;

function TRegistry.GetDataInfo(const ValueName: String; out Value: TRegDataInfo
  ): Boolean;
begin
  Result:=GetDataInfo(UnicodeString(ValueName), Value);
end;

function TRegistry.GetBaseKey(Relative: Boolean): HKey;
begin
  If Relative and (CurrentKey<>0) Then
    Result := CurrentKey
  else
    Result := RootKey;
end;

function TRegistry.GetData(const Name: UnicodeString; Buffer: Pointer; BufSize: Integer; out RegData: TRegDataType): Integer;
begin
  Result:=SysGetData(Name,Buffer,BufSize,RegData);
  If (Result=-1) then
    Raise ERegistryException.CreateFmt(SRegGetDataFailed, [Name]);
end;

function TRegistry.GetData(const Name: String; Buffer: Pointer;
  BufSize: Integer; out RegData: TRegDataType): Integer;
begin
  Result:=GetData(UnicodeString(Name), Buffer, BufSize, RegData);
end;

function TRegistry.GetKey(Key: String): HKEY;
begin
  Result:=GetKey(UnicodeString(Key));
end;

procedure TRegistry.ChangeKey(Value: HKey; const Path: String);
begin
  ChangeKey(Value, UnicodeString(Path));
end;


procedure TRegistry.PutData(const Name: UnicodeString; Buffer: Pointer;
  BufSize: Integer; RegData: TRegDataType);

begin
  If Not SysPutData(Name,Buffer,BufSize,RegData) then
    Raise ERegistryException.CreateFmt(SRegSetDataFailed, [Name]);
end;

procedure TRegistry.PutData(const Name: String; Buffer: Pointer;
  BufSize: Integer; RegData: TRegDataType);
begin
  PutData(UnicodeString(Name), Buffer, BufSize, RegData);
end;


function TRegistry.GetDataSize(const ValueName: UnicodeString): Integer;

Var
  Info: TRegDataInfo;

begin
  If GetDataInfo(ValueName,Info) Then
    Result := Info.DataSize
  else
    Result := -1;
end;

function TRegistry.GetDataSize(const ValueName: String): Integer;
begin
  Result:=GetDataSize(UnicodeString(ValueName));
end;

function TRegistry.GetDataType(const ValueName: UnicodeString): TRegDataType;

Var
  Info: TRegDataInfo;

begin
  GetDataInfo(ValueName, Info);
  Result:=Info.RegData;
end;

function TRegistry.GetDataType(const ValueName: String): TRegDataType;
begin
  Result:=GetDataType(UnicodeString(ValueName));
end;


function TRegistry.KeyExists(const Key: String): Boolean;
begin
  Result:=KeyExists(UnicodeString(Key));
end;

function TRegistry.LoadKey(const Key, FileName: String): Boolean;
begin
  Result:=LoadKey(UnicodeString(Key), UnicodeString(FileName));
end;

function TRegistry.OpenKey(const Key: String; CanCreate: Boolean): Boolean;
begin
  Result:=OpenKey(UnicodeString(Key), CanCreate);
end;

function TRegistry.OpenKeyReadOnly(const Key: String): Boolean;
begin
  Result:=OpenKeyReadOnly(UnicodeString(Key));
end;

function TRegistry.HasSubKeys: Boolean;

Var
  Info : TRegKeyInfo;

begin
  Result:=GetKeyInfo(Info);
  If Result then
    Result:=(Info.NumSubKeys>0);
end;

function TRegistry.ReadBinaryData(const Name: UnicodeString; var Buffer; BufSize: Integer): Integer;

Var
  RegDataType: TRegDataType;

begin
  Result := GetData(Name, @Buffer, BufSize, RegDataType);
end;

function TRegistry.ReadBinaryData(const Name: String; var Buffer;
  BufSize: Integer): Integer;
begin
  Result:=ReadBinaryData(UnicodeString(Name), Buffer, BufSize);
end;

function TRegistry.ReadInteger(const Name: UnicodeString): Integer;

Var
  RegDataType: TRegDataType;

begin
  GetData(Name, @Result, SizeOf(Integer), RegDataType);
  If RegDataType<>rdInteger Then
    Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
end;

function TRegistry.ReadInteger(const Name: String): Integer;
begin
  Result:=ReadInteger(UnicodeString(Name));
end;

function TRegistry.ReadInt64(const Name: UnicodeString): Int64;

Var
  RegDataType: TRegDataType;

begin
  GetData(Name, @Result, SizeOf(Int64), RegDataType);
  If RegDataType<>rdInt64 Then
    Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
end;

function TRegistry.ReadInt64(const Name: String): Int64;
begin
  Result:=ReadInt64(UnicodeString(Name));
end;

function TRegistry.ReadBool(const Name: UnicodeString): Boolean;

begin
  Result:=ReadInteger(Name)<>0;
end;

function TRegistry.ReadBool(const Name: String): Boolean;
begin
  Result:=ReadBool(UnicodeString(Name));
end;

function TRegistry.ReadCurrency(const Name: UnicodeString): Currency;

begin
  Result:=Default(Currency);
  ReadBinaryData(Name, Result, SizeOf(Currency));
end;

function TRegistry.ReadCurrency(const Name: String): Currency;
begin
  Result:=ReadCurrency(UnicodeString(Name));
end;

function TRegistry.ReadDate(const Name: UnicodeString): TDateTime;

begin
  Result:=Trunc(ReadDateTime(Name));
end;

function TRegistry.ReadDate(const Name: String): TDateTime;
begin
  Result:=ReadDate(UnicodeString(Name));
end;

function TRegistry.ReadDateTime(const Name: UnicodeString): TDateTime;

begin
  Result:=Default(TDateTime);
  ReadBinaryData(Name, Result, SizeOf(TDateTime));
end;

function TRegistry.ReadDateTime(const Name: String): TDateTime;
begin
  Result:=ReadDateTime(UnicodeString(Name));
end;

function TRegistry.ReadFloat(const Name: UnicodeString): Double;

begin
  Result:=Default(Double);
  ReadBinaryData(Name,Result,SizeOf(Double));
end;

function TRegistry.ReadFloat(const Name: String): Double;
begin
  Result:=ReadFloat(UnicodeString(Name));
end;

function TRegistry.ReadString(const Name: UnicodeString): UnicodeString;

Var
  Info : TRegDataInfo;
  ReadDataSize: Integer;
  u: UnicodeString;

begin
  Result:='';
  GetDataInfo(Name,Info);
  if info.datasize>0 then
  begin
    if Not (Info.RegData in [rdString,rdExpandString]) then
      Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
    if Odd(Info.DataSize) then
      SetLength(u,round((Info.DataSize+1)/SizeOf(UnicodeChar)))
    else
      SetLength(u,round(Info.DataSize/SizeOf(UnicodeChar)));
    ReadDataSize := GetData(Name,@u[1],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 and
         (u[Length(u)] = WideChar(0)) then
        SetLength(u,Length(u)-1);
      Result:=u;
    end;
  end;
end;

function TRegistry.ReadString(const Name: String): string;
begin
  Result:=ReadString(UnicodeString(Name));
end;


procedure TRegistry.ReadStringList(const Name: UnicodeString; AList: TStrings; ForceUtf8: Boolean=False);

Var
  UArr: TUnicodeStringArray;

begin
  UArr := ReadStringArray(Name);
  ArrayToList(UArr, AList, ForceUtf8);
end;

procedure TRegistry.ReadStringList(const Name: String; AList: TStrings);
begin
  ReadStringList(UnicodeString(Name), AList);
end;

function TRegistry.FixPath(APath: UnicodeString): UnicodeString;
const
  Delim={$ifdef XMLREG}'/'{$else}'\'{$endif};
begin
  //At this point we know the path is valid, since this is only called after OpenKey succeeded
  //Just sanitize it
  while (Pos(Delim+Delim,APath) > 0) do
    APath := UnicodeStringReplace(APath, Delim+Delim,Delim,[rfReplaceAll]);
  if (Length(APath) > 1) and (APath[Length(APath)] = Delim) then
    System.Delete(APath, Length(APath), 1);
  Result := APath;
end;

function TRegistry.RegMultiSzDataToUnicodeStringArray(U: UnicodeString): TUnicodeStringArray;
var
  Len, i, p: Integer;
  Sub: UnicodeString;
begin
  Result := nil;
  if (U = '') then Exit;
  Len := 1;
  for i := 1 to Length(U) do if (U[i] = #0) then Inc(Len);
  SetLength(Result, Len);
  i := 0;

  while (U <> '') and (i < Length(Result)) do
  begin
    p := Pos(#0, U);
    if (p = 0) then p := Length(U) + 1;
    Sub := Copy(U, 1, p - 1);
    Result[i] := Sub;
    System.Delete(U, 1, p);
    Inc(i);
  end;
end;

function TRegistry.ListToArray(List: TStrings; IsUtf8: Boolean): TUnicodeStringArray;
var
  i, curr, Len: Integer;
  u: UnicodeString;
begin
  Result := nil;
  Len := List.Count;
  SetLength(Result, Len);
  //REG_MULTI_SZ data cannot contain empty strings
  curr := 0;
  for i := 0 to List.Count - 1 do
  begin
    if IsUtf8 then
      u := Utf8Decode(List[i])
    else
      u := List[i];
    if (u>'') then
    begin
      Result[curr] := u;
      inc(curr);
    end
    else
      Dec(Len);
  end;
  if (Len <> List.Count) then SetLength(Result, Len);
end;

procedure TRegistry.ArrayToList(const Arr: TUnicodeStringArray; List: TStrings; ForceUtf8: Boolean);
var
  i: Integer;
begin
  List.Clear;
  for i := Low(Arr) to High(Arr) do
  begin
    if ForceUtf8 then
      List.Add(Utf8Encode(Arr[i]))
    else
      List.Add(String(Arr[i]));
  end;
end;

function TRegistry.ReadStringArray(const Name: UnicodeString): TUnicodeStringArray;
Var
  Info : TRegDataInfo;
  ReadDataSize: Integer;
  Data: UnicodeString;

begin
  Result := nil;
  GetDataInfo(Name,Info);
  //writeln('TRegistry.ReadStringArray: datasize=',info.datasize);
  if info.datasize>0 then
    begin
     If Not (Info.RegData in [rdMultiString]) then
       Raise ERegistryException.CreateFmt(SInvalidRegType, [Name]);
     SetLength(Data,Info.DataSize);
     ReadDataSize := GetData(Name,PWideChar(Data),Info.DataSize,Info.RegData) div SizeOf(WideChar);
     //writeln('TRegistry.ReadStringArray: ReadDataSize=',ReadDataSize);
     if ReadDataSize > 0 then
     begin
       // Windows returns the data with or without trailing zero's, so just strip all trailing null characters
        while (Data[ReadDataSize] = #0) do Dec(ReadDataSize);
       SetLength(Data, ReadDataSize);
       //writeln('Data=',dbgs(data));
       //Data := UnicodeStringReplace(Data, #0, AList.LineBreak, [rfReplaceAll]);
       //AList.Text := Data;
       Result := RegMultiSzDataToUnicodeStringArray(Data);
     end
   end
end;

function TRegistry.ReadStringArray(const Name: String): TStringArray;
var
  UArr: TUnicodeStringArray;
  i: Integer;
begin
  Result := nil;
  UArr := ReadStringArray(UnicodeString(Name));
  SetLength(Result, Length(UArr));
  for i := Low(UArr) to High(UArr) do Result[i] := UArr[i];
end;

function TRegistry.ReadTime(const Name: UnicodeString): TDateTime;

begin
  Result:=Frac(ReadDateTime(Name));
end;

function TRegistry.ReadTime(const Name: String): TDateTime;
begin
  Result:=ReadTime(UnicodeString(Name));
end;

function TRegistry.RegistryConnect(const UNCName: String): Boolean;
begin
  Result:=RegistryConnect(UnicodeString(UNCName));
end;

function TRegistry.ReplaceKey(const Key, FileName, BackUpFileName: String): Boolean;
begin
  Result:=ReplaceKey(UnicodeString(Key), UnicodeString(FileName), UnicodeString(BackUpFileName))
end;

function TRegistry.RestoreKey(const Key, FileName: String): Boolean;
begin
  Result:=RestoreKey(UnicodeString(Key), UnicodeString(FileName));
end;

function TRegistry.SaveKey(const Key, FileName: String): Boolean;
begin
  Result:=SaveKey(UnicodeString(Key), UnicodeString(FileName));
end;

function TRegistry.UnLoadKey(const Key: String): Boolean;
begin
  Result:=UnloadKey(UnicodeString(Key));
end;

function TRegistry.ValueExists(const Name: String): Boolean;
begin
  Result:=ValueExists(UnicodeString(Name));
end;

procedure TRegistry.WriteBinaryData(const Name: UnicodeString; const Buffer; BufSize: Integer);
begin
  PutData(Name, @Buffer, BufSize, rdBinary);
end;

procedure TRegistry.WriteBinaryData(const Name: String; const Buffer;
  BufSize: Integer);
begin
  WriteBinaryData(UnicodeString(Name), Buffer, BufSize);
end;

procedure TRegistry.WriteBool(const Name: UnicodeString; Value: Boolean);

begin
  WriteInteger(Name,Ord(Value));
end;

procedure TRegistry.WriteBool(const Name: String; Value: Boolean);
begin
  WriteBool(UnicodeString(Name), Value);
end;

procedure TRegistry.WriteCurrency(const Name: UnicodeString; Value: Currency);
begin
  WriteBinaryData(Name, Value, SizeOf(Currency));
end;

procedure TRegistry.WriteCurrency(const Name: String; Value: Currency);
begin
  WriteCurrency(UnicodeString(Name), Value);
end;

procedure TRegistry.WriteDate(const Name: UnicodeString; Value: TDateTime);
begin
  WriteBinarydata(Name, Value, SizeOf(TDateTime));
end;

procedure TRegistry.WriteDate(const Name: String; Value: TDateTime);
begin
  WriteDate(UnicodeString(Name), Value);
end;

procedure TRegistry.WriteTime(const Name: UnicodeString; Value: TDateTime);
begin
  WriteBinaryData(Name, Value, SizeOf(TDateTime));
end;

procedure TRegistry.WriteTime(const Name: String; Value: TDateTime);
begin
  WriteTime(UnicodeString(Name), Value);
end;

procedure TRegistry.WriteDateTime(const Name: UnicodeString; Value: TDateTime);
begin
  WriteBinaryData(Name, Value, SizeOf(TDateTime));
end;

procedure TRegistry.WriteDateTime(const Name: String; Value: TDateTime);
begin
  WriteDateTime(UnicodeString(Name), Value);
end;

procedure TRegistry.WriteExpandString(const Name, Value: UnicodeString);
begin
  PutData(Name, PWideChar(Value), ByteLength(Value), rdExpandString);
end;

procedure TRegistry.WriteExpandString(const Name, Value: String);
begin
  WriteExpandString(UnicodeString(Name), UnicodeString(Value));
end;


procedure TRegistry.WriteStringList(const Name: UnicodeString; List: TStrings; IsUtf8: Boolean=False);

Var
  UArr: TUnicodeStringArray;
begin
  UArr := ListToArray(List, IsUtf8);
  WriteStringArray(Name, UArr);
end;

procedure TRegistry.WriteStringArray(const Name: UnicodeString; const Arr: TUnicodeStringArray);
Var
  Data: UnicodeString;
  u: UnicodeString;
  i: Integer;
begin
  Data := '';
  //REG_MULTI_SZ data cannot contain empty strings
  for i := Low(Arr) to High(Arr) do
  begin
    u := Arr[i];
    if (u>'') then
    begin
      if (Data>'') then
        Data := Data + #0 + u
      else
        Data := Data + u;
    end;
  end;
  if StringSizeIncludesNull then
    Data := Data + #0#0;
  //writeln('Data=',Dbgs(Data));
  PutData(Name, PWideChar(Data), ByteLength(Data), rdMultiString);
end;

procedure TRegistry.WriteStringArray(const Name: String; const Arr: TStringArray);
var
  UArr: TUnicodeStringArray;
  i: Integer;
begin
  UArr := nil;
  SetLength(UArr, Length(Arr));
  for i := Low(Arr) to High(Arr) do UArr[i] := Arr[i];
  WriteStringArray(UnicodeString(Name), UArr);
end;

procedure TRegistry.WriteFloat(const Name: UnicodeString; Value: Double);
begin
  WriteBinaryData(Name, Value, SizeOf(Double));
end;

procedure TRegistry.WriteFloat(const Name: String; Value: Double);
begin
  WriteFloat(UnicodeString(Name), Value);
end;

procedure TRegistry.WriteInteger(const Name: UnicodeString; Value: Integer);
begin
  PutData(Name, @Value, SizeOf(Integer), rdInteger);
end;

procedure TRegistry.WriteInteger(const Name: String; Value: Integer);
begin
  WriteInteger(UnicodeString(Name), Value);
end;

procedure TRegistry.WriteInt64(const Name: UnicodeString; Value: Int64);
begin
  PutData(Name, @Value, SizeOf(Int64), rdInt64);
end;

procedure TRegistry.WriteInt64(const Name: String; Value: Int64);
begin
  WriteInt64(UnicodeString(Name), Value);
end;

procedure TRegistry.WriteString(const Name, Value: UnicodeString);
begin
  PutData(Name, PWideChar(Value), ByteLength(Value), rdString);
end;

procedure TRegistry.WriteString(const Name, Value: String);
begin
  WriteString(UnicodeString(Name), UnicodeString(Value));
end;

procedure TRegistry.GetKeyNames(Strings: TStrings);
var
  UArr: TUnicodeStringArray;
begin
  UArr := GetKeyNames;
  ArrayToList(UArr, Strings, True);
end;

procedure TRegistry.GetValueNames(Strings: TStrings);
var
  UArr: TUnicodeStringArray;
begin
  UArr := GetValueNames;
  ArrayToList(UArr, Strings, True);
end;

procedure TRegistry.MoveKey(const OldName, NewName: UnicodeString; Delete: Boolean);
begin

end;

procedure TRegistry.MoveKey(const OldName, NewName: String; Delete: Boolean);
begin
  MoveKey(UnicodeString(OldName), UnicodeString(NewName), Delete);
end;

procedure TRegistry.RenameValue(const OldName, NewName: String);
begin
  RenameValue(UnicodeString(OldName), UnicodeString(NewName));
end;

{ ---------------------------------------------------------------------
    Include TRegIniFile implementation
  ---------------------------------------------------------------------}

{$i regini.inc}

{ TRegistryIniFile }

// interface from
// http://www.koders.com/delphi/fid65C1FFAEF89B0CDC4B93FF94C1819686CA6141FC.aspx
constructor TRegistryIniFile.Create(const AFileName: string;
  AAccess: LongWord);
begin
  inherited create(AFilename);
  FRegInifile:=TreginiFile.Create(AFileName,AAccess);
end;

constructor TRegistryIniFile.Create(const AFileName: string);
begin
  Create(AFileName,KEY_ALL_ACCESS);
end;

destructor TRegistryIniFile.destroy;

begin
  FreeAndNil(FRegInifile);
  Inherited;
end;

procedure TRegistryIniFile.DeleteKey(const Section, Name: String);
begin
  FRegIniFile.Deletekey(section,name);
end;

procedure TRegistryIniFile.EraseSection(const Section: string);
begin
  FRegIniFile.EraseSection(section);
end;

function TRegistryIniFile.ReadBinaryStream(const Section, Name: string;
  Value: TStream): Integer;
begin
  result:=-1; // unimplemented
 //
end;

function TRegistryIniFile.ReadDate(const Section, Name: string;
  Default: TDateTime): TDateTime;
begin
  Result:=FRegInifile.ReadDate(Section,Name,Default);
end;

function TRegistryIniFile.ReadDateTime(const Section, Name: string;
  Default: TDateTime): TDateTime;
begin
  Result:=FRegInifile.ReadDateTime(Section,Name,Default);
end;

function TRegistryIniFile.ReadFloat(const Section, Name: string;
  Default: Double): Double;
begin
  Result:=FRegInifile.ReadFloat(Section,Name,Default);
end;

function TRegistryIniFile.ReadInteger(const Section, Name: string;
  Default: Integer): Longint;
begin
  Result:=FRegInifile.ReadInteger(Section, Name, Default);
end;

procedure TRegistryIniFile.ReadSection(const Section: string; Strings: TStrings);
begin
  FRegIniFile.ReadSection(Section,strings);
end;

procedure TRegistryIniFile.ReadSections(Strings: TStrings);
begin
  FRegIniFile.ReadSections(strings);
end;

procedure TRegistryIniFile.ReadSectionValues(const Section: string;
  Strings: TStrings);
begin
  FRegIniFile.ReadSectionValues(Section,strings);
end;

function TRegistryIniFile.ReadString(const Section, Name,
  Default: string): string;
begin
  Result:=FRegInifile.ReadString(Section, Name, Default);
end;

function TRegistryIniFile.ReadTime(const Section, Name: string;
  Default: TDateTime): TDateTime;
begin
  Result:=FRegInifile.ReadTime(Section,Name,Default);
end;

procedure TRegistryIniFile.UpdateFile;
begin
//  FRegIniFile.UpdateFile; ??
end;

procedure TRegistryIniFile.WriteBinaryStream(const Section, Name: string;
  Value: TStream);
begin
 // ??
end;

procedure TRegistryIniFile.WriteDate(const Section, Name: string;
  Value: TDateTime);
begin
  FRegInifile.WriteDate(Section,Name, Value);
end;

procedure TRegistryIniFile.WriteDateTime(const Section, Name: string;
  Value: TDateTime);
begin
  FRegInifile.WriteDateTime(Section,Name, Value);
end;

procedure TRegistryIniFile.WriteFloat(const Section, Name: string;
  Value: Double);
begin
  FRegInifile.WriteFloat(Section,Name, Value);
end;

procedure TRegistryIniFile.WriteInteger(const Section, Name: string;
  Value: Integer);
begin
  FRegInifile.WriteInteger(Section, Name, Value);
end;

procedure TRegistryIniFile.WriteString(const Section, Name, Value: String);
begin
  FRegInifile.WriteString(Section, Name, Value);
end;

procedure TRegistryIniFile.WriteTime(const Section, Name: string;
  Value: TDateTime);
begin
  FRegInifile.WriteTime(Section,Name, Value);
end;

function TRegistryIniFile.ValueExists(const Section, Ident: string): Boolean;
begin
  with FRegInifile do
    if OpenSection(Section) then
      try
        Result:=FRegInifile.ValueExists(Ident);
      finally
        CloseSection;
      end;
end;

{$ifdef XMLREG}
finalization
  TXMLRegistryInstance.FreeXMLRegistryCache;
{$endif}

end.
