Inno Setup - How to bind a checkbox with a registry key, to check if the key may be written? [duplicate] - checkbox

This question already has answers here:
How to make work the new added checkboxes with the tasks?
(2 answers)
Closed 10 months ago.
I need to create an installer with Inno Setup (version 6.2.1) in which I need to let the user select which file extension he want to associate with the installed app.
To achieve that I needed to create a custom page showing all the checkboxes I needed. This page respects a custom theme required by my company, for that reason I could not use the checkboxes automatically generated by the [Tasks] section.
However I cannot figure out how to associate the checkbox state with the registry key I want to install (or not).
For example, I have the following code:
[Code]
var
AssocABCCB: TNewCheckBox;
...
procedure InitializeWizard;
var
SelectAssocPage: TWizardPage;
begin
// select association custom page
SelectAssocPage := CreateCustomPage(wpLicense, ExpandConstant('{cm:SelectAssocTitle}'), '');
...
// .abc checkbox
AssocABCCB := TNewCheckBox.Create(SelectAssocPage);
AssocABCCB.Align := alTop;
AssocABCCB.Caption := ExpandConstant('{cm:AssocABC}');
AssocABCCB.Parent := SelectAssocPage.Surface;
AssocABCCB.OnClick := #AssocABCCBClick;
...
end;
Now I want to use the above checkbox to optionally write a key in the registry, let say something like that:
[Registry]
Root: HKCR; Subkey: "{#KeyAppName}.abc"; Flags: uninsdeletekeyifempty; Check: AssocABCCB.Checked
Of course the above code is not working, it raises a compilation error.
But is there a way to bind the above checkbox with the registry key, in order to determine if the key may be written or not? If yes, how may I achieve that?

So I finally found what was the issue.
In fact it was an if statement was not closed correctly in one of my functions. This forced the following end statement to be closed with a . instead of a ;
Here was my wrong code:
[Code]
...
procedure CurStepChanged(CurStep: TSetupStep);
begin
Log('CurStepChanged(' + IntToStr(Ord(CurStep)) + ') called');
if CurStep = ssPostInstall then
FinishedInstall := True;
end; // <= here was the issue
end.
function DoAssocABC: Boolean;
begin
Result := AssocABCCB.Checked;
end;
...
[Registry]
Root: HKCR; Subkey: "{#KeyAppName}.abc"; Flags: uninsdeletekeyifempty; Check: DoAssocABC
Because my function was located AFTER the dotted end, it WAS NOT VISIBLE from ANY other statements, for that reason my [Registry] statement didn't find the function I tried to bind.
Unfortunately the compiler didn't complain about that. I don't know if it's a bug from Inno Setup itself.
Correcting that, the Martin Prikryl comment became right, and this fixed my issue:
You cannot access a variable in the Check parameter directly. You have to wrap it to a function, as my answer shows. It does not matter if it is a [Files] or [Registry] section. –
Martin Prikryl

Related

How to insert a document if a filter returns nothing, otherwise replace the document found in mongodb with go?

I am not using the MGO package like in this example, just the active repo from here.
I am having a hard time reading the documentation. Basically, I have a bson.M object that I want to replace the current one, and if that one doesn't exist, insert it.
Currently my code looks like:
updateFilter := bson.D{{"from_symbol", fromSymbol}, {"to_symbol", strings.ToUpper(currency["to_symbol"].(string))}}
// The above seems to be correctly finding the documents I want
// currency is my bson.M object
_, err := collection.ReplaceOne(ctx, updateFilter, currency)
// However this line will not additionally insert if the object is not found, it is replacing fine
I'm sure I could manually run another query looking to see if the document exists, but that seems unnecessary.
Thank you!
EDIT:
It looks like there should be a way to do something with replaceOptions, see the documentation.
upsert := options.ReplaceOptions{Upsert: true}
_, err := collection.ReplaceOne(ctx, updateFilter, currency, upset)
However this gives me the error:
cannot use true (type bool) as type *bool in field value
Use the SetUpsert function:
collection.ReplaceOne(ctx,filter,newDoc,options.Replace().SetUpsert(true))

How to save file as "read-only" from Delphi?

