How can I check if the program was installed correctly using Exec() function in Inno Setup? [duplicate] - sql-server

Using innosetup and want to show error/msgbox if one of the [RUN] process does not return process code 0. I'm using it for authorization process, if authorization is not successful, i want to notify the user.
I have following:
Filename: "{pf32}\Common Files\Authorization.exe"; Parameters: " "{code:GetAuthorizationFilePath}" /s"; WorkingDir: "{tmp}"; Flags: skipifdoesntexist hidewizard; StatusMsg: "Authorizing License";
Returns me:
Process exit code:0
0 of course is successful, but if its not 0 i want to notify the user.
Is there a way to do that?
Thanks and Regards,
Kev84

I think there's no way to accomplish this from the [Run] section. What you can do is:
use the Pascal Script for this task
or display the modal error message from your executed application Authorization.exe and terminate it only after the user confirms the error message (setup will then continue e.g. with the execution of the other files in the [Run] section)
Here is the code sample of the Pascal Script; you can check also the commented version of this code:
[Code]
function NextButtonClick(CurPageID: Integer): Boolean;
var
ResultCode: Integer;
begin
Result := True;
if CurPageID = wpWelcome then
begin
Result := False;
if Exec(ExpandConstant('{pf32}\Common Files\Authorization.exe'), '', '',
SW_SHOW, ewWaitUntilTerminated, ResultCode) then
begin
if ResultCode = 0 then
Result := True
else
MsgBox('The authorization failed!', mbCriticalError, MB_OK);
end;
end;
end;

I had the same requirements: to run an external program and display an error message if the return code is not 0. It was very important for me to run the program in the Run section as I needed to display a status message and the progress bar is nice to have.
I found that you can use AfterInstall in the Run section to trigger the execution of your program and check the result code (see this link for more info about AfterInstall.)
So, my idea was to run a dummy program like change and to use the procedure specified in AfterInstall to run the real program and catch its result code.
[Code]
procedure ExecuteRealProgram();
var
ResultCode: Integer;
begin
if Exec(ExpandConstant('{pf32}\Common Files\Authorization.exe'), '', '', SW_SHOW,
ewWaitUntilTerminated, ResultCode)
then begin
if not (ResultCode = 0) then
MsgBox('Error! ResultCode is ' + IntToStr(ResultCode), mbCriticalError, MB_OK);
end
else
MsgBox('Exec failed! Error: ' + SysErrorMessage(ResultCode), mbCriticalError, MB_OK);
end;
end;
[Run]
Filename: "change.exe"; WorkingDir: "{tmp}"; \
StatusMsg: "Running external program. Please wait."; AfterInstall: ExecuteRealProgram

Related

How to restart Inno Setup installer based on result of procedure that executes a program/subinstaller

