mirror of
https://github.com/HeidiSQL/HeidiSQL.git
synced 2025-08-14 01:56:36 +08:00
Rethink portable releases:
* Remove complicated and unreadable NSIS stuff. No need to have a installer for a developer application. ZIP file is fine. * On startup, if a portable_settings.txt exists in the application folder, heidisql.exe will read its contents into a unique registry key. * In MainForm.OnDestroy, settings are written to that file again, and the reg key gets deleted. * Avoid using Microsoft's .reg file syntax, as you need admin privileges to store and restore such files, even when using the appropriate Windows API functions.
This commit is contained in:
@ -292,7 +292,7 @@ begin
|
||||
|
||||
i := 0;
|
||||
NewName := 'Unnamed';
|
||||
while MainReg.KeyExists(REGPATH + REGKEY_SESSIONS + NewName) do begin
|
||||
while MainReg.KeyExists(RegPath + REGKEY_SESSIONS + NewName) do begin
|
||||
inc(i);
|
||||
NewName := 'Unnamed-' + IntToStr(i);
|
||||
end;
|
||||
@ -314,7 +314,7 @@ var
|
||||
begin
|
||||
if MessageDlg('Delete session "' + SelectedSession + '" ?', mtConfirmation, [mbYes, mbCancel], 0) = mrYes then
|
||||
begin
|
||||
SessionKey := REGPATH + REGKEY_SESSIONS + SelectedSession;
|
||||
SessionKey := RegPath + REGKEY_SESSIONS + SelectedSession;
|
||||
if MainReg.KeyExists(SessionKey) then
|
||||
MainReg.DeleteKey(SessionKey);
|
||||
FSessionNames.Delete(FSessionNames.IndexOf(SelectedSession));
|
||||
@ -345,7 +345,7 @@ end;
|
||||
procedure Tconnform.RefreshSessionList;
|
||||
begin
|
||||
// Refresh list of session names
|
||||
MainReg.OpenKey(REGPATH + REGKEY_SESSIONS, True);
|
||||
MainReg.OpenKey(RegPath + REGKEY_SESSIONS, True);
|
||||
FSessionNames.BeginUpdate;
|
||||
MainReg.GetKeyNames(FSessionNames);
|
||||
FSessionNames.EndUpdate;
|
||||
@ -398,7 +398,7 @@ begin
|
||||
OpenRegistry;
|
||||
SessionFocused := Assigned(Node);
|
||||
if SessionFocused then begin
|
||||
SessionExists := MainReg.KeyExists(REGPATH + REGKEY_SESSIONS + SelectedSession);
|
||||
SessionExists := MainReg.KeyExists(RegPath + REGKEY_SESSIONS + SelectedSession);
|
||||
if SessionExists then begin
|
||||
OpenRegistry(SelectedSession);
|
||||
FOrgNetType := GetRegValue(REGNAME_NETTYPE, DEFAULT_NETTYPE, SelectedSession);
|
||||
@ -470,7 +470,7 @@ begin
|
||||
lblCounterRight.Enabled := False;
|
||||
|
||||
if (not Assigned(ListSessions.FocusedNode))
|
||||
or (not MainReg.KeyExists(REGPATH + REGKEY_SESSIONS + SelectedSession)) then
|
||||
or (not MainReg.KeyExists(RegPath + REGKEY_SESSIONS + SelectedSession)) then
|
||||
Exit;
|
||||
|
||||
DummyDate := StrToDateTime('2000-01-01');
|
||||
@ -517,9 +517,9 @@ begin
|
||||
MessageDLG('Session "'+NewText+'" already exists!', mtError, [mbCancel], 0);
|
||||
NewText := SelectedSession;
|
||||
end else begin
|
||||
SessionKey := REGPATH + REGKEY_SESSIONS + SelectedSession;
|
||||
SessionKey := RegPath + REGKEY_SESSIONS + SelectedSession;
|
||||
if MainReg.KeyExists(SessionKey) then
|
||||
MainReg.MoveKey(SessionKey, REGPATH + REGKEY_SESSIONS + NewText, true);
|
||||
MainReg.MoveKey(SessionKey, RegPath + REGKEY_SESSIONS + NewText, true);
|
||||
// Also fix internal session name in main form, which gets used to store e.g. "lastuseddb" later
|
||||
if Mainform.SessionName = SelectedSession then begin
|
||||
Mainform.SessionName := NewText;
|
||||
|
@ -21,7 +21,6 @@ const
|
||||
// General things
|
||||
APPNAME = 'HeidiSQL';
|
||||
APPDOMAIN = 'http://www.heidisql.com/';
|
||||
REGPATH = '\Software\' + APPNAME + '\';
|
||||
REGKEY_SESSIONS = 'Servers\';
|
||||
// Some unique char, used to separate e.g. selected columns in registry
|
||||
DELIM = '|';
|
||||
|
@ -250,9 +250,11 @@ type
|
||||
function KeyPressed(Code: Integer): Boolean;
|
||||
function GeneratePassword(Len: Integer): String;
|
||||
procedure InvalidateVT(VT: TVirtualStringTree; RefreshTag: Integer; ImmediateRepaint: Boolean);
|
||||
procedure HandlePortableSettings(StartupMode: Boolean);
|
||||
|
||||
var
|
||||
MainReg: TRegistry;
|
||||
RegPath: String = '\Software\' + APPNAME + '\';
|
||||
MutexHandle: THandle = 0;
|
||||
dbgCounter: Integer = 0;
|
||||
DecimalSeparatorSystemdefault: Char;
|
||||
@ -2593,9 +2595,11 @@ procedure OpenRegistry(Session: String = '');
|
||||
var
|
||||
folder : String;
|
||||
begin
|
||||
if MainReg = nil then
|
||||
if MainReg = nil then begin
|
||||
MainReg := TRegistry.Create;
|
||||
folder := REGPATH;
|
||||
HandlePortableSettings(True);
|
||||
end;
|
||||
folder := RegPath;
|
||||
if Session <> '' then
|
||||
folder := folder + REGKEY_SESSIONS + Session;
|
||||
if MainReg.CurrentPath <> folder then
|
||||
@ -2619,7 +2623,7 @@ end;
|
||||
Read a boolean preference value from registry
|
||||
@param string Name of the value
|
||||
@param boolean Default-value to return if valueName was not found
|
||||
@param string Subkey of REGPATH where to search for the value
|
||||
@param string Subkey of RegPath where to search for the value
|
||||
}
|
||||
function GetRegValue( valueName: String; defaultValue: Boolean; Session: String = '' ) : Boolean;
|
||||
begin
|
||||
@ -3456,6 +3460,108 @@ begin
|
||||
end;
|
||||
|
||||
|
||||
procedure HandlePortableSettings(StartupMode: Boolean);
|
||||
var
|
||||
Content, FileName, Name, Value, KeyPath: String;
|
||||
Lines, Segments: TStringList;
|
||||
i: Integer;
|
||||
DataType: TRegDataType;
|
||||
const
|
||||
Chr10Replacement = '<}}}>';
|
||||
Chr13Replacement = '<{{{>';
|
||||
Delimiter = '<|||>';
|
||||
|
||||
procedure ReadKeyToContent(Path: String);
|
||||
var
|
||||
Names: TStringList;
|
||||
i: Integer;
|
||||
SubPath: String;
|
||||
begin
|
||||
MainReg.OpenKeyReadOnly(Path);
|
||||
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;
|
||||
case DataType of
|
||||
rdString: begin
|
||||
Value := MainReg.ReadString(Names[i]);
|
||||
Value := StringReplace(Value, #13, Chr13Replacement, [rfReplaceAll]);
|
||||
Value := StringReplace(Value, #10, Chr10Replacement, [rfReplaceAll]);
|
||||
end;
|
||||
rdInteger:
|
||||
Value := IntToStr(MainReg.ReadInteger(Names[i]));
|
||||
rdBinary, rdUnknown, rdExpandString:
|
||||
MessageDlg(Names[i]+' has an unsupported data type.', mtError, [mbOK], 0);
|
||||
end;
|
||||
Content := Content + Value + CRLF;
|
||||
end;
|
||||
Names.Clear;
|
||||
MainReg.GetKeyNames(Names);
|
||||
for i:=0 to Names.Count-1 do
|
||||
ReadKeyToContent(Path + Names[i] + '\');
|
||||
Names.Free;
|
||||
end;
|
||||
begin
|
||||
// Export registry keys and values into textfile, for portable reasons
|
||||
FileName := ExtractFilePath(ParamStr(0)) + 'portable_settings.txt';
|
||||
if not FileExists(FileName) then
|
||||
Exit;
|
||||
|
||||
// Open the right key
|
||||
if StartupMode then
|
||||
RegPath := '\Software\' + APPNAME + ' Portable '+IntToStr(GetCurrentThreadId)+'\';
|
||||
|
||||
Screen.Cursor := crHourGlass;
|
||||
try
|
||||
if StartupMode then begin
|
||||
Content := ReadTextfile(FileName);
|
||||
Lines := Explode(CRLF, Content);
|
||||
for i:=0 to Lines.Count-1 do begin
|
||||
// Each line has 3 segments: reg path | data type | value
|
||||
Segments := Explode(Delimiter, Lines[i]);
|
||||
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:
|
||||
MessageDlg(Name+' has an unsupported data type.', mtError, [mbOK], 0);
|
||||
end;
|
||||
Segments.Free;
|
||||
end;
|
||||
Lines.Free;
|
||||
end else begin
|
||||
// Application closes: Recursively read values in keys and their subkeys into textfile
|
||||
ReadKeyToContent(RegPath);
|
||||
SaveUnicodeFile(FileName, Content);
|
||||
MainReg.CloseKey;
|
||||
MainReg.DeleteKey(RegPath);
|
||||
end;
|
||||
except
|
||||
On E:Exception do
|
||||
MessageDlg(E.Message, mtError, [mbOK], 0);
|
||||
end;
|
||||
Screen.Cursor := crDefault;
|
||||
|
||||
end;
|
||||
|
||||
|
||||
end.
|
||||
|
||||
|
||||
|
@ -1176,6 +1176,8 @@ begin
|
||||
deletefile(filename+'bmp');
|
||||
if MainReg <> nil then begin
|
||||
MainReg.CloseKey;
|
||||
// Export settings into textfile in portable mode.
|
||||
HandlePortableSettings(False);
|
||||
MainReg.Free;
|
||||
end;
|
||||
end;
|
||||
|
@ -186,7 +186,7 @@ begin
|
||||
comboExportOutputTarget.Text := '';
|
||||
// Add session names from registry
|
||||
SessionNames := TStringList.Create;
|
||||
MainReg.OpenKey(REGPATH + REGKEY_SESSIONS, True);
|
||||
MainReg.OpenKey(RegPath + REGKEY_SESSIONS, True);
|
||||
MainReg.GetKeyNames(SessionNames);
|
||||
for i:=0 to SessionNames.Count-1 do begin
|
||||
if SessionNames[i] <> Mainform.SessionName then
|
||||
|
Reference in New Issue
Block a user