I would like to save a file as a "read-only" file. Is it possible?
This is a two stage process, that is only available on the Windows platform.
Save the file.
Add the read-only attribute to the file's metadata.
Assuming that you already know how to do stage 1, let's consider stage 2. Using the System.IOUtils unit you set this attribute like so:
uses
System.IOUtils;
....
var
attributes: TFileAttributes;
....
attributes := TFile.GetAttributes(FileName);
Include(attributes, faReadOnly);
TFile.SetAttributes(FileName, attributes);
If you wish to remove the read-only attribute then you use exactly the same code but replace Include with Exclude.
For platforms other than Windows, you can still use TFile.GetAttributes and TFile.SetAttributes, but the available attributes are very different, reflecting the different filesystem models of Windows and the POSIX platforms.
Unfortunately the RTL fails to provide any way to check for errors in this code. So if you want to check for errors (you should) then you are actually best calling the Windows API function SetFileAttributes directly. You might do that like this:
function FileSetAttribute(const FileName: string; const Attr: DWORD; const Value: Boolean): Boolean;
var
Flags, NewFlags: DWORD;
begin
Flags := GetFileAttributes(PChar(FileName));
if Flags=INVALID_FILE_ATTRIBUTES then begin
Result := False;
end else begin
if Value then begin
NewFlags := Flags or Attr
end else begin
NewFlags := Flags and not Attr;
end;
Result := (NewFlags=Flags) or SetFileAttributes(PChar(FileName), NewFlags);
end;
end;
function FileSetReadOnly(const FileName: string; ReadOnly: Boolean): Boolean;
begin
Result := FileSetAttribute(FileName, faReadOnly, ReadOnly);
end;
As discussed previously this code is for Windows only. The FileSetReadOnly function returns a boolean indicating whether or not it succeeded. In case of failure, you can then call GetLastError for extended error information.
Save it as normal, f.ex. via
VAR S : TFileStream;
.
S := TFileStream.Create(FileName,fmCreate);
<Write to stream>
S.Free;
Then
USES SysUtils;
// Set R/O
IF FileSetAttr(FileName,FileGetAttr(FileName) or faReadOnly)<>NO_ERROR THEN
RaiseLastOSError;
afterwards to mark it as "Read/Only".
If you want to update it later on, you need to remove the Read/Only flag first:
// Set R/W
IF FileSetAttr(FileName,FileGetAttr(FileName) AND NOT faReadOnly)<>NO_ERROR THEN
RaiseLastOSError;
then update the file, then mark it as Read/Only again.

Executing a stored procedure twice using TAdoStoredProc in Delphi

I'm using Delphi 10.1 Berlin and I needed to use TAdoStoredProc in my project. I don't use any non-visual component in my forms or data modules. All my connection and db components are creating at run-time.
My Question: When I'm trying to execute TAdoStoredProc, it executes twice and my insert, update processes have worked twice.
var
mSp: TADOStoredProc;
i: Integer;
begin
mSp := TADOStoredProc.Create(nil);
mSp.Connection := conn;
mSp.ProcedureName := spname;
mSp.CommandTimeout := 600;
mSp.parameters.Refresh;
for i := 0 to parameters.Count - 1 do
begin
mSp.parameters.ParamByName(parameters[i].Name).Value := parameters[i].Value;
end;
mSp.ExecProc;
mSp.Open;
Result := mSp;
What should I do or change? Thanks.
As I see that your code required some return values. So you should use TAdoStoredProc like TAdoQuery. You don't need to use ExecProc function for execute, try to use only Open function.
You can just check out TADOStoredProc.ExecProc Method documentation from here.
If an application is only interested in the result set returned by a stored procedure, call Open method for the TADOStoredProc component or set its Active property to true.

how can use dynamic array in delphi