I have an install procedure that is executed if the sub exe is not installed, and if so, I want the final "lauch my app" checkbox at the end to be replaced by a "restart computer" checkbox.
How can I do that?
I tried to use the NeedRestart() and also use a global boolean variable. But I can't seem to make it work.
Minimal reproductible example:
the file line :
[Files]
Source: "Dependencies\ndp48-x86-x64-allos-enu.exe"; \
DestDir: "{app}"; Flags: deleteafterinstall; \
AfterInstall: InstallNETFramework; Check: NETFrameworkIsNotInstalled
The installation procedure:
procedure InstallNETFramework;
var
ResultCode: Integer;
StatusText: string;
begin
StatusText := WizardForm.StatusLabel.Caption;
WizardForm.StatusLabel.Caption := 'Installing .NET Framework 4.8...';
WizardForm.ProgressGauge.Style := npbstMarquee;
try
if not ShellExec('open', ExpandConstant('{app}\ndp48-x86-x64-allos-enu.exe'), '/norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode)
then
begin
MsgBox('.NET Framework 4.8 Installation did not succeed : ' + IntToStr(ResultCode) + '.', mbError, MB_OK);
end;
finally
WizardForm.StatusLabel.Caption := StatusText;
WizardForm.ProgressGauge.Style := npbstNormal;
end;
end;
The NeedToRestart event function:
[Code]
var NeedToRestart: boolean;
function NeedToRestart();
begin
//??
end;
If this installation procedure is executed, then at the end, instead of having a "launch {{app}}" I want "restart computer". If possible with the option now or later? It's fine if not.
As you correctly assumed:
introduce a global Boolean variable
set it when the .NET installer is successfully executed (you should check ResultCode to be really sure that it did)
and use the variable in the NeedRestart event function:
var
RestartNeeded: Boolean;
function NeedRestart(): Boolean;
begin
Result := RestartNeeded;
end;
procedure InstallNETFramework;
var
ResultCode: Integer;
begin
// ...
if Exec(ExpandConstant('{app}\ndp48-x86-x64-allos-enu.exe'),
'/norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) and
(ResultCode = { whatever success exit code[s] the installer uses }) then
begin
Log('.NET Framework 4.8 installation succeeded, need restart');
RestartNeeded := True;
// Or if the installer has specific code to indicate success with restart:
// RestartNeeded := (ResultCode = RestartCode);
end
else
begin
MsgBox('.NET Framework 4.8 installation did not succeed: ' +
IntToStr(ResultCode) + '.', mbError, MB_OK);
end;
// ...
end;
.NET installer return codes seem to be documented here (but I didn't test it):
https://learn.microsoft.com/en-us/dotnet/framework/deployment/deployment-guide-for-developers#return-codes
Similar question: Inno Setup and VC Redistributable and handling exit code 3010 gracefully
As hinted by #PMF in your other question, as alternative approach might be to try the RestartIfNeededByRun directive along with running your subinstaller using [Run]. Though that limits error handling.
If you want to keep executing (and checking) the .NET installation in code, you can use MakePendingFileRenameOperationsChecksum function:
var
ResultCode: Integer;
Checksum: string;
// ...
Checksum := MakePendingFileRenameOperationsChecksum;
if Exec(ExpandConstant('{app}\ndp48-x86-x64-allos-enu.exe'),
'/norestart', '', SW_SHOW, ewWaitUntilTerminated, ResultCode) and
(ResultCode = { whatever success exit code[s] the installer uses }) then
begin
Log('.NET Framework 4.8 installation succeeded, need restart');
if Checksum <> MakePendingFileRenameOperationsChecksum then
begin
Log('Need to restart detected');
RestartNeeded := True;
end;
end
// ...

Get exit code from batch file in Inno Setup using ShellExecuteEx

I have several batch files which I execute from Inno Setup. I use ShellExecuteEx() to execute batch files:
function ShellExecuteWait(const Verb, Filename, Params, WorkingDir: string; const ShowCmd: integer; const Timeout: cardinal; var ExitCode: DWORD): boolean;
var
ExecInfo: TShellExecuteInfo;
begin
Result := false;
ExecInfo.cbSize := SizeOf(ExecInfo);
ExecInfo.fMask := SEE_MASK_NOCLOSEPROCESS;
ExecInfo.Wnd := 0;
ExecInfo.lpVerb := Verb;
ExecInfo.lpFile := Filename;
ExecInfo.lpParameters := Params;
ExecInfo.lpDirectory := WorkingDir;
ExecInfo.nShow := ShowCmd;
if ShellExecuteEx(ExecInfo) then
begin
if WaitForSingleObject(ExecInfo.hProcess, Timeout) = WAIT_TIMEOUT then
begin
TerminateProcess(ExecInfo.hProcess, WAIT_TIMEOUT);
end else begin
end;
GetExitCodeProcess(ExecInfo.hProcess, ExitCode);
Result := (ExitCode = ERROR_SUCCESS);
end else begin
ExitCode := GetLastError();
end;
end;
if (not ShellExecuteWait('',
Command,
Params,
ExpandConstant('{tmp}'),
SW_SHOW,
Timeout,
ResultCode)) then...
But no matter what I try, I cannot get the exit code from the batch file in ResultCode (I always get back 0).
In researching this problem, I have read that the batch must NOT use exit /b NN. So I removed the /b switch, but I still always get 0.
What must I do to successfully get the exit code from a batch file?
Dennis
I found the problem. The command to invoke the batch file includes a pipe through another program, so the result code I get back is actually coming from the program through which the script's output is piped.

Delphi: What's wrong with REST API based on mORMot?

I try to build REST Web Server based on mORMot framework (Delphi) and have issue with getting data from remote DB.
Server.dpr
const
CONN_STR =
'DRIVER=SQL Server Native Client 10.0;UID=user;PWD=pass;server=server;'
+ 'Trusted_Connection=No;MARS_Connection=yes';
var
Model: TSQLModel;
RESTServer: TSQLRestServerDB;
Application: TwoStreamgateApplication;
HTTPServer: TSQLHttpServer;
ODBCConnProp: TSQLDBConnectionPropertiesThreadSafe;
begin
SQLite3Log.Family.Level := LOG_VERBOSE;
SQLite3Log.Family.PerThreadLog := ptIdentifiedInOnFile;
ODBCConnProp := TODBCConnectionProperties.Create('', CONN_STR, 'wo', 'wo');
try
ODBCConnProp.ThreadingMode := tmMainConnection;
Model := CreateModel;
VirtualTableExternalRegisterAll(Model, ODBCConnProp);
try
RESTServer := TSQLRestServerDB.Create(Model, ':memory:');
try
RESTServer.DB.Synchronous := smNormal;
RESTServer.DB.LockingMode := lmExclusive;
RESTServer.CreateMissingTables;
Application := TwoStreamgateApplication.Create;
try
Application.Start(RESTServer);
HTTPServer := TSQLHttpServer.Create('8092', [RESTServer]
{$IFNDEF ONLYUSEHTTPSOCKET}, '+', useHttpApiRegisteringURI{$ENDIF});
try
HTTPServer.RootRedirectToURI('api/default');
RESTServer.RootRedirectGet := 'api/default';
writeln('"MVC WOStreamgate Server" launched on port 8092 using ',
HTTPServer.HttpServer.ClassName);
writeln(#10'You can check http://localhost:8092/api/info for information');
writeln('or point to http://localhost:8092 to access the web app.');
writeln(#10'Press [Enter] to close the server.'#10);
readln;
writeln('HTTP server shutdown...');
finally
HTTPServer.Free;
end;
finally
Application.Free;
end;
finally
RESTServer.Free;
end;
finally
Model.Free;
end;
finally
ODBCConnProp.Free;
end;
Model is described below in ServerModel.pas unit
type
TSQLBaseData = class(TSQLRecord)
private
FName: RawUTF8;
FDetailURL: RawUTF8;
FLogoURL: RawUTF8;
FDescription: RawUTF8;
published
property Name: RawUTF8 read FName write FName;
property Description: RawUTF8 read FDescription write FDescription;
property LogoURL: RawUTF8 read FLogoURL write FLogoURL;
property DetailURL: RawUTF8 read FDetailURL write FDetailURL;
end;
TSQLStation = class(TSQLBaseData)
end;
function CreateModel: TSQLModel;
implementation
function CreateModel: TSQLModel;
begin
result := TSQLModel.Create(
[TSQLStation],
'api');
end;
View model in ServerViewModel.pas unit
TServerApplication = class(TMVCApplication, IServerApplication)
public
procedure Start(aServer: TSQLRestServer); reintroduce;
procedure StationView(ID: TID; out Station: TSQLStation);
end;
implementation
procedure TServerApplication.Start(aServer: TSQLRestServer);
begin
inherited Start(aServer,TypeInfo(IServerApplication));
fMainRunner := TMVCRunOnRestServer.Create(Self).
SetCache('Default',cacheRootIfNoSession,15);
aServer.Cache.SetCache(TSQLStation);
end;
procedure TServerApplication.StationView(ID: TID; out Station: TSQLStation);
begin
RestModel.Retrieve(ID, Station);
end;
and now once I type http://localhost:8092/api/station/10 in browser, I get response
{ "errorCode":404, "errorText":"Not Found" }
in case I type http://localhost:8092/api/stationview/10 in browser I get rendered page, but without info about station=10 or any other.
Where did I make a mistake? I want simply get JSON-response to my request
UPDATE:
Another info that can help. Once I try the query
procedure TestQuery();
var
SQLDBConnection: TODBCConnection;
Q: TQuery;
begin
SQLDBConnection := TODBCConnection.Create(ODBCConnProp);
Q := TQuery.Create(SQLDBConnection);
try
Q.SQL.Clear; // optional
Q.SQL.Add('select * from dbo.Station');
Q.Open;
...
I catch an error:
Project Server.exe raised exception class EODBCException with message 'TODBCStatement - TODBCLib
error: [HY106] [Microsoft][SQL Server Native Client 10.0]Fetch type
out of range (0) '.
Solution is very simple - need to add:
Database=myDataBase;
to
CONN_STR = 'DRIVER=SQL Server Native Client 10.0;UID=user;PWD=pass;server=server;'
+ 'Trusted_Connection=No;MARS_Connection=yes';Database=myDataBase;
Thanks Arnaud for great framework!!!

Login display error message

I need my program to log a user in from a database. This entails a diver number (like a username) and a password which is already in the database. Unfortunately, I don't know SQL right now and would rather use a technique similar to the one I've done here. I get an error message in run time that says: adotblDiversInfo: Cannot perform this operation on a closed dataset. Thank you so much for your help in advance (:
This is my code:
procedure TfrmHomeScreen.btnLogInClick(Sender: TObject);
var
iDiverNumber : Integer;
sPassword, sKnownPassword : String;
bFlagDiverNumber, bFlagPassword, Result : Boolean;
begin
iDiverNumber := StrToInt(ledDiverNumber.Text);
sPassword := ledPassword.Text;
with frmDM do
adotblDiversInfo.Filtered := False;
frmDM.adotblDiversInfo.Filter := 'Diver Number' + IntToStr(iDiverNumber);
frmDM.adotblDiversInfo.Filtered := True;
if frmDM.adotblDiversInfo.RecordCount = 0 then
ShowMessage(IntToStr(iDiverNumber) + ' cannot be found')
else
begin
sKnownPassword := frmDM.adotblDiversInfo['Password'];
if sKnownPassword = sPassword then
ShowMessage('Login successful')
else
ShowMessage('Incorrect password. Please try again');
end;
end;
The error you're getting is because you've forgotten to open the dataset before attempting to access it. Use frmDM.adoTblDiversInfo.Open; or frmDM.adoTblDiversInfo.Active := True; to do so before trying to use the table.
Your code could be much simpler (and faster) if you change it somewhat. Instead of filtering the entire dataset, simply see if you can Locate the proper record.
procedure TfrmHomeScreen.btnLogInClick(Sender: TObject);
var
iDiverNumber : Integer;
begin
if not frmDM.adoTblDiversInfo.Active then
frmDM.adoTblDiversInfo.Open;
iDiverNumber := StrToInt(ledDiverNumber.Text);
sPassword := ledPassword.Text;
if frmDM.adoTblDiversInfo.Locate('Diver Number', iDiverNumber, []) the
begin
if frmDM.adoTblDiversInfo['Password'] = ledPassword.Text then
ShowMessage('Login successful')
else
ShowMessage('Invalid password. Please try again.');
end
else
ShowMessage(ledDiverNumber.Text);
end;

Problem with checking ADOTable field values

I'm having yet another problem with Delphi. I wrote a piece of code that should check if a field in a database table equals to 0, and if that is true, changes font color and caption of a certain button. It runs on creation of the main form. However, when I run the program, nothing happens - the program does not appear and I get no errors. I seriously don't know what's wrong, seems like some kind of an infinite loop.
Here's the code:
procedure TForm1.FormCreate(Sender: TObject);
begin
ADOTableStorage.First;
while not ADOTableStorage.Eof do
If ADOTableStorage.FieldByName('amount').AsInteger = 0 then
begin
btStorage.Font.Color := clRed;
btStorage.Caption := 'Some items are out of stock!';
Break;
end;
ADOTableStorage.Next;
end;
Note: The ADOTableStorage table is a on the detail table in a Master-Detail connection.
Thanks!
I guess you are missing a begin/end in the while loop. Try this.
procedure TForm1.FormCreate(Sender: TObject);
begin
ADOTableStorage.First;
while not ADOTableStorage.Eof do
begin
If ADOTableStorage.FieldByName('amount').AsInteger = 0 then
begin
btStorage.Font.Color := clRed;
btStorage.Caption := 'Some items are out of stock!';
Break;
end;
ADOTableStorage.Next;
end;
end;

Resources