From 6ef3d170425770f496e6a45f779dcf4205a7969a Mon Sep 17 00:00:00 2001 From: Ansgar Becker Date: Mon, 9 Apr 2012 17:40:12 +0000 Subject: [PATCH] Use home brown file format for exporting and importing registry settings, as used for portable_settings.txt. Registry dumps cannot be imported into a separate registry key for a portable instance, which is hereby fixed, see http://www.heidisql.com/forum.php?t=10503 . --- source/const.inc | 3 ++ source/helpers.pas | 121 +++++++++++++++++++++++++++------------------ source/main.dfm | 15 ------ source/main.pas | 44 ++++++++++++++--- 4 files changed, 111 insertions(+), 72 deletions(-) diff --git a/source/const.inc b/source/const.inc index 059d1626..9a070db9 100644 --- a/source/const.inc +++ b/source/const.inc @@ -17,6 +17,9 @@ const REGKEY_QUERYHISTORY = 'QueryHistory'; // Some unique char, used to separate e.g. selected columns in registry DELIM = '|'; + CHR10REPLACEMENT = '<}}}>'; + CHR13REPLACEMENT = '<{{{>'; + DELIMITER = '<|||>'; // Used by maskSQL and fixSQL: SQL_VERSION_ANSI = -1; diff --git a/source/helpers.pas b/source/helpers.pas index 501f403b..31c72e0d 100644 --- a/source/helpers.pas +++ b/source/helpers.pas @@ -192,6 +192,8 @@ type function GeneratePassword(Len: Integer): String; procedure InvalidateVT(VT: TVirtualStringTree; RefreshTag: Integer; ImmediateRepaint: Boolean); procedure HandlePortableSettings(StartupMode: Boolean); + procedure ImportSettings(Filename: String); + procedure ExportSettings(Filename: String); function CharAtPos(Str: String; Pos: Integer): Char; function CompareAnyNode(Text1, Text2: String): Integer; function StringListCompareAnythingAsc(List: TStringList; Index1, Index2: Integer): Integer; @@ -2192,20 +2194,51 @@ begin end; -procedure HandlePortableSettings(StartupMode: Boolean); +procedure ImportSettings(Filename: String); var - Content, FileName, Name, Value, KeyPath: String; - Lines, Segments, AllKeys: TStringList; + Content, Name, Value, KeyPath: String; + Lines, Segments: TStringList; i: Integer; DataType: TRegDataType; - Proc: TProcessEntry32; - ProcRuns: Boolean; - SnapShot: THandle; - rx: TRegExpr; -const - Chr10Replacement = '<}}}>'; - Chr13Replacement = '<{{{>'; - Delimiter = '<|||>'; +begin + // Load registry settings from file + Content := ReadTextfile(FileName, nil); + Lines := Explode(CRLF, Content); + for i:=0 to Lines.Count-1 do begin + // Each line has 3 segments: reg path | data type | value. Continue if explode finds less or more than 3. + Segments := Explode(DELIMITER, Lines[i]); + if Segments.Count <> 3 then + continue; + KeyPath := RegPath + ExtractFilePath(Segments[0]); + Name := ExtractFileName(Segments[0]); + DataType := TRegDataType(StrToInt(Segments[1])); + MainReg.OpenKey(KeyPath, True); + if MainReg.ValueExists(Name) then + Continue; // Don't touch value if already there + Value := ''; + if Segments.Count >= 3 then + Value := Segments[2]; + case DataType of + rdString: begin + Value := StringReplace(Value, CHR13REPLACEMENT, #13, [rfReplaceAll]); + Value := StringReplace(Value, CHR10REPLACEMENT, #10, [rfReplaceAll]); + MainReg.WriteString(Name, Value); + end; + rdInteger: + MainReg.WriteInteger(Name, MakeInt(Value)); + rdBinary, rdUnknown, rdExpandString: + ErrorDialog(Name+' has an unsupported data type.'); + end; + Segments.Free; + end; + Lines.Free; +end; + + +procedure ExportSettings(Filename: String); +var + Content, Value: String; + DataType: TRegDataType; procedure ReadKeyToContent(Path: String); var @@ -2213,20 +2246,21 @@ const i: Integer; SubPath: String; begin - MainReg.OpenKeyReadOnly(Path); + // Recursively read values in keys and their subkeys into "content" variable + MainReg.OpenKey(Path, True); SubPath := Copy(Path, Length(RegPath)+1, MaxInt); Names := TStringList.Create; MainReg.GetValueNames(Names); for i:=0 to Names.Count-1 do begin DataType := MainReg.GetDataType(Names[i]); Content := Content + - SubPath + Names[i] + Delimiter + - IntToStr(Integer(DataType)) + Delimiter; + SubPath + Names[i] + DELIMITER + + IntToStr(Integer(DataType)) + DELIMITER; case DataType of rdString: begin Value := MainReg.ReadString(Names[i]); - Value := StringReplace(Value, #13, Chr13Replacement, [rfReplaceAll]); - Value := StringReplace(Value, #10, Chr10Replacement, [rfReplaceAll]); + Value := StringReplace(Value, #13, CHR13REPLACEMENT, [rfReplaceAll]); + Value := StringReplace(Value, #10, CHR10REPLACEMENT, [rfReplaceAll]); end; rdInteger: Value := IntToStr(MainReg.ReadInteger(Names[i])); @@ -2241,6 +2275,24 @@ const ReadKeyToContent(Path + Names[i] + '\'); Names.Free; end; + +begin + // Save registry settings to file + Content := ''; + ReadKeyToContent(RegPath); + SaveUnicodeFile(FileName, Content); +end; + + +procedure HandlePortableSettings(StartupMode: Boolean); +var + FileName: String; + AllKeys: TStringList; + i: Integer; + Proc: TProcessEntry32; + ProcRuns: Boolean; + SnapShot: THandle; + rx: TRegExpr; begin // Export registry keys and values into textfile, for portable reasons @@ -2270,41 +2322,12 @@ begin Screen.Cursor := crHourGlass; try + // Both ImportSettings and ExportSettings rely on RegPath pointing to the right reg key if StartupMode then begin - Content := ReadTextfile(FileName, nil); - Lines := Explode(CRLF, Content); - for i:=0 to Lines.Count-1 do begin - // Each line has 3 segments: reg path | data type | value. Continue if explode finds less or more than 3. - Segments := Explode(Delimiter, Lines[i]); - if Segments.Count <> 3 then - continue; - KeyPath := RegPath + ExtractFilePath(Segments[0]); - Name := ExtractFileName(Segments[0]); - DataType := TRegDataType(StrToInt(Segments[1])); - MainReg.OpenKey(KeyPath, True); - if MainReg.ValueExists(Name) then - Continue; // Don't touch value if already there - Value := ''; - if Segments.Count >= 3 then - Value := Segments[2]; - case DataType of - rdString: begin - Value := StringReplace(Value, Chr13Replacement, #13, [rfReplaceAll]); - Value := StringReplace(Value, Chr10Replacement, #10, [rfReplaceAll]); - MainReg.WriteString(Name, Value); - end; - rdInteger: - MainReg.WriteInteger(Name, MakeInt(Value)); - rdBinary, rdUnknown, rdExpandString: - ErrorDialog(Name+' has an unsupported data type.'); - end; - Segments.Free; - end; - Lines.Free; + ImportSettings(Filename); end else begin - // Application closes: Recursively read values in keys and their subkeys into textfile - ReadKeyToContent(RegPath); - SaveUnicodeFile(FileName, Content); + // Application closes + ExportSettings(Filename); MainReg.CloseKey; MainReg.DeleteKey(RegPath); diff --git a/source/main.dfm b/source/main.dfm index 0022661e..006283e3 100644 --- a/source/main.dfm +++ b/source/main.dfm @@ -2577,21 +2577,6 @@ object MainForm: TMainForm OnExecute = actLaunchCommandlineExecute end end - object SaveDialog2: TSaveDialog - DefaultExt = 'reg' - Filter = 'Registry-files (*.reg)|*.reg|All files (*.*)|*.*' - Options = [ofOverwritePrompt, ofHideReadOnly, ofEnableSizing] - Title = 'Export settings from registry...' - Left = 8 - Top = 128 - end - object OpenDialog2: TOpenDialog - DefaultExt = 'reg' - Filter = 'Registry-files (*.reg)|*.reg|All files (*.*)|*.*' - Title = 'Import settings to registry...' - Left = 72 - Top = 160 - end object menuConnections: TPopupMenu AutoHotkeys = maManual Images = ImageListMain diff --git a/source/main.pas b/source/main.pas index e7f26e92..1733743c 100644 --- a/source/main.pas +++ b/source/main.pas @@ -171,10 +171,8 @@ type ToolButton14: TToolButton; actExecuteQuery: TAction; actExecuteSelection: TAction; - SaveDialog2: TSaveDialog; ExportSettings1: TMenuItem; Importsettings1: TMenuItem; - OpenDialog2: TOpenDialog; menuSupportForum: TMenuItem; actExportData: TAction; actExecuteCurrentQuery: TAction; @@ -2203,19 +2201,49 @@ end; procedure TMainForm.actExportSettingsExecute(Sender: TObject); +var + Dialog: TSaveDialog; begin - // Export settings to .reg-file - if SaveDialog2.Execute then - ShellExec('regedit.exe', '', '/e "'+SaveDialog2.FileName+'" HKEY_CURRENT_USER'+REGPATH); + // Export settings to .txt file + Dialog := TSaveDialog.Create(Self); + Dialog.Title := 'Export '+APPNAME+' settings to file ...'; + Dialog.DefaultExt := 'txt'; + Dialog.Filter := 'Textfiles (*.txt)|*.txt|All files (*.*)|*.*'; + Dialog.Options := Dialog.Options + [ofOverwritePrompt]; + if Dialog.Execute then try + ExportSettings(Dialog.FileName); + MessageDialog('Settings successfully exported to '+Dialog.FileName, mtInformation, [mbOK]); + except + on E:Exception do + ErrorDialog(E.Message); + end; + Dialog.Free; end; + procedure TMainForm.actImportSettingsExecute(Sender: TObject); +var + Dialog: TOpenDialog; begin - // Import settings from .reg-file - if OpenDialog2.Execute then - ShellExec('regedit.exe', '', '"'+OpenDialog2.FileName+'"'); + // Import settings from .txt or .reg file + Dialog := TOpenDialog.Create(Self); + Dialog.Title := 'Import '+APPNAME+' settings from file ...'; + Dialog.Filter := 'Textfiles (*.txt)|*.txt|Registry dump, deprecated (*.reg)|*.reg|All files (*.*)|*.*'; + if Dialog.Execute then try + if LowerCase(ExtractFileExt(Dialog.FileName)) = 'reg' then + ShellExec('regedit.exe', '', '"'+Dialog.FileName+'"') + else begin + ImportSettings(Dialog.FileName); + MessageDialog('Settings successfully restored from '+Dialog.FileName, mtInformation, [mbOK]); + end; + except + on E:Exception do + ErrorDialog(E.Message); + end; + Dialog.Free; end; + procedure TMainForm.actExecuteQueryExecute(Sender: TObject); var Query: TSQLSentence;