i use dynamic array in delphi.
var
var
frame3:array[0..10] of TFrame3
procedure TForm1.Button1Click(sender:TObject);
begin
frame3[count] := TFrame3.create(self);
gridpanel2.insertcontrol(frame3[count]);
but this code is 'A component named Frame3 alredy exists.'
this error what can ido?
If you need multible instances of TFrame3 you need to give it a new name after you created it.
so change
frame3[count] := TFrame3.create(self);
gridpanel2.insertcontrol(frame3[count]);
To
frame3[count] := TFrame3.create(self);
frame3[count].Name := 'Frame3_' + InttoStr(Count);
gridpanel2.insertcontrol(frame3[count]);
The other issue is that it is not seen in your code how you are changing count loop variable. Is it defined in advance?
You need to do something like this:
procedure TForm1.Button1Click(sender:TObject);
var count:byte;
begin
for count:=1 to 10 do
begin
frame3[count] := TFrame3.create(self);
...
end;
or use any other way to set count before each array member (class instance) creation. This code will even probably not require to set Name property at all.

Display a (Synopse) SQLite3 table-column in a Delphi7 TListView

I would like to take the following unit (DrivesData) and display the drive column in a TListView. I've never worked with the (Synopse) SQLite3 code before so I'm hoping someone could give me a little push in the right direction.
Just add the DrivesData unit to the uses clause then run and it will create the "drives.sqlite" database file with a list of drives 'A' to 'Z'.
unit DrivesData;
interface
uses
SynCommons, SQLite3Commons;
type
TDrives = class(TSQLRecord)
private
{ Private declarations }
FDrive: RawUTF8;
protected
{ Protected declarations }
FDrivesModel: TSQLModel;
FDrivesDatabase: TSQLRest;
public
{ Public declarations }
constructor Create(); override;
destructor Destroy(); override;
published
{ Published declarations }
property Drive: RawUTF8 read FDrive write FDrive;
end;
var
DriveRecord: TDrives;
implementation
uses
SQLite3;
function CreateDrivesModel(): TSQLModel;
begin
Result := TSQLModel.Create([TDrives]);
end;
{ TDrives }
constructor TDrives.Create();
var
X: Char;
begin
inherited Create();
FDrivesModel := CreateDrivesModel();
FDrivesDatabase := TSQLRestServerDB.Create(FDrivesModel, 'drives.sqlite');
TSQLRestServerDB(FDrivesDatabase).DB.Execute(
'CREATE TABLE IF NOT EXISTS drives ' +
'(id INTEGER PRIMARY KEY, drive TEXT NOT NULL UNIQUE COLLATE NOCASE);');
for X := 'A' to 'Z' do
begin
TSQLRestServerDB(FDrivesDatabase).DB.Execute(
'INSERT OR IGNORE INTO drives (drive) VALUES ("' + X + ':")');
end;
end;
destructor TDrives.Destroy();
begin
if Assigned(FDrivesDatabase) then
FDrivesDatabase.Free();
if Assigned(FDrivesModel) then
FDrivesModel.Free();
inherited Destroy();
end;
initialization
DriveRecord := TDrives.Create();
finalization
if Assigned(DriveRecord) then
DriveRecord.Free();
end.
Nice try!
But I'm afraid you are missing some points of the framework:
for instance you're mixing record level and MVC application level: a TSQLRecord maps a DB table and you should not declare MVC TSQLModel and TSQLRest inside this class;
and you're missing the ORM approach, you don't need to write all those SQL code (the CREATE TABLE and the INSERT): the framework will write it for you, with no error, and the exact expected column type (with collations)!
Instead of using a TSQLRestServerDB directly by itself, you should better use a TSQLRestClientDB (which will instantiate its privately owned TSQLRestServerDB), even if you are still working locally. So you'll get a lot more features with no performance penalty.
You are using a Char type in your code. Our framework is UTF-8 oriented, so you should use AnsiChar instead, or use StringToUtf8() function to ensure correctness (at least with Unicode version of Delphi).
I'll recommend that you take a look at the sample code source code and the provided documentation (especially the SAD document, in the general presentation in the first pages, including the SynFile main demo).
To retrieve some data, then display it in the VCL (e.g. in a TListBox), take a look at the TSQLTableJSON class. There are some code sample in the SAD document (take a look at the keyword index, at the beginning of the document, if you're a bit lost).
Perhaps StackOverflow is not the best place to ask such specific questions. You have our forum available at http://synopse.info to post any questions regarding this framework. You can post your code here.
Thanks for your interest!

Resources